如何将ffmpeg视频帧转换为YUV444?

时间:2018-03-04 23:41:05

标签: c++ c ffmpeg yuv subsampling

我一直关注如何使用tutorial和SDL制作一个没有音频的简单视频播放器的ffmpeg。在查看教程时,我意识到它已经过时了,它使用的许多函数,对于ffmpeg和SDL,都被弃用了。所以我搜索了一个最新的解决方案,发现了一个stackoverflow问题answer,完成了教程缺失的内容。

然而,它使用质量低的YUV420。我想实现YUV444并且在研究色度子采样之后,查看YUV的不同格式,我对如何实现它感到困惑。据我所知YUV420是YUV444质量的四分之一。 YUV444意味着每个像素都有自己的色度样本,因此更加详细,而YUV420意味着像素被组合在一起并具有相同的色度样本,因此不太详细。

根据我的理解,YUV(420,422,444)的不同格式在命令y,u和v的方式上有所不同。所有这些都有点压倒性因为我对编解码器没有做太多,转换等。任何帮助将非常感激,如果需要其他信息,请在downvoting之前告诉我。

以下是我提到的有关转换为YUV420的answer的代码:

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字节的组合,如下图所示:

但是我也了解到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不当有关,但我并不积极。

1 个答案:

答案 0 :(得分:1)

经过几个小时的搜索网络寻找可能的答案后,我偶然发现了post,其中有人询问有关YUV444支持打包或平面模式的信息。我发现的唯一当前格式是打包的AYUV。

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

唯一的解决方案是使用支持AYUV / YUV444的不同库。