短视频业务兴起,web player 不可或缺,市面上已有的播放器太过"成熟",一个 SDK 中包含了大而全的功能,动辄几百 KB。
在业务相对简单时,可能还需要费劲去剔除部分功能;在开发业务定制功能时,没有一个规范而舒适的方式来开发与组织,随着业务扩展代码难以管理。
因此设计这样一个简单并且易扩展的播放器,用以支持业务从简单到复杂的演变。
- MSE: Media Source Extension, API allowing media data to be accessed from HTML video and audio elements
- 简单,对于简单的场景提供简单的解决方案,理解代码洁癖患者,同时避免不必要的维护成本
- 易扩展,随着业务发展渐进增强
- HTML5 技术选型,接口、属性、行为交互等方面参考 W3C WHATWG 标准
- 对内采用 DOM Event 作为通信机制
- 对外采用插件机制实现依赖反转,在播放器扩展时尽可能减少各功能间的耦合
如下图所示,播放器主要由以下几个模块组成:
- Html5 处理 html5 video 兼容性问题,基于 video 已有的方法,提供自定义 api
- Event 事件机制,代理原生事件并提供自定义事件的能力
- Plugin 插件机制,为不同类型的插件提供接口及运行机制
- Helper 辅助模块
下面详细再介绍下 Event 和 Plugin 模块
由于大量的事件都是通过与用户交互触发,因此直接使用 DOM 的 Event 机制即可
- 使用 addEventListener、removeEventListener 完成事件绑定与注销
- 使用 CustomEvent 实现自定义事件功能
- 使用 dispatchEvent 实现手动触发事件功能
插件分为 3 类:
- UI 插件,样式相关
- MediaSource 插件,基于 MSE 提供更多视频格式支持
- 其他插件
特点
- 与 DOM 交互
- 与播放器交互
- 组件化
- 逻辑与视图的结合
- 组件间组合 * 支持 jsx 语法,写起来方便点
实现
提供 Component 类:
- 构造函数
- 接收 player 和 props 参数
- 提供 this.player 与播放器交互
- 提供 this.options 获取组件参数
- 提供 this.el 指向组件 DOM 元素
- 组件的逻辑写在构造函数里(别说了,没有其他生命周期)
- 方法
- createEl: 返回组件 DOM 元素(提供 jsx 语法支持)
- dispose: 销毁组件
- static createElement: 用于实现组件间组合,不过通过 js 手写实在是不方便,已经通过 jsx 语法糖掩盖(突然感觉有点像巧克力味的屎)
- static register 绑定组件
- 其他
- larkplayer 有一些工具方法提供,编写插件时会方便点
- DOM 提供一些对 DOM 的便捷操作
- Events 提供事件相关方法
- larkplayer 有一些工具方法提供,编写插件时会方便点
如何编写插件
- 继承 Component 类并实现 createEl dispose 方法,在构造函数中书写组件逻辑
- 通过 Component.register(class, options) 绑定插件(插件名默认为首字母小写的类名,options.name 可手动指定插件名)
- 播放器初始化时通过 options.UI.name 传递参数
- 播放器初始化完成后通过 player.UI.name 访问插件实例
详见 《UI 插件编写示例》
初始化时机
播放器构造函数中初始化,所有绑定的组件都会被渲染并添加到播放器 DOM 容器中
注意事项
- 在组件构造函数中,如果要选择 this.el 的子元素,需要将上下文设置为 this.el,因为此时元素还未被插入到 html 中
特点
影响播放器对视频资源的解析及播放
实现
提供 MediaSourceHandler 类
- 构造函数
- 接收 player 和 options 参数
- 提供 this.player 与播放器交互
- 提供 this.options 接收参数
- 方法
- src: 处理视频资源
- play: 播放视频
- dispose: 销毁插件
- static canPlay: 判断能否支持当前视频类型
- static select: 筛选能支持当前视频类型的插件
- static register: 绑定插件
如何编写插件
- 继承 MediaSourceHandler 类并实现 src play dispose 方法,以及 canPlay 静态方法
- 通过 MediaSourceHandler.register(class, options) 绑定(插件名默认为首字母小写的类名,options.name 可手动指定插件名)
- 播放器初始化时通过 options.MS.name传递参数
初始化及调用时机
- 播放器初始化时,如果存在视频资源,则选择合适的插件处理视频
- 视频链接改变时,选择合适的插件处理视频
- 视频播放时,如果当前有 MediaSource 插件正在运行,优先调用插件的 play 方法
特点
没想好有什么特点,姑且认为他们只是简单的调用播放器的 api,比如断点续播之类的
实现
提供 Plugin 类
- 构造函数
- 接收 player options 参数
- 提供 this.player
- 提供 this.options
- 方法
- dispose: 销毁插件
- static register: 静态方法,绑定插件
如何编写插件
- 继承 Plugin 类,并实现 dispose 方法
- 通过 Plugin.register(class, options) 绑定,options.name 可指定插件名(插件名默认为首字母小写的类名,options.name 可手动指定插件名)
- 播放器初始化时通过 options.plugin.name 传递参数
- 播放器初始化完成后通过 player.plugin.name 访问插件实例
详见 《普通插件编写》
初始化时机
播放器构造函数中初始化插件
有负责插件存取的方法即可,不予详述