MediaSource 是 HTML5 中的一个实验性特性,用于给 `HTMLMediaElement` 对象提供数据源。这里的数据源通常是使用 `XMLHttpRequest` 获得的数据。在 XHR 取得数据后,我们可以使用 JavaScript 对数据进行一些操作,比如提取 ID3、修改封装类型等等。 某 bilibili 开源的 Flv.js 就是使用这个特性实现的在 HTML5 环境下播放 H264 + AAC 格式的 FLV 视频。

最近在写 `Nano Player` 的时候遇到了两个比较尴尬的问题:1、ID3 信息必须手动指定;2、网易云音乐上下载的 MP3 由于某些原因必须缓冲 2MB 才能播放(详见MP3缓冲2MB才开始播放的解决方法)。于是想到是不是可以用这个 MediaSource 来暴改 MP3 呢?我也不知道

不管怎样,要用这个 MSE 来解决问题,首先就是要用它来播放文件(如果获得的数据直接喂给 MSE 都不能播放那还讨论个什么鬼)

创建若干变量并初始化:


let mediaSource = new MediaSource();
let audio = new Audio();
let media = 'audio.mp3';

audio.src = URL.createObjectURL(mediaSource);
audio.controls = 'controls';

然后是对 `sourceopen` 事件添加监听。当 `sourceopen` 事件触发后,我们需要创建一个 `sourceBuffer`,随后使用 `XMLHttpRequest` 获得媒体文件的内容,并附加到这个 `sourceBuffer` 内。当以上工作完成后,调用 `play` 方法播放音频:


mediaSource.addEventListener('sourceopen', () => {
  let sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');

  let xhr = new XMLHttpRequest();
  xhr.open('GET', media);
  xhr.responseType = 'arraybuffer';
  xhr.onreadystatechange = () => {
    if (xhr.readyState === xhr.DONE && xhr.status === 200) {
      sourceBuffer.addEventListener('updateend', () => {
        mediaSource.endOfStream();
        audio.play();
      });
      sourceBuffer.appendBuffer(xhr.response);
    }
  };
  xhr.send();
});

最后,为了方便检查,将 `audio` 添加到 `body` 中:


document.querySelector('body').appendChild(audio);

嗯,以上的这些代码就能实现一个本来只要三行代码就完成的工作了。具体效果可以看这里:MSE Playground 01

下面需要解决的问题是,为什么刚开始尝试使用的 M4A 格式无法正常播放,这个坑死人了我哪知道只能 MP3 啊浪费了两个多小时……


由于目前来看,MediaSource 的表现在各个浏览器中并不一致,同时支持的格式就目前而言还是十分有限,这个系列估计就这一篇了吧 23333(逃

参考链接:

  1. Media Source Extensions
  2. MediaSource - Web APIs | MDN
  3. Media Source Extensions for Audio
  4. HTML5 Media - types and codecs examples + canPlayType(type) test
  5. html5 video tag codecs attribute
  6. nickdesaulniers/netfix: Let's build a Netflix