Playing with MediaSource (01)

Notice

This is an old article and only available in Chinese. If you need a translation, please leave a comment and I will do my best to provide it as soon as possible.

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 都不能播放那还讨论个什么鬼)

创建若干变量并初始化:

1
2
3
4
5
6
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 方法播放音频:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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 中:

1
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
comments powered by Disqus
Except where otherwise noted, content on this blog is licensed under CC-BY 2.0.
Built with Hugo
Theme Stack designed by Jimmy