You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用libav封装MP4视频时如何调整pts/dts补偿帧率不足问题

解决方案

问题根源

你最初的思路是完全正确的,用真实生成时间计算pts就能解决播放快进的问题,报错pts < dts以及快进是由3个配置错误导致的:

  1. 流和编码器的time_base设置不合理,用1/FPS作为时间基粒度过大,无法匹配你传入的毫秒级时间戳精度
  2. x264默认开启B帧编码,B帧会导致dts和pts不一致,非固定间隔送帧场景下很容易出现时序报错
  3. 原逐帧递增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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 01:27:00