我想使用 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,但似乎没有一个对我有用:
AV_CODEC_FLAG_GLOBAL_HEADER
:使用h264流创建mkv容器时数据无效,因为额外数据为空我在下面附上了我的代码(错误处理和一些其他细节被删除!)。当我写入 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
[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,因为它从未根据现有帧速率设置。