FFMPEG C 库:将 h264 流编码到 Matroska .mkv 容器中会创建损坏的文件

问题描述 投票:0回答:1

我想使用 FFMPEG C 库创建仅包含 h264 流的 Matroska Video .mkv 文件,但生成的 .mkv 文件已损坏。

该文件无法使用 Windows Media Player 或 ffplay 播放,当我尝试

ffprobe
生成的文件时,出现以下错误消息:

[h264 @ 0000015060d8f5c0] No start code is found.
    Last message repeated 1 times
[h264 @ 0000015060d8f5c0] non-existing PPS 0 referenced
    Last message repeated 1 times
[h264 @ 0000015060d8f5c0] decode_slice_header error
[h264 @ 0000015060d8f5c0] no frame!
[h264 @ 0000015060d8f5c0] non-existing PPS 0 referenced
    Last message repeated 1 times
[h264 @ 0000015060d8f5c0] decode_slice_header error
[h264 @ 0000015060d8f5c0] no frame!
[h264 @ 0000015060d8f5c0] non-existing PPS 0 referenced
    Last message repeated 1 times
# [...]
# this continues for a long time

我已按照其他故障排除步骤将 h264 编码为 Matroska,但似乎没有一个对我有用:

我在下面附上了我的代码(错误处理和一些其他细节被删除!)。当我写入 mp4 容器时,此代码有效,但写入 .mkv 时则无效。 ffmpeg 没有通过错误代码返回任何错误。

初始化代码(C++):

auto format = av_guess_format("matroska", (recording_base_path + full_file_path_).c_str(), NULL);
avformat_alloc_output_context2(&format_context_, format, NULL, (recording_base_path + full_file_path_).c_str());

// initialize ffmpeg codec context
const AVCodec* codec = avcodec_find_encoder_by_name("libx264");
video_stream_ = avformat_new_stream(format_context_, NULL);
codec_context_ = avcodec_alloc_context3(codec);

codec_context_->width = output_size_.Width;
codec_context_->height = output_size_.Height;
codec_context_->time_base = av_make_q(1, 90000);
codec_context_->gop_size = 1;
codec_context_->max_b_frames = 1;
codec_context_->pix_fmt = AV_PIX_FMT_YUV420P;
codec_context_->profile = FF_PROFILE_H264_MAIN;
codec_context_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

codec_context_->extradata = (uint8_t*)av_mallocz(1024 * 1024);
codec_context_->extradata_size = 1024 * 1024;

avcodec_parameters_from_context(video_stream_->codecpar, codec_context_);
avcodec_open2(codec_context_, codec, NULL);

avio_open(&format_context_->pb, full_file_path_.c_str(), AVIO_FLAG_WRITE);

avformat_write_header(format_context_, nullptr);

对帧进行编码并将其写入容器:

// Allocate input frame and copy data from mappedTex.pData
AVFrame *input_frame = av_frame_alloc();
input_frame->data[0] = (uint8_t *)mappedTex.pData;
input_frame->linesize[0] = mappedTex.RowPitch;

// Output frame
AVFrame *avFrame = av_frame_alloc();
avFrame->format = AV_PIX_FMT_YUV420P;
avFrame->width = codec_context_->width;
avFrame->height = codec_context_->height;
std::chrono::microseconds time_since_segment_start = std::chrono::duration_cast<std::chrono::microseconds>(frame.SystemRelativeTime() - started_at_);
avFrame->pts = av_rescale_q(time_since_segment_start.count(), AVRational{1,1000000}, codec_context_->time_base);

av_frame_get_buffer(avFrame, 0);

// Convert input_frame from RGB to YUV and save it into avFrame
sws_scale(sws_ctx_, input_frame->data, input_frame->linesize, 0,
          frame.ContentSize().Height, avFrame->data,
          avFrame->linesize);

av_frame_free(&input_frame);

auto ret = avcodec_send_frame(codec_context_, avFrame);
AVPacket* packet = av_packet_alloc();

while (true) {
    ret = avcodec_receive_packet(codec_context_, packet);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
        break;
    } else if (ret < 0) {
        std::cout << "avcodec_receive_packet failed" << std::endl;
    } else {
        av_packet_rescale_ts(packet, codec_context_->time_base, video_stream_->time_base);
        // TODO: try to write something to packet->side_data!
        ret = av_interleaved_write_frame(format_context_, packet);
        //ret = av_write_frame(format_context_, packet);
        if (ret < 0) {
            std::cout << "av_interleaved_write_frame failed" << std::endl;
        }
    }
}

上面代码运行时得到的输出:

[libx264 @ 00000175ac8ec1c0] MB rate (810000000) > level limit (16711680)
[libx264 @ 00000175ac8ec1c0] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 00000175ac8ec1c0] profile Main, level 6.2, 4:2:0, 8-bit
[libx264 @ 00000175ac8ec1c0] 264 - core 164 - H.264/MPEG-4 AVC codec - Copyleft 2003-2023 - http://www.videolan.org/x264.html - options: cabac=1 ref=1 deblock=1:0:0 analyse=0x1:0x111 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=0 me_range=16 chroma_me=1 trellis=1 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=36 lookahead_threads=6 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=0 weightp=0 keyint=1 keyint_min=1 scenecut=40 intra_refresh=0 rc=crf mbtree=0 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
[libx264 @ 00000175ac8ec1c0] frame I:107   Avg QP:19.47  size: 88593
[libx264 @ 00000175ac8ec1c0] mb I  I16..4: 76.9%  0.0% 23.1%
[libx264 @ 00000175ac8ec1c0] coded y,uvDC,uvAC intra: 17.4% 14.0% 12.8%
[libx264 @ 00000175ac8ec1c0] i16 v,h,dc,p: 93%  7%  0%  0%
[libx264 @ 00000175ac8ec1c0] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 44% 29%  8%  1%  2%  4%  5%  3%  4%
[libx264 @ 00000175ac8ec1c0] i8c dc,h,v,p: 86%  4% 10%  0%
[libx264 @ 00000175ac8ec1c0] kb/s:14859.19
c++ ffmpeg h.264 matroska
1个回答
0
投票

[h264 @ 0000015060d8f5c0] 未找到起始代码。 最后一条消息重复了 1 次 [h264 @ 0000015060d8f5c0] 引用不存在的 PPS 0 最后一条消息重复了 1 次 [h264 @ 0000015060d8f5c0] 解码切片头错误 [h264@0000015060d8f5c0]没有框架! [h264 @ 0000015060d8f5c0] 引用不存在的 PPS 0 最后一条消息重复了 1 次 [h264 @ 0000015060d8f5c0] 解码切片头错误 [h264@0000015060d8f5c0]没有框架! [h264 @ 0000015060d8f5c0] 引用不存在的 PPS 0 最后一条消息重复了1次

您的 H.264 编码器似乎没有使用这些设置来设置关键帧。请在这里学习示例代码:https://ffmpeg.org//doxygen/trunk/encode_video_8c-example.html

我被这句话玷污了:

codec_context_->time_base = av_make_q(1, 90000);

为什么不按照文档的建议使用

AVRational
类型?

time_base = (AVRational){1, 25};
framerate = (AVRational){25, 1};

没有任何障碍可以查看您的帧速率是否恒定。

对于有损 H.264,GOP 大小将大于 1:

    c->gop_size = 10;
    c->max_b_frames = 1;
    c->pix_fmt = AV_PIX_FMT_YUV420P;

我最担心的是你永远不会增加

pts
字段,因此序列中的每个帧都应该具有相同的时间戳,因为
av_packet_rescale_ts
函数只会扩大 valid 时间戳,但不会增加或重新计算它。因此,所有帧的时间戳均为 0,因为它从未根据现有帧速率设置。

© www.soinside.com 2019 - 2024. All rights reserved.