配置AVStream生成29.97FPS MKV时帧率显示异常,如何解决?
解决FFmpeg编码MKV时time_base被修改导致帧率显示异常的问题
MKV(Matroska)的muxer有个内置行为:它会自动调整视频流的time_base到1/1000(毫秒精度),这就是你设置的1001/30000会被avformat_write_header覆盖的原因。不过别担心,播放正常说明你的PTS转换逻辑是对的,只是播放器和媒体信息工具依赖的帧率参数没被正确设置而已。
这里有两个关键步骤可以解决显示问题:
1. 明确设置视频流的帧率参数
MKV muxer会忽略你设置的time_base来计算帧率,但它会尊重你显式设置的avg_frame_rate和r_frame_rate。对于NTSC的29.97fps,对应的帧率比例是30000/1001,直接把这两个参数加上:
AVStream *videoStream = avformat_new_stream(_formatContext, NULL); if (!videoStream) { // 处理创建流失败的情况 } // 核心:设置正确的NTSC帧率 videoStream->avg_frame_rate = (AVRational){30000, 1001}; videoStream->r_frame_rate = videoStream->avg_frame_rate;
2. 确保编码器上下文与流参数同步
在调用avcodec_parameters_from_context之前,先把编码器上下文的time_base设置为你需要的值,这样codecpar会同步这个信息(即使muxer后续修改stream的time_base,codecpar里的信息依然能辅助工具识别正确帧率):
// 先设置编码器上下文的time_base _videoCodecContext->time_base = (AVRational){ (int)_frameDuration.value, (int)_frameDuration.timescale }; // 1001, 30000 // 再复制参数到流的codecpar int err = avcodec_parameters_from_context(videoStream->codecpar, _videoCodecContext); if (err < 0) { // 处理参数复制失败 }
修改后的完整代码片段
把这些调整整合到你的现有代码中:
AVOutputFormat *outputFormat = av_guess_format("matroska", NULL, NULL); int err = avformat_alloc_output_context2(&_formatContext, outputFormat, NULL, filename); // 设置编码器上下文的time_base _videoCodecContext->time_base = (AVRational){ (int)_frameDuration.value, (int)_frameDuration.timescale }; // 1001, 30000 AVStream *videoStream = avformat_new_stream(_formatContext, NULL); if (!videoStream) { // 错误处理逻辑 } // 显式设置帧率参数 videoStream->avg_frame_rate = (AVRational){30000, 1001}; videoStream->r_frame_rate = videoStream->avg_frame_rate; // 同步编码器参数到流 err = avcodec_parameters_from_context(videoStream->codecpar, _videoCodecContext); if (err < 0) { // 错误处理逻辑 } // 打开文件并写入头 err = avio_open(&_formatContext->pb, filename, AVIO_FLAG_WRITE); if (err >= 0) { err = avformat_write_header(_formatContext, &options); }
原理说明
- MKV muxer调整
time_base是为了兼容大部分播放设备,这个行为无法直接阻止(也没必要,因为不影响播放同步)。 avg_frame_rate是MKV规范中用于标识媒体帧率的关键字段,MediaInfo和VLC都会优先读取这个值来显示帧率,而不是通过time_base计算。- 你之前的
av_packet_rescale_ts逻辑完全正确,只要基于最终stream的time_base来转换PTS,音视频同步就不会有问题。
这样修改后,VLC会显示正确的29.97fps,MediaInfo也会识别出标准的NTSC帧率,同时播放同步不受任何影响。
内容的提问来源于stack exchange,提问作者vtruant
相关产品推荐
相关产品推荐

