mediasource api 结合 fmp4 实现低延迟自适应流播放,需初始化段(moov)与分片段(moof+mdat)分离、按时间顺序加载、正确设置 mime 类型、在 sourceopen 后追加缓冲并用 updateend 驱动分片加载,必要时校准时间戳。

MediaSource API 是 HTML5 中实现自定义视频流加载的核心机制,配合 FMP4(Fragmented MP4)格式,可以实现低延迟、可动态切换码率的流式播放。关键在于将视频按时间或大小分片(fragments),以 ISO BMFF 格式组织,并通过 SourceBuffer 顺序追加。
准备可分片的FMP4文件结构
FMP4 不是普通 MP4,必须满足“分片化”要求:包含独立的 moov(初始化段)和多个连续的 moof + mdat(媒体分片段)。初始化段只需加载一次,后续每个分片只含帧数据与时间信息,体积小、解析快。
- 服务端需提供两个资源路径:一个
/init.mp4(含moov),多个/seg-1.mp4、/seg-2.mp4等(仅含moof+mdat) - 可用
ffmpeg生成标准 FMP4:ffmpeg -i input.mp4 -c:v libx264 -c:a aac -f mp4 -movflags +frag_keyframe+empty_moov+default_base_moof output.fmp4 - 注意:分片必须严格按解码时间顺序排列,
moof中的tfdt时间戳需连续且无重叠
创建MediaSource并挂载到video元素
先创建 MediaSource 实例,监听 sourceopen 事件后添加 SourceBuffer,再将 MediaSource 赋值给 video.src:
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', () => {
const mime = 'video/mp4; codecs="avc1.64001f, mp4a.40.2"';
const sourceBuffer = mediaSource.addSourceBuffer(mime);
// 加载 init segment
fetch('/init.mp4')
.then(r => r.arrayBuffer())
.then(buf => sourceBuffer.appendBuffer(buf));
});
注意:MIME 类型中的 codecs 必须与实际视频音频编码严格匹配(如 H.264 主配置、AAC LC),否则 addSourceBuffer 抛错。
分片顺序加载与缓冲管理
在 updateend 事件中触发下一分片加载,避免阻塞;同时监听 updatestart 和 error 做状态控制:
- 维护一个分片索引队列(如
[1, 2, 3...]),每次成功 append 后取下一个 - 在
sourceBuffer.onupdateend中发起下一分片请求:fetch(`/seg-${nextIndex}.mp4`).then(r => r.arrayBuffer()).then(buf => sourceBuffer.appendBuffer(buf)); - 若
sourceBuffer.updating === true,暂不追加新数据,防止冲突 - 可调用
sourceBuffer.remove(start, end)清理已播放旧缓冲(适用于直播场景)
处理时间戳偏移与同步问题
FMP4 分片内的时间戳(tfdt)默认是绝对时间,但浏览器要求 append 的分片时间线连续。若服务端未对齐,需手动偏移:
- 首次 append 初始化段后,记录其
duration或使用video.duration推算起始时间 - 后续分片若含相对时间戳,需在 JS 层读取
moof.tfdt.baseMediaDecodeTime并累加偏移量(需解析二进制,推荐用 mux.js 辅助) - 更稳妥方式:服务端统一用绝对 PTS,且分片间
tfdt差值等于前一分片时长
不复杂但容易忽略
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











