引言
随着在线视频平台的普及,在线视频成为了用户日常生活中不可或缺的一部分,如何提升用户观看体验,尤其是在视频加载时间和播放流畅性方面,成为了开发者关注的焦点。视频预加载技术,作为提升视频加载效率和用户体验的重要手段,逐渐成为现代网页视频播放的一项至关重要的优化技术。
1.什么是视频预加载技术
视频预加载技术是指在用户实际播放视频之前,提前加载部分或全部视频内容到本地缓存中的技术。通过预加载,可以减少视频播放时的缓冲时间,避免因网络延迟或带宽不足导致的卡顿现象,从而提升用户的观看体验。
传统的视频加载方式通常会在页面加载时将视频文件整体下载到本地,直至开始播放。此方法虽然简便,但在带宽不稳定的情况下容易出现卡顿和延迟。本文将探讨两种主流的视频预加载技术:HTML5 video 标签的 preload 属性与基于媒体源扩展(Media Source Extensions,简称 MSE)技术的分片请求方法。通过对这两种技术的对比分析,探讨它们在提升用户体验中的作用和适用场景。
2.HTML5 video 标签的 preload 属性
2.1preload 属性的取值
preload 属性有以下几种取值:
- auto
- 行为:浏览器会尽量预加载整个视频文件(或至少一部分)。通常浏览器会在页面加载时下载视频的一部分数据,以便用户点击播放时能更快速地开始。
场景:适合希望视频可以尽快开始播放的情况。例如,用户在观看的视频可能是页面的重点内容,提前加载会改善用户体验。
示例: - metadata
- 行为:只会预加载视频的元数据(如视频的时长、分辨率等),但不会预加载视频内容本身。只有当用户点击播放时,才会开始下载视频的内容。
场景:适用于希望减少页面加载时间或带宽消耗,同时仍然希望能在页面加载时获取视频的基本信息(例如,时长、分辨率)以用于展示或其他目的。 - none
- 行为:浏览器不会预加载任何视频数据,视频的下载将在用户点击播放时才开始。
场景:适用于希望完全控制视频何时加载的情况,或者当视频非常大且不希望它在页面加载时占用带宽时使用。 - 没有设置 preload 属性
- 行为:如果没有显式设置 preload 属性,浏览器的默认行为可能会有所不同,通常会根据浏览器类型和版本进行优化。对于现代浏览器(如 Chrome 和 Firefox),默认值一般是 metadata,即只预加载视频的元数据而不预加载视频内容。
2.2加载流程
浏览器开始加载 HTML 文件,并解析其中的内容,浏览器解析到 video 元素时,会根据 preload 属性决定是否预加载视频资源,load 事件触发根 preload 设置触发预加载行为。
2.3注意事项
- 带宽和性能
- 预加载视频会消耗带宽,尤其是视频文件较大时。使用 metadata 或 none 有助于减少初始加载时的带宽消耗,适合性能要求较高的页面。
- 用户体验
- auto 可以提供更好的用户体验,避免点击播放后出现等待缓冲的现象。但这也会带来较大的数据流量消耗,因此要根据实际需要权衡。
- 浏览器行为
- 不同浏览器可能对 preload 的默认行为有不同的实现,特别是在没有显式设置时。为了确保一致性,最好明确设置 preload 属性
3.基于 MSE 实现 web 前端视频预加载
MSE(Media Source Extensions)API 是一个用于动态地将媒体流(如视频或音频)加载到 HTML video 元素中的浏览器 API,该对象能够将媒体片段(如视频文件)动态地拼接在一起并播放。在浏览器端实现逐块加载流媒体,它为 HTML5 video 元素提供了更大的控制,特别是在实时视频流、视频点播、直播等场景中。
3.1实现原理
视频预加载的基本思路是:当用户请求播放视频时,先通过后台请求获取视频的部分数据(例如前几秒的内容),将这些数据通过 MSE API 加载到视频元素中,然后尽早开始播放,确保视频流畅地呈现给用户。
3.2执行流程
- 编写加载器 loader,请求 mp4 视频数据。
- 编写解析器将 mp4 视频数据进行流处理等 。
- 将解复用的视频数据转成 fmp4 格式并传递给 MediaSource。
- 通过 createObjectURL 将 MediaSource 与 video 进行关联,完成播放。
3.3实现步骤
- 创建视频播放元素
需要创建一个 video 元素,并将其与 MSE API 配合使用。
- 初始化 MSE API
- 接下来,使用 JavaScript 初始化 MSE API,创建一个 MediaSource 对象,并通过 SourceBuffer 来添加视频数据。
- const videoElement = document.getElementById('videoElement'); const mediaSource = new MediaSource(); videoElement.src = URL.createObjectURL(mediaSource);
- 处理视频数据
- mediaSource.addEventListener('sourceopen', () => { const mime = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'; const sourceBuffer = mediaSource.addSourceBuffer(mime); const data = new ArrayBuffer([...]); sourceBuffer.appendBuffer(data); });
3.4mp4 视频解析与播放的关键功能
3.4.1获取和解析 MOOV 元数据
MOOV 元数据包含了视频的所有关键信息,如视频轨道(trak)、音频轨道、编码格式、时长、时间尺度等。请求数据块后,代码会尝试解析视频的 MOOV 元数据,获取视频和音频的具体细节,包括:
- 视频和音频的编码格式(如 avcC, hvcC 或 mp4a)
- 分辨率(width 和 height)
- 时间尺度(videoTimeScale 和 audioTimeScale)
- 音频的通道数、采样率等。
3.4.2数据加载和视频分片处理
为了保证流畅的播放,尤其是在处理大文件时,视频数据通常会被分成多个小块(chunk)进行加载。在请求数据时,会根据设置的 start 和 end 字节区间请求视频数据。数据块加载后,把数据处理为二进制数据,供后续处理和播放使用。
3.4.3视频关键帧解析
视频的关键帧(KeyFrame)是视频的基本帧,用于快速定位视频的播放位置。视频的关键帧索引数据存储在 stss(同步帧索引)箱体中。代码解析 stss 数据并获取关键帧的具体位置。获取每个关键帧的相关信息后(包括时间、大小、偏移量等)。还会处理 ctts(Composition Time to Sample)和 stsc(Chunk to Sample)数据,以帮助准确定位视频的关键帧。
3.4.4音频关键帧解析
音频的关键帧解析与视频相似。首先根据视频的关键帧来同步音频的帧索引。然后通过音频的 stts 和 stsc 数据来提取音频的关键帧。
3.4.5片段加载 (loadFragment)
视频数据通常以片段(Fragment)为单位进行加载和播放。每个片段包含了一定时间范围内的音视频数据。根据给定的 fragIndex 加载对应的字节范围,并获取数据块。加载完成后,处理相应数据生成一个完整的 MP4 片段,包括音频和视频的缓冲数据。
3.4.6音视频缓冲处理
音频和视频的样本数据被提取并封装成一个 MP4 片段。每个样本包含了视频/音频的大小、时长、偏移量以及缓冲数据等。通过将视频和音频缓冲区拼接成一个完整的 MP4 片段,最终返回给播放器进行播放。
3.5产品中使用该技术优化体验
3.5.1秒开视频(减少首帧加载时间)
提前解析 MOOV,提升首屏加载速度,减少白屏时间:
播放器在加载视频时,可以先解析 MOOV 盒,快速获取视频的元数据,包括帧率、时长、关键帧位置等,并提前为 MediaSource 准备数据。
const mp4Parser = new MP4({ url: 'video.mp4' });
mp4Parser.on('moovReady', () => {
console.log('MOOV 数据解析完成');
startPlayback();
});
mp4Parser.init();
3.5.2快速拖动(精准关键帧跳转)
按需加载关键帧,支持精准快进:通过解析 stss 盒,播放器可以快速定位到最近的关键帧,并直接加载相应的片段。
function seek(time) {
mp4Parser.seek(time)
.then(() => {
console.log(`已跳转到 ${time} 秒`);
});
}
document.getElementById('seekButton').addEventListener('click', () => {
seek(120);
});
3.5.3按需加载(减少无用数据下载)
仅加载播放所需片段,降低带宽占用:通过 loadFragment(fragIndex) 方法,播放器可以只加载当前播放时间段的片段,而非整个视频文件。
mp4Parser.loadFragment(0)
.then((fragment) => {
appendBuffer(fragment);
});
function appendBuffer(fragment) {
const mediaSource = new MediaSource();
videoElement.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', () => {
const sourceBuffer = mediaSource.addSourceBuffer('video/mp4');
sourceBuffer.appendBuffer(fragment);
});
}
3.6优点
- 按需加载:通过分片请求,视频的加载可以更加灵活,可以根据用户的播放进度动态加载数据,避免不必要的带宽浪费。
- 适用于大规模流媒体:对于需要实时播放或者长时间播放的视频流,MSE 技术提供了更强的支持,尤其在处理大文件或长时间视频时表现优越。
- 动态调节:可以根据当前的网络状况和带宽自动调整加载速率,优化播放过程中出现卡顿。
3.7缺点
- 实现复杂:相比 video 标签的 preload 属性,MSE 需要更多的 JavaScript 代码来控制视频流的加载,增加了开发复杂度。
- 浏览器兼容性:尽管大部分现代浏览器都支持 MSE,但老旧浏览器或一些特殊平台上的兼容性较差。
- 资源消耗较高:由于 MSE 依赖 JavaScript 处理媒体流,可能会消耗更多的 CPU 和内存资源。
4.对比分析
特性 | video 标签的 preload 属性 | MSE 分片请求 |
技术实现 | 简单,直接在 HTML 中设置 | 需要 JavaScript 代码,较为复杂 |
加载方式 | 整体加载或按元数据加载 | 动态分片加载,按需加载 |
带宽管理 | 限制较少,主要依赖浏览器行为 | 可以动态调整带宽,精确控制加载速度 |
用户体验 | 对低带宽用户友好,但无法动态调整视频加载方式 | 优化长视频流播放,避免卡顿 |
适用场景 | 短小视频,简单场景,适用于带宽较好环境 | 长时间直播或按需视频流,大规模流媒体 |
浏览器支持 | 主流浏览器支持较好 | 现代浏览器支持良好,老旧浏览器支持较差 |
5. 总结
通过对 HTML5 video 标签的 preload 属性与 MSE 分片请求的对比分析,可以看出两者在不同的应用场景下各有优势。preload 属性适用于较为简单的视频播放需求,特别是在带宽较为充裕的情况下。而 MSE 技术则在复杂的视频播放场景中表现出色,尤其是在实时流媒体、长时间视频播放和动态加载方面具有不可替代的优势。
开发者应根据具体的业务需求和用户环境选择合适的视频预加载技术,以优化视频播放体验。在未来,随着网络技术的进一步发展,预加载技术可能会更加智能化,能够根据用户的实时需求自动调整加载策略,进一步提升用户体验。