MediaSource 本身不支持直接循环,需结合 video.currentTime 和 timeupdate 事件控制跳转,并用 fMP4 片段预加载实现精准循环;关键前提是片段为 fragmented MP4 且切点对齐关键帧。

要实现视频中特定片段的循环回放,MediaSource 接口本身不直接提供“跳转+循环”逻辑,它只负责将媒体数据(如 MP4 或 WebM 片段)动态喂给 元素。真正的“指定时间范围循环”需结合 video.currentTime、video.addEventListener('timeupdate') 和 MediaSource 的分段加载能力协同完成。核心思路是:用 MediaSource 管理自定义片段数据流,再由 JS 控制播放器在到达结束点时精准跳回起点。
准备可随机访问的视频片段(关键前提)
MediaSource 要支持无缝循环,源数据必须满足:
- 视频编码为 fragmented MP4(fMP4) 或 WebM,不能是普通完整 MP4;
- 每个片段(segment)需包含独立的 moof + mdat,且带正确的时间戳(timestampOffset);
- 推荐使用 MP4Box.js 或服务端工具(如 FFmpeg)提取/生成目标区间片段:
-
ffmpeg -i input.mp4 -ss 10 -t 5 -c copy -f mp4 -reset_timestamps 1 output_10s_5s.mp4(注意:-c copy不重编码但可能不生成 fMP4;实际建议加-movflags +frag_keyframe+empty_moov输出可流式片段) - 浏览器中可用 MP4Box.js 切割并生成 fMP4 ArrayBuffer,确保每个 segment 的
startTime和duration准确
创建 MediaSource 并挂载到 video 元素
初始化 MediaSource 实例,监听 sourceopen 事件后创建 SourceBuffer,并设置 MIME 类型(如 video/mp4; codecs="avc1.64001f"):
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
<p>mediaSource.addEventListener('sourceopen', () => {
const sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.64001f"');
// 后续向 sourceBuffer.appendBuffer() 添加片段数据
});</p>
⚠️ 注意:sourceBuffer.mode 应设为 'sequence'(按时间顺序追加)或 'segments'(需手动指定 appendWindowStart/appendWindowEnd),后者更适合精确控制循环区间。
实现“片段内循环”的播放控制逻辑
MediaSource 只管数据供给,循环行为由 JS 主动管理。典型做法是:
- 设定循环起始时间
loopStart = 10(秒)、结束时间loopEnd = 15(秒) - 监听
timeupdate,当video.currentTime >= loopEnd时,立即执行video.currentTime = loopStart - 为避免因 seek 导致卡顿或黑屏,需确保目标时间点已有对应视频数据加载进 SourceBuffer —— 即提前 append 对应时间段的 fMP4 片段
- 可配合
video.readyState和sourceBuffer.updating避免在更新中 seek,例如:
video.addEventListener('timeupdate', () => {
if (video.currentTime >= loopEnd && !sourceBuffer.updating) {
video.currentTime = loopStart;
}
});
优化体验:预加载 + 无缝衔接
单纯反复 append 同一片段可能导致解码中断。更优方式是:
- 一次性 append 循环片段 2–3 遍(时间上拼接),形成连续 buffer(如 10–15s, 15–20s, 20–25s),再通过
timeupdate在逻辑上“软循环” - 利用
sourceBuffer.changeType()动态切换编码参数(如不同码率),配合 MSE 的abort()和remove()清理过期 buffer - 监听
sourcebuffer.updateend后再允许 seek,保证数据就绪 - 对移动端,需在用户手势后调用
video.play()(iOS Safari 强制要求)
不复杂但容易忽略:循环精度受视频 GOP 结构影响,若 loopStart 不在关键帧(I-frame)位置,seek 可能延迟半秒以上。务必用工具检查并调整切点对齐 IDR 帧。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











