簡體   English   中英

怎么把ffmpeg視頻幀轉換成YUV444?

[英]How to convert ffmpeg video frame to YUV444?

我一直在遵循有關如何使用ffmpeg和SDL制作沒有音頻的簡單視頻播放器的教程 (尚未)。 在瀏覽本教程時,我意識到它已經過時,並且不建議使用ffmpeg和SDL所使用的許多功能。 因此,我搜索了最新的解決方案,並找到了一個stackoverflow問題答案 ,以完成本教程所缺少的內容。

但是,它使用質量低的YUV420。 我想實現YUV444,在研究了色度二次采樣並查看了YUV的不同格式后,如何實現它感到困惑。 據我了解,YUV420是質量YUV444的四分之一。 YUV444表示每個像素都有自己的色度樣本,因此更為詳細; YUV420表示像素被分組在一起並具有相同的色度樣本,因此較不詳細。

而且據我了解,YUV(420、422、444)的不同格式在排序y,u和v的方式上是不同的。所有這些都有些讓人不知所措,因為我在編解碼器,轉換方面做得很少,等等。任何幫助將不勝感激,如果需要其他信息,請在投票之前讓我知道。

這是我提到的有關轉換為YUV420的答案中的代碼:

texture = SDL_CreateTexture(
        renderer,
        SDL_PIXELFORMAT_YV12,
        SDL_TEXTUREACCESS_STREAMING,
        pCodecCtx->width,
        pCodecCtx->height
        );
    if (!texture) {
        fprintf(stderr, "SDL: could not create texture - exiting\n");
        exit(1);
    }

    // initialize SWS context for software scaling
    sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
        pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
        AV_PIX_FMT_YUV420P,
        SWS_BILINEAR,
        NULL,
        NULL,
        NULL);

    // set up YV12 pixel array (12 bits per pixel)
    yPlaneSz = pCodecCtx->width * pCodecCtx->height;
    uvPlaneSz = pCodecCtx->width * pCodecCtx->height / 4;
    yPlane = (Uint8*)malloc(yPlaneSz);
    uPlane = (Uint8*)malloc(uvPlaneSz);
    vPlane = (Uint8*)malloc(uvPlaneSz);
    if (!yPlane || !uPlane || !vPlane) {
        fprintf(stderr, "Could not allocate pixel buffers - exiting\n");
        exit(1);
    }

    uvPitch = pCodecCtx->width / 2;
    while (av_read_frame(pFormatCtx, &packet) >= 0) {
        // Is this a packet from the video stream?
        if (packet.stream_index == videoStream) {
            // Decode video frame
            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

            // Did we get a video frame?
            if (frameFinished) {
                AVPicture pict;
                pict.data[0] = yPlane;
                pict.data[1] = uPlane;
                pict.data[2] = vPlane;
                pict.linesize[0] = pCodecCtx->width;
                pict.linesize[1] = uvPitch;
                pict.linesize[2] = uvPitch;

                // Convert the image into YUV format that SDL uses
                sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
                    pFrame->linesize, 0, pCodecCtx->height, pict.data,
                    pict.linesize);

                SDL_UpdateYUVTexture(
                    texture,
                    NULL,
                    yPlane,
                    pCodecCtx->width,
                    uPlane,
                    uvPitch,
                    vPlane,
                    uvPitch
                    );

                SDL_RenderClear(renderer);
                SDL_RenderCopy(renderer, texture, NULL, NULL);
                SDL_RenderPresent(renderer);

            }
        }

        // Free the packet that was allocated by av_read_frame
        av_free_packet(&packet);
        SDL_PollEvent(&event);
        switch (event.type) {
            case SDL_QUIT:
                SDL_DestroyTexture(texture);
                SDL_DestroyRenderer(renderer);
                SDL_DestroyWindow(screen);
                SDL_Quit();
                exit(0);
                break;
            default:
                break;
        }

    }

    // Free the YUV frame
    av_frame_free(&pFrame);
    free(yPlane);
    free(uPlane);
    free(vPlane);

    // Close the codec
    avcodec_close(pCodecCtx);
    avcodec_close(pCodecCtxOrig);

    // Close the video file
    avformat_close_input(&pFormatCtx);

編輯:

經過更多的研究,我了解到YUV420中首先存儲所有Y,然后依次存儲U和V字節,如下圖所示:
(來源: wikimedia.org

但是我還了解到YUV444以U,Y,V的順序存儲,並且重復如下圖所示:

我嘗試更改代碼中的一些內容:

    // I changed SDL_PIXELFORMAT_YV12 to SDL_PIXELFORMAT_UYVY
    // as to reflect the order of YUV444
    texture = SDL_CreateTexture(
        renderer,
        SDL_PIXELFORMAT_UYVY,
        SDL_TEXTUREACCESS_STREAMING,
        pCodecCtx->width,
        pCodecCtx->height
        );
    if (!texture) {
        fprintf(stderr, "SDL: could not create texture - exiting\n");
        exit(1);
    }

    // Changed AV_PIX_FMT_YUV420P to AV_PIX_FMT_YUV444P
    // for rather obvious reasons
    sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
        pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
        AV_PIX_FMT_YUV444P,
        SWS_BILINEAR,
        NULL,
        NULL,
        NULL);

    // There are as many Y, U and V bytes as pixels I just
    // made yPlaneSz and uvPlaneSz equal to the number of pixels
    yPlaneSz = pCodecCtx->width * pCodecCtx->height;
    uvPlaneSz = pCodecCtx->width * pCodecCtx->height;
    yPlane = (Uint8*)malloc(yPlaneSz);
    uPlane = (Uint8*)malloc(uvPlaneSz);
    vPlane = (Uint8*)malloc(uvPlaneSz);
    if (!yPlane || !uPlane || !vPlane) {
        fprintf(stderr, "Could not allocate pixel buffers - exiting\n");
        exit(1);
    }

    uvPitch = pCodecCtx->width * 2;
    while (av_read_frame(pFormatCtx, &packet) >= 0) {
        // Is this a packet from the video stream?
        if (packet.stream_index == videoStream) {
            // Decode video frame
            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

            // Rearranged the order of the planes to reflect UYV order
            // then set linesize to the number of Y, U and V bytes
            // per row
            if (frameFinished) {
                AVPicture pict;
                pict.data[0] = uPlane;
                pict.data[1] = yPlane;
                pict.data[2] = vPlane;
                pict.linesize[0] = pCodecCtx->width;
                pict.linesize[1] = pCodecCtx->width;
                pict.linesize[2] = pCodecCtx->width;

                // Convert the image into YUV format that SDL uses
                sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
                    pFrame->linesize, 0, pCodecCtx->height, pict.data,
                    pict.linesize);

                SDL_UpdateYUVTexture(
                    texture,
                    NULL,
                    yPlane,
                    1,
                    uPlane,
                    uvPitch,
                    vPlane,
                    uvPitch
                    );
//.................................................

但是現在我在調用SDL_UpdateYUVTexture訪問沖突...老實說,我不確定是什么問題。 我認為這可能與設置AVPicture pic的成員datalinesize不當有關,但我並不樂觀。

經過數小時的搜索,我無意間發現了關於YUV444對打包或平面模式的支持的帖子 我發現的唯一當前格式是打包的AYUV。

他們得到的答案是所有當前支持的格式的列表,其中不包括AYUV。 因此,SDL不支持YUV444。

唯一的解決方案是使用支持AYUV / YUV444的其他庫。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM