larkplayer 是一款轻量级 & 易扩展的 html5 播放器,是为解决一些中小型的视频业务场景。这些业务不必定须要大而全的解决方案,而且他们每每有本身的定制化需求。javascript
背景
为何要编写 larkplayer?(注意,这里面有一些个人我的观点)html
目前 html5 web player 社区已经比较成熟,videojs 和 jwplayer 等都是优秀的解决方案。然而,社区的两极分化也比较严重:html5
- 排名靠前的播放器基本都是一种『大而全』的状态:功能丰富&定制化,体积动辄几百K。对于一些相对简单的业务,仍是有点过重了,尤为是在移动端,在作扩展时也可能因为一些已有的功能而碍手碍脚
- 一些小一点的播放器,每每只是解决了本身特定的业务场景的问题,缺少扩展性
- 一些播放器存在对某些大型库(好比
jQuery
、AngularJS
等)的依赖,通用性与可移植性不是很好
所以,对于一些中小型的视频业务场景,其实并无一个舒服的播放器方案可供选择。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 及其插件均支持以 script
、npm
以及各类模块化
的方式引用,你能够怎么舒服怎么来。
基本使用
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)
(跳转到某一时刻) 等 - 事件上,能够监听
play
、pause
、end
、error
、timeupdate
、loadstart
、fullscreen
等
更多的功能和事件能够查看 API。
使用插件
另外有一些经常使用但可能不是必须的功能,好比自定义样式、m3u8 文件播放、断点续播等,咱们已经提供了一些插件:
- larkplayer-ui 提供了一套适应 PC 与 WAP 的皮肤
- larkplayer-hls 提供播放 m3u8 文件的功能
- larkplayer-vr 提供全景视频播放功能
- larkplayer-auto-resume 提供自动续播功能
- larkplayer-play-muted 提供静音播放时的 UI
插件的使用也十分简单,只需在 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 端的样式,在代码中最终就像这样:
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 吧,谢谢 :)