在HTML5中最简单的音视频播放方式是直接将资源链接传入video标签的src属性。这种加载方式也被称为流式加载,即并不需要等待资源完全下载好后才开始音视频的播放,而是一边加载数据一边进行播放。通过观察控制台可以发现,在加载一个大体积视频的时候,通常会发送多个请求来分段请求该资源,每个请求都会带着Range: byteds=<start>-的请求头,响应的状态码是206,响应头包括Content-Length: <length>; Content-Range: byted <start>-<end>/<length>。
上述方式是直接将加载后的视频数据交给video自动进行解封装、解码、播放,这种方式虽然实现简单但也限制了我们扩展其功能的能力,实际上目前主流的视频播放器都不是使用这种原生方式播放视频的,相反它们都是借助媒体扩展(Media Source Extension) 来实现各种定制化的需求,比如可以实现以下特性:
- HTML5本身不支持FLV、HLS(m3u8)容器格式,但我们可以先通过fetch或ajax手动请求资源数据,再将其转化为分段式MP4格式(FMP4),再通过MediaSource进行播放,从而实现对多种容器格式的兼容。
- 请求视频资源时可以通过持续发送请求的形式来进行分段请求(如可以通过Range头部实现),并依次通过appendBuffer来实现流式加载,从而可以实现分辨率的动态切换、视频源的切换、直播功能。(以前想要实现分辨率的切换本质是修改video的src属性,重新加载新的视频源时可能会卡一下)
在介绍具体的实现之前,我们可以先观察一下主流的视频网站(Youtube、B站等)的播放器的行为。首先能够观察到它们的video标签的src并不是指向着一个真实存在的URL地址,而是一个类似blob:https://www.bilibili.com/ed89dd41-bff1-427e-80c4-fa796d34c3cb的虚拟URL;另外观察网络栏可以发现,在视频的播放过程中会持续发送请求来加载数据,一般来说每次请求的数据大小为几百KB。
接下来将会简单介绍MediaSource的API以及使用方式。
<body>
<video id="video"></video>
<script>
const video = document.getElementById("video");
const mediaSource = new MediaSource();
mediaSource.addEventListener("sourceopen", function () {
const mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
fetch("http://server.com/my-video.mp4")
.then((res) => res.arrayBuffer())
.then((buf) => {
sourceBuffer.addEventListener("updateend", function () {
mediaSource.endOfStream();
video.play();
});
sourceBuffer.appendBuffer(buf);
});
});
const url = URL.createObjectURL(mediaSource);
video.src = url;
</script>
</body>
首先通过new MediaSource()创建mediaSource实例,通过URL.createObjectURL创建虚拟地址,然后把虚拟地址传给video,这会触发mediaSource的sourceopen事件,此时我们可以创建一个或多个sourceBuffer,然后发送请求并把获取到的媒体资源通过appendBuffer进行加载。
在这个例子中我们只发送了一个请求来获取完整的多媒体数据,在实际的应用中会使用多个请求来获取分段数据。
参考: