「视频直播技术详解」系列之六:现代播放器原理,
?关于直播的技术文章不少,成体系的不多。我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面、深入地了解视频直播技术,更好地技术选型。
本系列文章大纲如下:
(一)采集
(二)处理
(三)编码和封装
(四)推流和传输
(五)延迟优化
(六)现代播放器原理
(七)SDK 性能测试模型
在上一篇延迟优化中,我们分享了不少简单实用的调优技巧。本篇是《视频直播技术详解》系列之六:现代播放器原理。
近年来,多平台适配需求的增长导致了流媒体自适应码率播放的兴起,这迫使 Web 和移动开发者们必须重新思考视频技术的相关逻辑。首先,巨头们分分发布了 HLS、HDS 和 Smooth Streaming 等协议,把所有相关细节都隐藏在它们专供的 SDK 中。开发者们没法自由的修改播放器中的多媒体引擎等逻辑:你没法修改自适应码率的规则和缓存大小,甚至是你切片的长度。这些播放器可能用起来简单,但是你没有太多去定制它的选择,即便是糟糕的功能也只能忍受。
但是随着不同应用场景的增加,可定制化功能的需求越来越强。仅仅是直播和点播之间,就存在不同的 buffer 管理、ABR 策略和缓存策略等方面的差别。这些需求催生了一系列更为底层关于多媒体操作 API 的诞生:Flash 上面的 Netstream,HTML5 上的 Media Source Extensions,以及 Android 上的 Media Codec,同时业界又出现了一个基于 HTTP 的标准流格式 MPEG-DASH。这些更高级的能力为开发者提供了更好的灵活性,让他们可以构建适合自己业务需求的播放器和多媒体引擎。
今天我们来分享一下如何构建一个现代播放器,以及构建这样一个播放器需要哪些关键组件。通常来说,一个典型的播放器可以分解成三部分:UI、 多媒体引擎和解码器,如图 1 所示:
图 1. 现代播放器架构
用户界面(UI):这是播放器最上层的部分。它通过三部分不同的功能特性定义了终端用户的观看体验:皮肤(播放器的外观设计)、UI(所有可自定义的特性如播放列表和社交分享等)以及业务逻辑部分(特定的业务逻辑特性如广告、设备兼容性逻辑以及认证管理等)。
多媒体引擎:这里处理所有播放控制相关的逻辑,如描述文件的解析,视频片段的拉取,以及自适应码率规则的设定和切换等等,我们将在下文中详细讲解这部分内容。由于这些引擎一般和平台绑定的比较紧,因此可能需要使用多种不同的引擎才能覆盖所有平台。
解码器和 DRM 管理器:播放器最底层的部分是解码器和 DRM 管理器,这层的功能直接调用操作系统暴露出来的 API。解码器的主要功能在于解码并渲染视频内容,而 DRM 管理器则通过解密过程来控制是否有权播放。
接下来我们将使用例子来介绍各层所扮演的不同角色。
一、用户界面(UI)
UI 层是播放器的最上层,它控制了你用户所能看到和交互的东西,同时也可以使用你自己的品牌来将其定制,为你的用户提供独特的用户体验。这一层最接近于我们说的前端开发部分。在 UI 内部,我们也包含了业务逻辑组件,这些组件构成了你播放体验的独特性,虽然终端用户没法直接和这部分功能进行交互。
UI 部分主要包含三大组件:
1. 皮肤
皮肤是对播放器视觉相关部分的统称:进度控制条、按钮和动画图标等等,如图 2 所示。和大部分设计类的组件一样,这部分组件也是使用 CSS 来实现的,设计师或者开发者可以很方便的拿来集成(即便你使用的是 JW Player 和 Bitdash 这种整套解决方案)。
图 2. 播放器皮肤
2. UI 逻辑
UI 逻辑部分定义了播放过程中和用户交互方面所有可见的交互:播放列表、缩略图、播放频道的选择以及社交媒体分享等。基于你预期达到的播放体验,还可以往这部分中加入很多其它的功能特性,其中有很多以插件的形式存在了,或许可以从中找到一些灵感:Plugins · videojs/video.js Wiki · GitHub 逻辑部分包含的功能较多,我们不一一详细介绍,直接以 Eurosport 播放器的 UI 来作为例子直观感受一下这些功能。
图 3. Eurosport 播放器的用户界面
从图 3 可以看出,除了传统的 UI 元素之外,还有一个非常有趣的特性,在用户观看 DVR 流媒体的时候,直播以小视窗的形式展示,观众可以通过这个小窗口随时回到直播中。由于布局或者 UI 和多媒体引擎完全独立,这些特性在 HTML5 中使用 dash.js 只需要几行代码就能实现。对于 UI 部分来说,最好的实现方式是让各种特性都以插件/模块的形式添加到 UI 核心模块中。
3. 业务逻辑
除了上面两部分「可见」的功能特性之外,还有一个不可见的部分,这部分构成了你业务的独特性:认证和支付、频道和播放列表的获取,以及广告等。这里也包含一些技术相关的东西,比如用于 A/B 测试模块,以及和设备相关的配置,这些配置用于在多种不同类型的设备之间选择多个不同的媒体引擎。
为了揭开底层隐藏的复杂性,我们在这里更详细的讲解一下这些模块:
设备检测与配置逻辑:这是最重要的特性之一,因为它将播放和渲染剥离开来了。例如,基于你浏览器的不同版本,播放器可能会自动为你选择一个基于 HTML5 MSE 的多媒体引擎 hls.js,或者为你选择一个基于 flash 的播放引擎 FlasHls 来播放 HLS 视频流。这部分的最大特点在于,无论你使用什么样的底层引擎,在上层都可以使用相同的 JavaScript 或者 CSS 来定制你的 UI 或者业务逻辑。
能够检测用户设备的能力允许你按需配置终端用户的体验:如果是在移动设备而非 4K 屏幕设备上播放,你可能需要从一个较低的码率开始。
A/B 测试逻辑:A/B 测试是为了能够在生产环节中灰度部分用户。例如,你可能会给部分 Chrome 用户提供一个新的按钮或者新的多媒体引擎,并且还能保证它所有的工作都正常如期进行。
广告(可选):在客户端处理广告是最复杂的业务逻辑之一。如 videojs-contrib-ads 这个插件模块的流程图给出一样,插入广告的流程中包含多个步骤。对于 HTTP 视频流来说,你或多或少会用到一些已有的格式如 VAST、VPAID 或者 Google IMA,它们能够帮你从广告服务器中拉取视频广告(通常是过时的非自适应格式),放在视频的前期、中期和后期进行播放,且不可跳过。
总结:
针对你的定制化需求,你可能选择使用包含所有经典功能的 JW Player 来播放(它也允许你定制部分功能),或者基于 Videojs 这样的开源播放器来定制你自己的功能特性。甚至为了在浏览器和原生播放器之间统一用户体验,你也可以考虑使用 React Native 来进行 UI 或者皮肤的开发,使用 Haxe 来进行业务逻辑的开发,这些优秀的库都可以在多种不同类型的设备之间共用同一套代码库。
图 4. 业务逻辑流程图
二、多媒体引擎
近年来,多媒体引擎更是以一种全新独立的组件出现在播放器架构中。在 MP4 时代,平台处理了所有播放相关的逻辑,而只将一部分多媒体处理相关的特性(仅仅是播放、暂停、拖拽和全屏模式等功能)开放给开发者。
然而,新的基于 HTTP 的流媒体格式需要一种全新的组件来处理和控制新的复杂性:解析声明文件、下载视频片段、自适应码率监控以及决策指定等等甚至更多。起初,ABR 的复杂性被平台或者设备提供商处理了。然而,随着主播控制和定制播放器需求的递增,一些新的播放器中慢慢也开放了一些更为底层的 API(如 Web 上的 Media Source Extensons,Flash 上的 Netstream 以及 Android 平台的 Media Codec),并迅速吸引来了很多基于这些底层 API 的强大而健壮的多媒体引擎。
图 5. Google 提供的多媒体处理引擎 Shakaplayer 的数据流程图
接下来我们将详细讲解现代多媒体处理引擎中各组件的细节:
1. 声明文件解释和解析器
在基于 HTTP 的视频流中,一切都是以一个描述文件开始。该声明文件包含了媒体服务器所需理解的元信息:有多少种不同类型的视频质量、语言以及字母等,它们分别是什么。解析器从 XML 文件(对于 HLS 来说则是一种特殊的 m3u8 文件)中取得描述信息,然后从这些信息中取得正确的视频信息。当然,媒体服务器的类型很多,并不是所有都正确的实现了规范,因此解析器可能需要处理一些额外的实现错误。
一旦提取了视频信息,解析器则会从中解析出数据,用于构建流式的视觉图像,同时知道如何获取不同的视频片段。在某些多媒体引擎中,这些视觉图像先以一副抽象多媒体图的形式出现,然后在屏幕上绘制出不同 HTTP 视频流格式的差异特征。
在直播流场景中,解析器也必须周期性的重新获取声明文件,以便获得最新的视频片段信息。
2. 下载器(下载声明文件、多媒体片段以及密钥)
下载器是一个包装了处理 HTTP 请求原生 API 的模块。它不仅用于下载多媒体文件,在必要的时候也可以用于下载声明文件和 DRM 密钥。下载器在处理网络错误和重试方面扮演着非常重要