使用libavcodec解码视频到YUV422

时间:2018-08-14 18:16:33

标签: ffmpeg rgb video-processing yuv libavcodec

我想编写一个打开mp4文件并将其解码为yuv422文件的C ++应用程序。 我基于libavcodec教程编写了一些代码,但是找不到将位深度和格式设置为YUV422的地方。

这是我写的一些代码

void read_video_stream(AVFormatContext *pFormatContext, AVCodec *pCodec, AVCodecParameters *pCodecParameters,
                       int video_stream_index)
{
    AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec);
    std::unique_ptr<AVCodecContext, av_deleter> ctx_guard(pCodecContext);
    if (!pCodecContext) {
        return;
    }
    if (avcodec_parameters_to_context(pCodecContext, pCodecParameters) < 0) {
        return;
    }
    // i tried setting it here
    if (avcodec_open2(pCodecContext, pCodec, NULL) < 0) {
        return;
    }
    while (true) {
        std::unique_ptr<AVPacket, std::function<void(AVPacket*)>> packet{
                        new AVPacket,
                        [](AVPacket* p){ av_packet_unref(p); delete p; }};

        av_init_packet(packet.get());
        int response = av_read_frame(pFormatContext, packet.get());
        if (AVERROR_EOF == response) {
            std::cout << "EOF\n";
        }
        else if (response < 0) {
            std::cout << "Error " << response;
            return;
        }
        if (packet->stream_index != video_stream_index) {
            continue;
        }
        response = avcodec_send_packet(pCodecContext, packet.get());
        if (response < 0) {
            std::cout << "Error while sending a packet to the decoder: " << response;
            return;
        }
        while (response >= 0) {
            std::shared_ptr<AVFrame> pFrame{  av_frame_alloc(), AVFrameDeleter};
            response = avcodec_receive_frame(pCodecContext, pFrame.get());
            if (response == AVERROR(EAGAIN)) {
                continue;
            }
            if (response == AVERROR_EOF) {
                std::cerr << "got to last frame\n";
                return;
            }
            else if (response < 0) {
                std::cerr << "Error while receiving a frame from the decoder: " << response;
                return;
            }

            if (response >= 0) {
                // copy line to cyclic buffer
                cb.push_back(std::move(pFrame));

            }
        }
    }
}
我的最终目标是将未压缩的数据(需要在pFrame-> data [0-2]中)发送到网络中的设备。 你能帮我解决这个问题吗 谢谢

1 个答案:

答案 0 :(得分:2)

评论全部说明”“您不解码为特定格式。您将解码为流所编码的格式。如果要使用其他格式,请在确定后转换(使用swacale) 。”

我会填补空白。多数电影文件(mp4)最初都是YUV420P格式,因此在解码后将获得此文件。但这可能有所不同,因此我将其称为解码像素格式。获得具有解码像素格式的AVFrame之后,可以使用swscale()函数将其转换为任何其他像素格式。

像素格式转换(使用swscale())需要两件事:

1)设置缩放器上下文。在FFmpeg中,像素格式的转换和缩放是通过相同的功能完成的。保持两个大小不变,就不会缩放,只能转换。

除非参数已更改且不再有效,否则您无需重复执行一次以上操作:

SwsContext *swsContext = sws_getContext(src_width, src_height, src_pixfmt,
                                        dst_width, dst_height, dst_pixfmt,
                                        SWS_BILINEAR, NULL, NULL, NULL);

示例src_pixfmtAV_PIX_FMT_YUV420P
示例dst_pixfmtAV_PIX_FMT_YUV422PAV_PIX_FMT_UYVY422
SWS_BILINEAR是缩放算法。当还需要扩展时,您可能需要这一点。他们说双线性有利于扩大规模,而双三次有利于缩小规模。我不是这个领域的专家。但是我所知道的是,双线性比其他许多算法都运行良好且速度更快。

2)要进行转换,您将需要以下内容:

AVFrame *dstframe = av_frame_alloc();
if (dstframe == NULL)
{
    fprintf(stderr, "Error: av_frame_alloc() failed.\n");
    exit(EXIT_FAILURE);
}

dstframe->format = AV_PIX_FMT_UYVY422; /* choose same format set on sws_getContext() */
dstframe->width  = srcframe->width; /* must match sizes as on sws_getContext() */
dstframe->height = srcframe->height; /* must match sizes as on sws_getContext() */
int ret = av_frame_get_buffer(dstframe, 32);
if (ret < 0)
{
    fprintf(stderr, "Error: could not allocate the video frame data\n");
    exit(EXIT_FAILURE);
}

/* do the conversion */
ret = sws_scale(swsContext,             /* SwsContext* on step (1) */
                srcframe->data,         /* srcSlice[] from decoded AVFrame */
                srcframe->linesize,     /* srcStride[] from decoded AVFrame */
                0,                      /* srcSliceY   */
                src_height,             /* srcSliceH  from decoded AVFrame */
                dstframe->data,         /* dst[]       */
                dstframe->linesize);    /* dstStride[] */

if (ret < 0)
{
    /* error handling */
}

成功转换后,您将在dstframe上拥有所需的东西。

在此处查看更多格式,功能和参数的详细信息:https://www.ffmpeg.org/doxygen/trunk/index.html

希望有帮助。