MediaSource

  • 本质MediaSource是一种可用于创建媒体流的对象,不过它的数据来源是JavaScript动态生成的,并非实时采集的。

  • 核心用途:主要用于实现流媒体播放,像视频点播、分段加载视频等场景都会用到它。

  • 关键特性

    • 支持非实时的、可控的媒体播放。

    • 可以把媒体数据分成多个SourceBuffer,从而实现分段加载和播放。

    • 能够进行随机访问,用户可以在视频的不同片段之间自由跳转。

视频处理

官方文档中,MediaSource在创建addSourceBuffer时,需要传入MIME参数,他描述了视频的具体格式,且视频必须经历碎片化处理后才能被使用,否则会触发报错。

碎片化处理

我们通过Bento4来处理视频,使用Bento4的mp4fragment工具,将视频文件碎片化。命令如下:

mp4fragment .\video\3.mp4 .\video\3-1.mp4

MEIE类型设置

addSourceBuffer所需要的MEIE类型不只是一个宽泛的类型,而是一个精确的类型,例如:video/mp4; codecs="avc1.640028, mp4a.40.2" ,他描述了视频的格式、视频编码、音频编码。

我们使用小工具来获取视频的详细编码格式:

is fragmented: true字段标识该视频支持碎片化。

MediaSource.isTypeSupported('video/mp4; codecs="avc1.64001e, mp4a.40.2"'); // => true表示视频的格式是video/mp4; codecs="avc1.64001e, mp4a.40.2“,且该格式能被当前浏览器支持。

满足这两条要求的视频才能被使用。

使用示例

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
</head>

<body>
  <video id="videoBody" muted controls></video>
  <script>
    var video = document.getElementById('videoBody');
    var assetURL = '/assets/1.mp4';
    var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
    var isEnd = false
    // 检查浏览器是否支持MediaSource
    if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
      // 创建mediaSource
      var mediaSource = new MediaSource;
      video.src = URL.createObjectURL(mediaSource);
      mediaSource.addEventListener('sourceopen', sourceOpen);
    } else {
      console.error('Unsupported MIME type or codec: ', mimeCodec);
    }

    function sourceOpen () {
      // 创建sourceBuffer
      var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
      fetchAB(assetURL, function (buf) {
        // 监听sourceBuffer的updateend事件
        sourceBuffer.addEventListener('updateend', function (_) {
          console.log('updateend');
          // 检查mediaSource是否准备好
          if (mediaSource.readyState === 'open' && !sourceBuffer.updating) {
            // appendBuffer结束
            if (isEnd) {
              mediaSource.endOfStream();
              video.play();
            }
          } else {
            console.log('not ready');
          }
        });
        // 添加视频
        sourceBuffer.appendBuffer(buf);
        // 模仿多个视频添加的情况
        setTimeout(() => {
          // 假设就是最后一个视频
          isEnd = true
          // 设置视频播放位置
          sourceBuffer.timestampOffset = 60
          // 添加视频
          sourceBuffer.appendBuffer(buf);
        }, 2000);
      });
    };
    // 获取视频
    function fetchAB (url, cb) {
      var xhr = new XMLHttpRequest;
      xhr.open('get', url);
      // 以arraybuffer形式
      xhr.responseType = 'arraybuffer';
      xhr.onload = function () {
        cb(xhr.response);
      };
      xhr.send();
    };
  </script>
</body>

</html>