HTML5音视频同步的本质是依据媒体文件内嵌PTS时间戳严格解码渲染,而非简单同时调用play();浏览器完全信任PTS,不对音画偏移自动修正,对齐必须在数据层(如转码封装时调整音频PTS)完成,播放时仅可微调补偿抖动。

HTML5 视频与音频同步,本质不是“让两者同时开始”,而是让它们在同一时间轴上按各自时间戳严格解码渲染。浏览器不自动修正音画偏移,所有对齐必须由时间戳驱动、由代码保障。
时间戳是唯一权威依据
浏览器完全信任媒体文件内嵌的 PTS(显示时间戳)。MP4 容器中,视频轨和音频轨各自携带 PTS,解码器按这些值决定“该帧/该样本何时呈现”。若原始文件里音频 PTS 比视频早 200ms,播放时音频就一定会先响——哪怕你调用 play() 的时机完全一致。
常见错误做法:用 video.currentTime = 0 后立刻设 audio.currentTime = 0。这仅设置播放起始点,不改变分片内 PTS,无法修复底层时间偏移。
验证方法:用 ffprobe -v quiet -show_entries stream=codec_type,start_time -of default input.mp4 查看两轨 start_time 是否一致。偏差超过 0.05 秒,就需预处理。
立即学习“前端免费学习笔记(深入)”;
对齐必须在数据层完成
真正可靠的对齐发生在媒体数据写入前,而非播放时补救:
- 服务端转码时加 -copyts 或 -fflags +genpts,避免丢弃或重算时间戳
- 要音频比视频晚播 300ms,就在封装阶段把所有音频分片的 PTS 统一 +0.3(单位为 timescale tick)
- 使用 fMP4 格式,确保每个 moof box 中含可修改的 earliest_presentation_time 字段
- 禁止直接改视频 PTS——它是主时间基准,动它易引发跳帧或解码失败
播放时双 SourceBuffer 严格交替写入
通过 MediaSource 实现动态加载时,音视频必须共用一个逻辑时间线:
- 先写入相同的 init segment 到 video 和 audio 的 SourceBuffer
- 后续 media segment 按时间递增顺序交替 append:视频 PTS=10.0 → 音频 PTS=10.3 → 视频 PTS=10.5 → 音频 PTS=10.8
- 写入前确认 sourceBuffer.timestampOffset === 0,否则浏览器会叠加额外偏移
- 监听 updateend 事件,确保上一段写完再写下一段,防止时间断层
运行中微调只用于补偿抖动
网络波动或解码延迟可能导致 ≤100ms 的轻微脱节,此时可用 JS 补偿,但仅作兜底:
- 监听 video.timeupdate,每帧读取 video.currentTime
- 计算目标音频时间:audioTargetTime = video.currentTime + targetDelay
- 若 Math.abs(audio.currentTime - audioTargetTime) > 40ms,且 audio.readyState ≥ HAVE_FUTURE_DATA,才执行 audio.currentTime = audioTargetTime
- 频繁触发说明时间戳本身有问题,应回溯数据源,而非依赖运行时修补



















