首页 > 解决方案 > 使用 libav 将 RGBA 帧编码为 MP4 但输出一团糟

问题描述

我正在尝试将视频解码为 RGB 帧,然后对帧进行后处理,最后将帧编码为视频。但是输出的视频完全是一团糟: 来自potplayer的截图

我写了一个最小的例子来说明我的想法。首先,我从一些源视频中读取了一些信息:

    AVFormatContext* inputFormatCtx = nullptr;
    int ret = avformat_open_input(&inputFormatCtx, inputParamsVideo, nullptr, nullptr);
    assert(ret >= 0);
    ret = avformat_find_stream_info(inputFormatCtx, NULL);
    av_dump_format(inputFormatCtx, 0, inputParamsVideo, 0);

    assert(ret >= 0);
    AVStream* inputVideoStream = nullptr;
    for (int i = 0; i < inputFormatCtx->nb_streams; i++)
    {
        const auto inputStream = inputFormatCtx->streams[i];
        if (inputStream->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            inputVideoStream = inputStream;
            break;
        }
    }

    assert(inputVideoStream != nullptr);
    AVCodecParameters* inputParams = inputVideoStream->codecpar;
    AVRational framerate = inputVideoStream->codec->framerate;
    auto gop_size = inputVideoStream->codec->gop_size;
    auto maxBFrames = inputVideoStream->codec->max_b_frames;

然后我将信息分配给输出流:

AVFormatContext *outputAVFormat = nullptr;
avformat_alloc_output_context2(&outputAVFormat, nullptr, nullptr, kOutputPath);
assert(outputAVFormat);
AVCodec* codec = avcodec_find_encoder(outputAVFormat->oformat->video_codec);
assert(codec);
AVCodecContext* encodingCtx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(encodingCtx, inputParams);
encodingCtx->time_base = av_inv_q(framerate);
encodingCtx->max_b_frames = maxBFrames;
encodingCtx->gop_size = gop_size;


if (outputAVFormat->oformat->flags & AVFMT_GLOBALHEADER)
    encodingCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
AVStream* outStream = avformat_new_stream(outputAVFormat, nullptr);
assert(outStream != nullptr);
ret = avcodec_parameters_from_context(outStream->codecpar, encodingCtx);
assert(ret >= 0);
outStream->time_base = encodingCtx->time_base;

然后我将 RGBA 帧(从文件中读取)转换为 YUV420Psws_scale和编码:

    ret = avcodec_open2(encodingCtx, codec, nullptr);
    assert(ret >= 0);
    av_dump_format(outputAVFormat, 0, kOutputPath, 1);

    ret = avio_open(&outputAVFormat->pb, kOutputPath, AVIO_FLAG_WRITE);
    assert(ret >= 0);
    ret = avformat_write_header(outputAVFormat, nullptr);
    assert(ret >= 0);

    AVFrame* frame = av_frame_alloc();
    frame->width = inputParams->width;
    frame->height = inputParams->height;
    frame->format = inputParams->format;
    frame->pts = 0;
    assert(ret >= 0);

    ret = av_frame_get_buffer(frame, 32);
    int frameCount = 0;
    assert(ret >= 0);
    ret = av_frame_make_writable(frame);
    assert(ret >= 0);
    SwsContext* swsContext = sws_getContext(inputParams->width, inputParams->height,
        AV_PIX_FMT_RGBA, frame->width,
        frame->height, static_cast<AVPixelFormat>(inputParams->format),
        SWS_BILINEAR, NULL, NULL, NULL);


    for (auto inputPicPath : std::filesystem::directory_iterator(kInputDir))
    {
        int width, height, comp;
        unsigned char* data = stbi_load(inputPicPath.path().string().c_str(), &width, &height, &comp, 4);
        int srcStrides[1] = { 4 * width };
        int ret = sws_scale(swsContext, &data, srcStrides, 0, height, frame->data,
            frame->linesize);
        assert(ret >= 0);
        frame->pts = frameCount;
        //frame->pict_type = AV_PICTURE_TYPE_I;
        frameCount += 1;
        encode(encodingCtx, frame, 0, outputAVFormat);

        stbi_image_free(data);
    }

    while (encode(encodingCtx, nullptr, 0, outputAVFormat))
    {
        ;
    }

    static bool encode(AVCodecContext* enc_ctx, AVFrame* frame, std::uint32_t streamIndex,
        AVFormatContext * formatCtx)
    {
        int ret;
        int got_output = 0;
        AVPacket packet = {};
        av_init_packet(&packet);
        ret = avcodec_encode_video2(enc_ctx, &packet, frame, &got_output);
        assert(ret >= 0);
        if (got_output) {
            packet.stream_index = streamIndex;
            av_packet_rescale_ts(&packet, enc_ctx->time_base, formatCtx->streams[streamIndex]->time_base);
            ret = av_interleaved_write_frame(formatCtx, &packet);
            assert(ret >= 0);
            return true;
        }
        else {
            return false;
        }
    }

最后我清理了一些东西:

    av_write_trailer(outputAVFormat);
    sws_freeContext(swsContext);
    avcodec_free_context(&encodingCtx);
    avio_closep(&outputAVFormat->pb);
    avformat_free_context(outputAVFormat);
    av_frame_free(&frame);

我转储了输入格式和输出格式:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'H:\Me.MP4':
  Metadata:
    major_brand     : mp42
    minor_version   : 1
    compatible_brands: mp41mp42isom
    creation_time   : 2019-04-03T05:44:22.000000Z
  Duration: 00:00:06.90, start: 0.000000, bitrate: 1268 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 540x960, 1238 kb/s, 29.86 fps, 30 tbr, 600 tbn, 1200 tbc (default)
    Metadata:
      creation_time   : 2019-04-03T05:44:22.000000Z
      handler_name    : Core Media Video
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 8000 Hz, stereo, fltp, 24 kb/s (default)
    Metadata:
      creation_time   : 2019-04-03T05:44:22.000000Z
      handler_name    : Core Media Audio
[libx264 @ 000002126F90C1C0] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 000002126F90C1C0] profile High, level 3.1, 4:2:0, 8-bit
[libx264 @ 000002126F90C1C0] 264 - core 157 - H.264/MPEG-4 AVC codec - Copyleft 2003-2018 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=12 lookahead_threads=2 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=0 weightp=2 keyint=12 keyint_min=1 scenecut=40 intra_refresh=0 rc_lookahead=12 rc=abr mbtree=1 bitrate=1238 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, mp4, to './output.mp4':
    Stream #0:0: Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 540x960, q=2-31, 1238 kb/s, 29.86 tbn

更新:

我删除后

encodingCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

输出视频是正确的。此外,输出 avi 也可以。

标签: ffmpegmp4h.264video-processinglibavcodec

解决方案


推荐阅读