| 4 min read

使用 mux.js 播放 .ts 文件

本篇大概是Media Source 系列 - 播放 m3u8 文件 的一篇补充。这里大概需要涉及一些视频的编码和解码方面的知识补充。

mux.jsvideojs 团队出品的一款轻量级前端工具,用于嗅探或者修改 video 的视频的格式:

Lightweight utilities for inspecting and manipulating video container formats.

我们播放的视频现在有很多类型,比如 mp4, flv, 3gp 等。但是我们从浏览器角度出发,目前最好的格式 还是 mp4, 但是如果我们需要播放 .ts 的切片的文件(m3u8)单元或者 .flv 文件。这个时候,实际我们通过 Video 是无法直接播放的。(.m3u8 文件 Apple 的 Safari 是可以支持的)。

但是如果我们能够认识不同的视频格式之前的差别,那么我们就可以实现前端的解码器,将不能够播放的格式转换成浏览器支持的格式。

FLV-Flash Video 是一种网络视频格式,用作流媒体格式,它的出现有效地解决了视频文件导入Flash后,使导出的SWF文件体积庞大,不能在网络上有效使用等缺点。

FLV文件=FLV头文件+ tag1+tag内容1 + tag2+tag内容2 + ...+... + tagN+tag内容N

TS-
MPEG2-TS
是一种传输和存储包含音效、视频与通信协议各种数据的标准格式,用于数字电视广播系统,如DVB、ATSC、IPTV等等。TS的解码方式是从PID为0的TS packet内,解析出PAT table,然后PAT table找到各个program源的PID。解码器根据PMT table的ES streaming的PID,将TS流上的packet进行区分,并按不同的ES流进行解码。

而其中 packet 是传输流(transport stream)内数据的最基本单位,包含了一个同步字节(sync byte)值为0x47,接着是13比特的Packet Identifier(PID). Packets有188 bytes的长度[1]。但是后面有可能会追加一些验证字节,所以packets的长度有可能变为192、204或者208Byte的长度

Transmuxer

如图:

mux.js 中的 transmuxer 主要做的事情就是讲 push 进去的字节内容进行解析然后分离出 metadata ,音频 和视频流。随后我们在进进行处理,进行合流。这个时候就会重组成一个浏览器可以播放的 mp4 格式流数据。

那么我们如果要实现 video 的播放就可以按照这样的流程

首先我们需要请求一个 ts 文件

var transmuxer = new muxjs.mp4.Transmuxer();
...
fetch('./video/avegers01.ts', {
      // set header
    })
    .then(function(response) {
      return response.arrayBuffer();
    })
    .then(function(arrayBuffer) {
      // data events signal a new fMP4 segment is ready:
      transmuxer.push(new Uint8Array(arrayBuffer));
    });

接下来我们通过 监听 data 事件获取拿到的分片文件内容:


transmuxer.on('data', function (segment) {
      remuxedSegs.push(segment);
      remuxedBytesLength += segment.data.byteLength;
      remuxedInitSegment = segment.initSegment;
     bytes = new Uint8Array(remuxedInitSegment.byteLength + remuxedBytesLength)
        bytes.set(remuxedInitSegment, offset);
        offset += remuxedInitSegment.byteLength;
        for (j = 0, i = offset; j < remuxedSegs.length; j++) {
        bytes.set(remuxedSegs[j].data, i);
        i += remuxedSegs[j].byteLength;
      }
      sourceBuffer.appendBuffer(bytes);
      video.play(); 
    });

在上面这步,之前我们还需要绑定 Video 到 MSE 上(关于如何实现 Media Source Extensions 播放) 。

var video = document.querySelector('.js-player-m3u8');
  if (window.MediaSource) {
    var mediaSource = new MediaSource();
    video.src = URL.createObjectURL(mediaSource);
    mediaSource.addEventListener('sourceopen', sourceOpen, { once: true });
  }

我们只需要在定义 sourceOpen 函数中触发最早实现的 fetch 就可以播放了。

mux.js 的实现实际上是对视频的 Buffer 数据进行改写,如果有兴趣可以去阅读它的源码,它提供了 flv/mp4 的辅助工具。

扩展阅读

You Can Speak "Hi" to Me in Those Ways