美文网首页让前端飞
H5直播 —— 从入门到放弃(准备篇)

H5直播 —— 从入门到放弃(准备篇)

作者: 闪闪发光的狼 | 来源:发表于2019-01-31 14:35 被阅读5次

    历史问题

    前些年,WEB的视频直播都是由flash或者插件实现。但是插件的权限太大,浏览器不放心,于是把插件给咔擦了,剩下一个flash躲在角落里瑟瑟发抖。目前,flash也已经沦落到需要用户手动启用才能播放的地步。这么一来,用户体验肯定不行,客户该抱怨抱怨,该投诉投诉。
    那么,H5直播成了未来的唯一一条路了。从此,WEB开发人员不得不扛上视频直播的担子。(呸,什么都要WEB开发人员做)

    MSE

    video元素让我们能够播放完整的视频文件,MSE(Media Source Extension)则提供了底层控制视频流的方法。它提供了一个音视频播放轨道,可以由JS不停地向轨道添加实时的视频段,从而达到直播的效果。

    MediaSource.isTypeSupported()
    检测 MS 是否支持某个特定的编码和容器盒子
    MediaSource.addSourceBuffer()
    创建一个带有给定MIME类型的新的 SourceBuffer并添加到 MediaSource 的SourceBuffer 列表。它返回的sourceBuffer很重要,之后的视频信息都是往它身上扔。
    MediaSource.removeSourceBuffer()
    删除sourceBuffer。播放结束后就可以把它删了。
    SourceBuffer.appendBuffer()
    不停地往sourceBuffer上扔视频片段,直播就能达成了。基本上直播的核心就是这句话,剩下的就是appendBuffer的参数对不对的问题。如何生成这个参数才是H5直播的难点
    附上MDN上的例子,对着我的中文注释,应该很好理解

    var video = document.querySelector('video');
    
    var assetURL = 'frag_bunny.mp4'; //记住这个文件名,待会要考
    var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
    
    if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {  //判断MS是否支持该文件类型
      var mediaSource = new MediaSource;
      //console.log(mediaSource.readyState); // closed
      video.src = URL.createObjectURL(mediaSource); //video和MS建立连接
      mediaSource.addEventListener('sourceopen', sourceOpen);  //只有MS的状态变为open后才能对它进行处理
    } else {
      console.error('Unsupported MIME type or codec: ', mimeCodec);
    }
    
    function sourceOpen (_) {
      //console.log(this.readyState); // open
      var mediaSource = this;
      var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);  //创建sourceBuffer
      fetchAB(assetURL, function (buf) {
        sourceBuffer.addEventListener('updateend', function (_) {
          mediaSource.endOfStream(); 
          video.play();
          //console.log(mediaSource.readyState); // ended
        });
        sourceBuffer.appendBuffer(buf);  //核心。塞视频数据了。buf是获取到文件的二进制数据
      });
    }
    
    function fetchAB (url, cb) {  //获取文件二进制数据的
      console.log(url);
      var xhr = new XMLHttpRequest;
      xhr.open('get', url);
      xhr.responseType = 'arraybuffer';
      xhr.onload = function () {
        cb(xhr.response);
      };
      xhr.send();
    }
    

    有一天,你兴冲冲地找了个MP4文件,想试试上面那套代码,结果,无法播放视频。这是为什么?因为普通的MP4文件是不支持MSE播放的,只有Fragement MP4(FMP4)才行。回过头看看,例子的文件名 frag_bunny。frag可以大致看出这个例子用的就是FMP4文件。

    ArrayBuffer

    目前的浏览器提供了ArrayBuffer来处理二进制数据,它表示通用的、固定长度的原始二进制数据缓冲区。ArrayBuffer 不能直接操作,而是要通过TypedArray对象或DataView对象来操作。它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。

    var buf = new ArrayBuffer(8);
    console.log(buf.byteLength)  //8
    

    TypedArray有如下几种类型:
    Int8Array(); 8位有符号整数
    Uint8Array(); 8位无符号整数
    Uint8ClampedArray(); 8位无符号整型固定数组
    Int16Array(); 16位有符号整数
    Uint16Array(); 16位无符号整数
    Int32Array(); 32位有符号整数
    Uint32Array(); 32位无符号整数
    Float32Array(); 32位浮点数
    Float64Array(); 64位浮点数

    var buffer = new ArrayBuffer(8);
    var view8 = new Int8Array(buffer);
    var view16 = new Int16Array(buffer);
    var view32   = new Int32Array(buffer);
    console.log(view8.byteLength,view16.byteLength,view32.byteLength);  // 8 8 8
    console.log(view8.length,view16.length,view32.length)  //  8 4 2
    

    byteLength表示字节数,因为view8,view16,view32都是来自同一份内存,所以字节数都为8。length表示数组的长度,Int16Array 一个元素占2个字节,所以数组长度为4。
    TypedArray.prototype.subarray()
    返回一个新的、基于相同 ArrayBuffer、元素类型也相同的的 TypedArray。开始的索引将会被包括,而结束的索引将不会被包括。

    const uint8 = new Uint8Array([10, 20, 30, 40, 50]);
    console.log(uint8.subarray(1, 3));
    // Uint8Array  [20, 30]
    
    console.log(uint8.subarray(1));
    // Uint8Array [20, 30, 40, 50]
    

    TypedArray.prototype.set(Array,offset)
    set() 方法用于从指定数组中读取值,并将其存储在类型化数组中。Array表示源数据,offset表示起始偏移量。

    var buffer = new ArrayBuffer(8);
    var uint8 = new Uint8Array(buffer);
    uint8.set([1,2,3], 3);
    console.log(uint8); // Uint8Array [ 0, 0, 0, 1, 2, 3, 0, 0 ]
    

    Video

    视频播放离不开video元素。
    [element].currentTime
    当前播放秒数。可以在代码层面上拖动视频进度条。
    [element].duration
    视频时长。直播的过程中duration在不停增大,因为video正在一直接收视频数据。网络稳定的情况下,duration-currentTime可以粗略的计算出视频延时。
    理论上来说,currentTime设定的和duration差距越小,延时就越小。
    其他的事件和基础方法请自行查阅。

    到此,准备工作已就绪。
    下篇讲解 如何把appendBuffer函数调用的参数凑出来。

    相关文章

      网友评论

        本文标题:H5直播 —— 从入门到放弃(准备篇)

        本文链接:https://www.haomeiwen.com/subject/bvdroftx.html