作者 | 翁鹏
42
如果检查主流视频网站的视频,就会发现网站的 video
元素的 src
属性都是 blob
开头的字符串。
为什么视频链接前面会有 blob
前缀?这是因为视频网站使用了这篇文章要讲的 MSE 来播放视频。
Media Source Extensions
虽然 video
很强大,但是还有很多功能 video
并不支持,比如直播,即时切换视频清晰度,动态更新音频语言等功能。
MSE(Media Source Extensions)就来解决这些问题,它是 W3C 的一种规范,如今大部分浏览器都支持。
它使用 video
标签加 JS 来实现这些复杂的功能。它将 video
的 src
设置为 MediaSource
对象,然后通过 HTTP 请求获取数据,然后传给 MeidaSource
中的 SourceBuffer
来实现视频播放。
如何将 MediaSource
和 video
元素连接呢?这就需要用到 URL.createObjectURL
它会创建一个 DOMString
表示指定的 File
对象或 Blob
(二进制大对象) 对象。这个 URL 的生命周期和创建它的窗口中的 document
绑定。
const video = document.querySelector('video')
const mediaSource = new MediaSource()
mediaSource.addEventListener('sourceopen', ({ target }) => {
URL.revokeObjectURL(video.src)
const mime = 'video/webm; codecs="vorbis, vp8"'
const sourceBuffer = target.addSourceBuffer(mime) // target 就是 mediaSource
fetch('/static/media/flower.webm')
.then(response => response.arrayBuffer())
.then(arrayBuffer => {
sourceBuffer.addEventListener('updateend', () => {
if (!sourceBuffer.updating && target.readyState === 'open') {
target.endOfStream()
video.play()
}
})
sourceBuffer.appendBuffer(arrayBuffer)
})
})
video.src = URL.createObjectURL(mediaSource)
addSourceBuffer
方法会根据给定的 MIME 类型创建一个新的 SourceBuffer
对象,然后会将它追加到 MediaSource
的 SourceBuffers
列表中。
我们需要传入相关具体的编解码器(codecs)字符串,这里第一个是音频(vorbis),第二个是视频(vp8),两个位置也可以互换,知道了具体的编解码器浏览器就无需下载具体数据就知道当前类型是否支持,如果不支持该方法就会抛出 NotSupportedError
错误。更多关于媒体类型 MIME 编解码器可以参考 RFC 4281。
这里还在一开始就调用了 revokeObjectURL
。这并不会破坏任何对象,可以在 MediaSource
连接到 video
后随时调用。它允许浏览器在适当的时候进行垃圾回收。
视频并没有直接推送到 MediaSource
中,而是 SourceBuffer
,一个 MeidaSource
中有一个或多个 SourceBuffer
。每个都与一种内容类型关联,可能是视频、音频、视频和音频等。
视频格式
HTML5 标准指定时,想指定一种视频格式作为标准的一部分,所有浏览器都必须实现。但是对于 H.264
视频编码各个厂商产生的争论,主要是 H.264
非常强大(高画质、高压缩比、成熟的编解码器...),但是它也要高昂的授权费。Mozilla 这类免费浏览器,并没有从其开发的浏览器上获得直接收入,但是让 H.264
加入标准,它就要支付相应的授权费,所有认为是不可接受的。于是后来放弃了视频格式指定的统一,浏览器厂商可以自由选择支持的格式。
不过现在所有主流浏览器都支持 H.264
编码格式的视频,所有选择视频编码时优先选择 H.264
编码。
MediaSource API
MediaSource
属性
属性 | 描述 |
---|---|
sourceBuffers | 返回包含 MediaSource 所有 SourceBuffer 的 SourceBufferList 对象 |
activeSourceBuffers | 上个属性的子集,返回当前选择的视频轨道,启用的音频规定和显示或隐藏的文本轨道 |
duration | 获取或者设置当前媒体展示的时长,负数或 NaN 时抛出 InvalidAccessError ,readyState 不为 open 或 SourceBuffer.updating 属性为 true 时抛出 InvalidStateError |
readyState | 表示 MediaSource 的当前状态 |
readyState
有以下值:
closed
未附着到一个media
元素上open
已附着到一个media
元素并准备好接收SourceBuffer
对象ended
已附着到一个media
元素,但流已被MediaSource.endOfStream()
结束
MediaSource
方法
方法 | 描述 |
---|---|
addSourceBuffer(mime) | 根据给定 MIME 类型创建一个新的 SourceBuffer 对象 |