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

历史问题

前些年,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直播 —— 从入门到放弃(准备篇))