FFmpeg倍速播放本质是修改PTS/DTS时间戳而非重编码,视频通过缩放PTS控制显示间隔,音频可仅调PTS(变调)或结合重采样保持音调,需用av_rescale_q安全计算并统一时间基准。

ffmpeg 倍速播放本质是改 pts/dts,不是重编码
倍速播放在 FFmpeg 里不等于“重新压缩音视频”,而是通过调整解码后帧的时间戳(pts、dts)控制播放节奏。直接改 AVPacket 或 AVFrame 的时间戳即可实现 0.5x~2x 等常见倍速,无需调用 swr_convert 或 sws_scale——除非你同时要变调(pitch shift),那才需要音频重采样。
关键点:视频靠改 pts 控制显示间隔;音频靠改 pts + 调整采样率/重采样(可选)来同步。跳过重编码能避免画质损失和 CPU 暴增。
怎么安全修改 AVPacket 的 pts/dts 实现倍速
在解复用(av_read_frame)之后、解码(avcodec_send_packet)之前修改时间戳最稳妥。注意:必须先用 av_format_get_start_time 或流的 start_time 归一化,再按倍速系数缩放:
pkt.pts = av_rescale_q(pkt.pts - stream->start_time, stream->time_base, stream->time_base) * speed;pkt.dts = av_rescale_q(pkt.dts - stream->start_time, stream->time_base, stream->time_base) * speed;- 务必用
av_rescale_q而非简单乘除,避免整数溢出或精度丢失 - 若流没有
start_time(如某些 MP4),可用pkt.pts第一帧值作为偏移基准 - 修改后需重新计算
duration:例如pkt.duration = av_rescale_q(pkt.duration, stream->time_base, stream->time_base) / speed;
音频倍速后变调?要不要重采样由场景定
单纯拉伸 PTS 会导致音频播放变慢/快,但音调也跟着下降/升高(像磁带快放)。是否重采样取决于需求:
立即学习“C++免费学习笔记(深入)”;
- 只要“快听”不care音调(如播客倍速)→ 只调
pts,保持原始音频数据不变 - 需要保持原音调(如音乐学习)→ 必须用
swr_context重采样:输入采样率设为orig_sr * speed,输出设为orig_sr,再喂给解码器 - 注意:重采样必须在解码后、送声卡前做,且
swr_convert输入缓冲大小需按新采样率动态算(nb_samples * speed向上取整) - 别忘了更新重采样后的
AVFrame的pts:按原始时长 × speed 再映射回目标 time_base
seek 后倍速失效?关键在 time_base 和 pts 的单位一致性
倍速下 seek 容易跳到错帧,根本原因是 av_seek_frame 默认按流的 time_base 计算位置,而你已把 PTS 缩放过。修复方式:
- seek 前先把目标时间(秒)还原成“原始时间轴”:例如想跳到 60 秒处的 1.5x 播放点 → 实际 seek 到
60 / 1.5 = 40秒 - 或者,seek 后手动遍历 packet,找到第一个
pts >= target_pts_after_speed的包(比依赖av_seek_frame更稳) - 所有时间计算统一用
AV_TIME_BASE_Q转换,避免混用 ms / us / 秒导致差一个 1000 倍 - 调试时打印
av_ts2timestr(pkt.pts, &stream->time_base),确认修改前后时间戳逻辑自洽
真正麻烦的是音画同步逻辑——一旦倍速不一致(比如只加速视频没动音频),av_sync 模块会疯狂追帧。所以生产环境建议音视频使用同一倍速系数,并在 AVSyncType 为 AV_SYNC_AUDIO_MASTER 时,以音频 PTS 为唯一时钟源。


















