使用libav封装MP4视频时如何调整pts/dts补偿帧率不足问题
解决方案
问题根源
你最初的思路是完全正确的,用真实生成时间计算pts就能解决播放快进的问题,报错pts < dts以及快进是由3个配置错误导致的:
- 流和编码器的
time_base设置不合理,用1/FPS作为时间基粒度过大,无法匹配你传入的毫秒级时间戳精度 - x264默认开启B帧编码,B帧会导致dts和pts不一致,非固定间隔送帧场景下很容易出现时序报错
- 原逐帧递增pts的逻辑完全忽略了实际生成帧的时间间隔,播放器只会按照固定帧率对应的时间间隔播放,自然会出现快进
具体修改步骤
1. 调整初始化阶段的编码/流配置
首先修改视频流和编码器上下文的基础参数,这一步是核心:
// 删除原来的 time_base = {1, FPS} 配置,替换为以下内容 // 流时间基采用毫秒级粒度,匹配你传入的时间戳单位 ost->st->time_base = (AVRational){1, 1000}; // 编码器时间基和流时间基保持一致 AVCodecContext *c = ost->enc; c->time_base = ost->st->time_base; // 仅将预期帧率作为编码器参考参数,不强制固定间隔送帧 c->framerate = (AVRational){FPS, 1}; // 关闭B帧,从根源避免dts和pts不一致的问题,实时录制场景无负面影响 c->max_b_frames = 0; // 可选:x264编码器针对性优化,降低实时编码延迟 av_opt_set(c->priv_data, "preset", "veryfast", 0); av_opt_set(c->priv_data, "tune", "zerolatency", 0);
2. 调整get_video_frame的pts计算逻辑
你原来的pts计算逻辑不需要大改,只需要保证传入的timestamp是从录制开始到当前帧生成时的毫秒数,且严格单调递增即可:
// timestamp 为录制启动到当前帧生成的毫秒数,必须保证比上一帧的timestamp大 ost->frame->pts = av_rescale_q(timestamp, AVRational{1, 1000}, c->time_base); // 由于编码器time_base已经设为1/1000,也可以直接简化为: // ost->frame->pts = timestamp;
3. write_frame逻辑无需修改
你现有的write_frame逻辑完全符合要求:av_packet_rescale_ts会自动完成编码器时间基到流时间基的转换,av_interleaved_write_frame会自动处理封装层面的时序对齐,不需要手动调整packet的pts/dts。
注意事项
- 必须保证传入的
timestamp严格单调递增,不能出现当前帧时间戳小于上一帧的情况,否则仍会出现时序错误 - 流的
avg_frame_rate字段可以设置为你预期的FPS值,该字段仅作为元数据展示,不会影响实际播放速度 - 如果确实需要保留B帧编码,不要手动修改packet的dts,保持
av_interleaved_write_frame调用即可,ffmpeg会自动完成时序排序,只是需要更大的缓存延迟
内容的提问来源于stack exchange,提问作者Nuno Santos
相关产品推荐
相关产品推荐

