音视频同步必须以pts为基准,音频主时钟最稳定;硬解易丢pts,需手动透传;网络流和mp4封装的时间戳偏差需在解封装阶段归一化。

音视频 PTS/DTS 不对齐时播放器会卡顿或音画不同步
音画不同步本质是音频和视频帧的显示时间没对上,PTS(Presentation Time Stamp)才是决定“什么时候播”的关键,DTS(Decoding Time Stamp)只管“什么时候解码”,播放器必须用 PTS 做同步基准。常见错误是直接拿 DTS 当显示时间用,尤其在 H.264/H.265 的 B 帧场景下,DTS 和 PTS 顺序不一致,硬套会导致画面跳变或音频拖长。
实操建议:
- 解封装后立刻检查每帧的
pts是否有效(非AV_NOPTS_VALUE),无效时按前一帧 + 帧间隔估算,别直接丢弃 - 音频流通常
pts == dts,但视频流一定以pts为准;FFmpeg 解码后AVFrame->pts可能为空,此时应从AVPacket->pts搬过来(前提是该 packet 成功送入解码器) - 不要依赖解码器自动补
pts:libavcodec 在某些配置下(如AV_CODEC_FLAG_LOW_DELAY)可能不填充AVFrame->pts
用音频时钟做主时钟(audio master clock)最稳
人耳对音频延迟更敏感,所以主流播放器(VLC、MPV)都把音频输出时间作为全局参考,视频帧根据这个时钟动态调整显示时机。如果反过来用视频做主时钟,音频容易出现断续或重采样抖动。
实操建议:
- 音频时钟不是简单读取声卡已播放样本数——要用
pa_context_get_timing_info()(PulseAudio)或IAudioClock::GetPosition()(WASAPI)这类接口获取硬件级播放位置,避免靠软件计数累积误差 - 每次音频回调(如
SDL_AudioCallback)里更新时钟值,用单调递增的高精度时间(std::chrono::steady_clock)打时间戳,别用系统时间 - 视频渲染线程每帧计算:
video_pts - audio_clock,差值超过阈值(如 ±40ms)就插帧或丢帧,而不是强行等
ffplay 的 sync_type 参数背后是三种同步策略
ffplay -sync audio 是默认行为,对应音频主时钟;-sync video 把视频 PTS 当主时钟,适合调试;-sync ext 则接入外部时钟(比如 NTP 或硬件信号)。实际写 C++ 播放器时,sync_type 不是开关,而是影响三个地方:时钟选择逻辑、音视频队列的等待策略、以及丢帧/重复帧判定条件。
实操建议:
- 音频队列满时,宁可丢视频帧也不丢音频包——音频缓冲区一旦欠载,就会触发重采样或静音填充,用户立刻感知到卡顿
- 视频帧显示前必须校验:
frame->pts != AV_NOPTS_VALUE && frame->pts >= audio_clock - tolerance,否则跳过渲染 - 启用
AV_SYNC_AUDIO_MASTER模式时,av_gettime_relative()获取当前系统时间要转成与音频时钟同单位(微秒),别直接拿AV_TIME_BASE去除
硬解(VA-API / VideoToolbox)下 PTS 传递容易断裂
硬解码器(如 Intel VA-API、Apple VideoToolbox)常把 pts 存在私有结构体里,FFmpeg 的 AVHWDeviceContext 不保证透传到 AVFrame。现象是硬解视频帧的 pts 全为 AV_NOPTS_VALUE,导致同步完全失效。
实操建议:
- 创建硬解设备时开启
AV_HWDEVICE_OPTION_USE_FRAMES(FFmpeg 5.0+),并确保AVBufferRef的user_opaque字段存了原始 packet 的pts - 硬解成功后,在
get_buffer2回调或AVHWFramesContext::hwframe_lock后手动把 packet 的pts拷贝进AVFrame->pts - macOS 上 VideoToolbox 返回的
CMSampleBufferRef可用CMSampleBufferGetOutputPresentationTimeStamp()提取真实 PTS,别信AVFrame->pts
最难搞的其实是网络流里的 PCR/SCR 时间戳漂移,还有 MP4 封装中 moov 里 mvhd 的 time_scale 和 track 中 tkhd 的不一致——这些不会报错,但会让所有 PTS 偏移一个固定量,得在 demux 阶段就归一化。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











