【videojs 系列03】 Tech 扩展,播放 hls 的方案选择

3,282 阅读2分钟

Tech 扩展

Tech 是 videojs 中处理视频播放的技术,在【videojs 系列02】videojs 中四种扩展性设计 中我们知道,videojs 核心源码中提供了 Html5 作为默认 Tech,用于播放浏览器默认支持的视频类型。同时支持扩展 Tech,用于支持浏览器无法直接播放的视频类型,例如 flv、hls、dash 等。

在 videojs 源码中,定义了 Tech 基类和 Tech.registerTech 静态方法。所有的扩展 Tech 都直接或间接地继承自 Tech 并使用 Tech.registerTech 注册到全局。

videojs 官方提供了许多扩展 Tech,例如 videojs-youtubevideojs-vimeo

registerSourceHandler

除了实现 Tech 的派生类并 registerTech 之外,还有另一种方式扩展 Tech,即 registerSourceHandler

以 videojs 官方提供的 videojs-dash 为例,关键代码如下,完整代码见 videojs-dash.js

定义一个 SourceHandler 并通过 registerSourceHandler 注册到 Html5 Tech 中,使得 Html5 Tech 可以支持更多视频类型。

videojs.DashSourceHandler = function() {
  return {
    canHandleSource(source) {
      const dashExtRE = /\.mpd/i;

      if (!canHandleKeySystems(source)) {
        return '';
      }

      if (videojs.DashSourceHandler.canPlayType(source.type)) {
        return 'probably';
      } else if (dashExtRE.test(source.src)) {
        return 'maybe';
      }
      return '';

    },

    handleSource(source, tech, options) {
      return new Html5DashJS(source, tech, options);
    },

    canPlayType(type) {
      return videojs.DashSourceHandler.canPlayType(type);
    }
  };
};

videojs.DashSourceHandler.canPlayType = function(type) {
  const dashTypeRE = /^application\/dash\+xml/i;

  if (dashTypeRE.test(type)) {
    return 'probably';
  }

  return '';
};

// Only add the SourceHandler if the browser supports MediaSourceExtensions
if (window.MediaSource) {
  videojs.getTech('Html5').registerSourceHandler(videojs.DashSourceHandler(), 0);
}

选择什么方案来播放 hls 直播流

关于 Hls 的支持,videojs 官方目前提供的最新支持是 videojs-http-streaming,简称 VHS,替代过去的 videojs-contrib-hls

Github 上也有一些第三方实现,基于 hls.js 的扩展,例如基于 Tech.registerTech 实现的 videojs-hlsjs,和基于 registerSourceHandler 实现的 videojs-hlsjs-plugin

对比这两个第三方库,可以发现使用 Tech.registerTech 会比使用 registerSourceHandler 代码更简单。

videojs 较新的版本已经默认内置了 VHS,只有 videojs.core.js 才是剔除了 VHS 的核心代码。但是对比 VHS 和 hls.js 源码之后,我们还是决定使用 hls.js。

因为 hls.js 代码结构更好,与 flv.js 结构类似,更容易理解。而且 hls.js 更加纯粹,没有和 videojs 绑定,可以单独使用或结合其他的播放器 UI 使用。

所以,最终方案是基于 hls.js 通过 Tech.registerTech 来实现扩展,但是 videojs-hlsjs 已经是 4 年前的代码,比较落后。鉴于继承 Tech 自己重写也比较简单,这里就自己写了一个 Hls Tech。

自己实现的 hls tech

基于 hls.js 实现 Tech 的简单代码如下,注意此处使用了 script 标签加载 hls.min.js,所以是从 window 中获取 Hls 对象。

import videojs from 'video.js';
const Html5 = videojs.getTech('Html5');
const Hls = window['Hls'];


class Hlsjs extends Html5 {
  hls_;
  static canPlaySource: (techId: any, source: any) => any;
  static isSupported: () => any;

  setSrc(src) {
    if (this.hls_) {
      this.hls_.stopLoad();
      this.hls_.detachMedia();
      this.hls_ = null;
    }

    this.hls_ = new Hls();
    this.hls_.attachMedia(this.el_);
    this.hls_.loadSource(src);
  }

  /**
   * @override {HTML5.currentSrc}
   */
  currentSrc() {
    if (this.hls_) {
      return this.hls_.url;
    }
    return this.el_.currentSrc;
  }

  /**
   * @override {Html5.dispose}
   */
  dispose() {
    if (this.hls_) {
      this.hls_.detachMedia();
      this.hls_.destroy();
    }
    super.dispose();
  }
}

Hlsjs.isSupported = function () {
  return Hls && Hls.isSupported();
};

Hlsjs.canPlaySource = function (source, options) {
  if(source.type === 'application/x-mpegURL'){
    return 'maybe';
  }

  return '';
};

videojs.registerTech('Hlsjs', Hlsjs);
export default Hlsjs;

注册了 Hlsjs 之后,在 videojs 的初始化参数中,将 techOrder 由 ['html5', 'flvjs'] 改为 ['html5', 'hlsjs', 'flvjs'] 即可。

video.js 和 hls.js 都用 script 标签在顶部引入,video.js 使用 video.core.min.js 避免引入 VHS。