larkplayer: 插件化的 HTML5 播放器

larkplayer 是一款輕量級 & 易擴展的 html5 播放器,是爲解決一些中小型的視頻業務場景。這些業務不必定須要大而全的解決方案,而且他們每每有本身的定製化需求。javascript

背景

爲何要編寫 larkplayer?(注意,這裏面有一些個人我的觀點)html

目前 html5 web player 社區已經比較成熟,videojsjwplayer 等都是優秀的解決方案。然而,社區的兩極分化也比較嚴重:html5

  • 排名靠前的播放器基本都是一種『大而全』的狀態:功能豐富&定製化,體積動輒幾百K。對於一些相對簡單的業務,仍是有點過重了,尤爲是在移動端,在作擴展時也可能因爲一些已有的功能而礙手礙腳
  • 一些小一點的播放器,每每只是解決了本身特定的業務場景的問題,缺少擴展性
  • 一些播放器存在對某些大型庫(好比 jQueryAngularJS 等)的依賴,通用性與可移植性不是很好

所以,對於一些中小型的視頻業務場景,其實並無一個舒服的播放器方案可供選擇。java

思考

如何解決上訴問題?node

業務上:git

  • 輕量,儘可能不提供"多餘"的功能,代碼體積小
  • 易擴展,能輕易地支持如廣告、皮膚、統計等各類定製化需求

代碼上:github

  • 解決兼容性問題,把瑣碎的細節留在內部,對外接口統一
  • 插件化機制,解耦各功能,提供方便擴展的接口與方式
  • 原生 javascript 編寫,減小對類庫的依賴,便於在各類環境下使用與移植

設計

larkplayer 的靈感來自 videojs,採用插件化的設計:播放器自己只是一個精巧的核心,包含一些必備的機制和 API,其他功能由插件提供。
你能夠自由選擇和編寫本身的插件,作到按需取用,漸進加強。在 ugilfy + gzip 後,larkplayer 代碼體積約 12KB.web

圖片描述

1. 內部模塊及說明

圖片描述

  • Html5 模塊負責抹平兼容性問題,對外提供統一的 API
  • Event 模塊提供事件機制,支持原生事件及自定義事件
  • Plugin 模塊用於提供插件機制,爲各種插件提供基類,約定其接口及運行方式
  • Util 中包含一些經常使用的工具方法,好比對 DOM 的一些便捷操做函數
  • Player 模塊聚合以上模塊,用以實例化播放器以及對外提供接口

2. API

3. 事件機制

事件做爲播放器內部核心的溝通機制,爲內部的狀態流轉以及後續的擴展提供底層支持。
目前自定義事件系統的實現方式主要有 2 種:npm

  • DOM Event:HTML 原生 API,可以監聽 DOM 相關事件,支持自定義事件,具備捕獲、冒泡機制,須要一些兼容性處理
  • EventEmitter:JS 實現的事件機制,核心是一套『訂閱發佈模式』,容許自定義事件,擁有更加靈活的 API,沒法監聽 DOM 相關事件,基本無兼容性問題

因爲 DOM Event 可以監聽 DOM 相關事件,同時冒泡機制對後續 UI 插件的控制有必定的幫助,所以選用其做爲自定義事件系統實現的基礎。api

經過 DOM Event 實現如下幾類主要功能:

  • 事件監聽
  • 事件註銷
  • 支持自定義事件類型
  • 支持手動觸發事件(經過 JS 觸發而不是用戶交互觸發)

DOM Event 流程可經過下圖概覽:
圖片描述

4. 插件機制

插件是一種經常使用的『依賴反轉』的方式,使得播放器沒必要依賴外部或下層組件,而是全部外部插件都依賴播放器自己。
同時各插件因爲是面向的播放器接口,插件 A 不知道 插件 B 的存在,所以能極大地下降各插件(功能)間的耦合。

如何設計插件的類型和接口便關係到後續長期的發展,通過業務經驗的總結以及對其餘解決方案的參考,總結出如下 3 類插件類型:

  • UI 插件,DOM 相關,每每是要添加某些樣式或交互,如皮膚、彈幕等
  • MSE 插件,播放技術相關,基於 Media Source Extension,可擴展播放器對其餘視頻類型的支持,如 m3u八、flv 等
  • 其餘插件,能夠看作是一種保留類型,上述兩種類型沒法知足時,落到此類型,後續某類新的插件高頻出現時,可再次抽離出新的類型

這幾類插件如何運行呢?這裏簡單介紹下,具體能夠參見設計文檔源碼

1.UI 插件

  • Component 類做爲基類,提供事件、DOM 操做工具函數支持,可獲取到播放器引用
  • 組件化的方式開發,經過構建工具與代碼配合,支持 JSX 語法
  • 可在播放器初始化時傳遞參數,以及從播放器上獲取插件實例

2.MSE 插件

  • MSEHandler 類做爲基類,提供事件支持,可獲取播放器引用,給予修改播放器 play 等方法的權限
  • 可在播放器初始化時傳遞參數,以及從播放器上獲取插件實例

3.其餘插件

  • Plugin 類做爲基類,提供事件支持,可獲取播放器引用
  • 可在播放器初始化時傳遞參數,以及從播放器上獲取插件實例

實踐

咱們已經在多個業務中使用 larkplayer,並開發了十幾個插件用於解決各類業務需求,支持了千萬級/天的視頻播放。
larkplayer 及其插件均支持以 scriptnpm 以及各類模塊化的方式引用,你能夠怎麼舒服怎麼來。

基本使用

larkplayer 使用方式十分簡單,將如下代碼粘貼到任意編輯器中,用瀏覽器打開便可運行,更詳細的使用文檔能夠參考這裏

<!DOCTYPE html>
<html>
<head>
    <title>larkplayer quick start</title>
</head>
<body>
    <div id="container"></div>
    <script type="text/javascript" src="https://unpkg.com/larkplayer@latest/dist/larkplayer.js"></script>
    <script type="text/javascript">
        // js 文件以 umd 的形式包裝,以 script 的形式引用時,larkplayer 會直接掛載在 window 上
        var width = Math.min(document.body.clientWidth, 640);
        var player = larkplayer('container', {
            width: width,
            height: width * 9 / 16,
            controls: true,
            src: 'https://baikebcs.bdimg.com/baike-other/big-buck-bunny.mp4'
        });

        // 支持全部的 html5 標準事件以及一些自定義事件
        player.on('play', function () {
            console.log('play');
        });
        player.on('ended', function () {
            console.log('播放完成!');
        });
    </script>
</body>
</html>

larkplayer 自己已經包含一些基礎而核心的功能和機制,好比

  • 功能上,支持 pause()play()requestFullscreen()exitFullscreen()currentTime(second)(跳轉到某一時刻) 等
  • 事件上,能夠監聽 playpauseenderrortimeupdateloadstartfullscreen

更多的功能和事件能夠查看 API

使用插件

另外有一些經常使用但可能不是必須的功能,好比自定義樣式、m3u8 文件播放、斷點續播等,咱們已經提供了一些插件:

插件的使用也十分簡單,只需在 larkplayer 以後引入插件便可。
下面的代碼爲播放器添加了自定義的樣式以及斷點續播功能,將其粘貼到任意編輯器,用瀏覽器打開便可運行。

<!DOCTYPE html>
<html>
<head>
    <title>larkplayer plugin exmaple</title>
</head>
<body>
    <div id="container"></div>
    <script type="text/javascript" src="https://unpkg.com/larkplayer@latest/dist/larkplayer.js"></script>
    <!-- 自定義樣式插件 https://github.com/dblate/larkplayer-ui -->
    <script src="https://unpkg.com/larkplayer-ui@latest/dist/larkplayer-ui.js"></script>
    <!-- 斷點續播插件 https://github.com/dblate/larkplayer-auto-resume -->
    <script type="text/javascript" src="https://unpkg.com/larkplayer-auto-resume@latest/dist/index.js"></script>
    <script type="text/javascript">
        var width = Math.min(document.body.clientWidth, 640);
        var player = larkplayer('container', {
            width: width,
            height: width * 9 / 16,
            controls: true,
            src: 'https://baikebcs.bdimg.com/baike-other/big-buck-bunny.mp4'
        });
    </script>
</body>
</html>

larkplayer-ui 插件 可以自適應 PC 與 WAP 展示如下兩種樣式:

WAP 端樣式
圖片描述

PC 端樣式
圖片描述

值得一提的是,larkplayer-ui 是一種典型的 UI 類插件,這類插件支持 JSX 語法,書寫起來很是方便。好比 WAP 端的樣式,在代碼中最終就像這樣:

controls-mobile.js

import classnames from 'classnames';
import {Component, util} from 'larkplayer';
import ControlBar from './control-bar';
import ProgressBarSimple from './progress-bar-simple';
import Loading from '../component/loading';
import PlayButton from '../component/play-button';
import NotSupport from '../component/not-support';
import Error from '../component/error';

export default class ControlsMobile extends Component {
    createEl() {
        return (
            <div className={classnames(
                'lark-custom-controls',
                'lark-custom-controls--mobile',
                this.options.className)}
            >
                <ControlBar />
                <PlayButton />
                <Loading />
                <Error />
                <ProgressBarSimple />
                <NotSupport />
            </div>
        );
    }
}

若是你有興趣,也能夠本身查看源碼

管理插件

larkplayer 的這種設計,使得他可能存在大量的插件,每次調用播放器後面都跟着大量的插件引用會致使重複的代碼。這裏給出一種解決方案:

1.新建 common/player.js 文件,將 larkplayer 和公用的插件封裝在裏面,業務上調用 common/player.js 便可

/**
 * @file 視頻播放器,包含 larkplayer 及全部公用插件
 */

import larkplayer from 'larkplayer';
import 'larkplayer-ui';
import 'larkplayer-hls';
import 'larkplaer-auto-resume';
...

export default larkplayer;

2.對於只在特定場景使用的插件,因爲引用次數較少,在對應的場景引用便可

/**
 * @file VR 視頻播放
 */

import player from 'common/player.js';
import 'larkplayer-vr';

const myPlayer = player('video-el');
...

編寫插件

如下是 3 類插件的編寫示例:

其餘

測試

採用 karma + jasmine 完成單測編碼編寫&運行
BrowserStack 提供真機環境用於測試迴歸

文檔

  • 一些示例、思想說明的文檔手動編寫
  • API 一類的文檔由 nodejs 插件 jsdoc 從代碼註釋生成

構建

採用 grunt 構建,完成模塊化、打包、壓縮、代碼轉換等工做。

總覽

如下是目前整個項目的結構組成
圖片描述

最後,若是你已經看到了這裏,不妨到 github 上點個 star 吧,謝謝 :)

相關文章
相關標籤/搜索