这是我参与「第四届青训营 」笔记创作活动的第29天
随着B站、抖音、快手、淘宝直播等直播视频平台的快速崛起,前端衍生出了多媒体技术方向,各公司的传统前端团队里陆续出现了一支新军:Web多媒体团队。光看团队Title,这应该是一个拥有前端×多媒体交叉领域稀有技能的群体。阿里巴巴内部也存在众多Web多媒体团队,在内部我们针对新人整理了一份多媒体前端技术入门指南,今天将这份入门指南也对外分享出来,让大家了解我们近几年在该领域有哪些方向的工作,有哪些实践和落地,本文将从以下几个方面带着大家一窥究竟:
- 什么是多媒体前端?
- W3C标准的媒体技术
- 播放场景和解决方案
- 面向消费:直播视频里的业务体系
- 面向生产:直播推流、视频剪辑等工具
- 阿里巴巴前端委员会多媒体方向的发展和规划
什么是多媒体前端?
利用专业的前端能力,解决多媒体场景下各类技术和业务问题的前端,称之为多媒体前端。目前的多媒体前端主要从两类人群转化而来,一类是学数字媒体专业后从事前端开发的,一类是专业做前端后再学习多媒体的。这是两个成熟的知识体系交叉碰撞后的一个新的工作领域,需要具备高度还原、体验把控、跨端、工程化等前端开发能力,又要具有音视频基础、流媒体协议、播放技术、Web媒体技术等多媒体能力,目前这类人才的缺口还非常大。
W3C 标准的媒体技术
最开始接触多媒体前端开发,一般场景比较简单,比如在网页上播放一个视频/音频,实现基础的播放控制(播放、暂停、进度条、音量大小、静音等)。在HTML5标准之前如果要在浏览器里播放视频内容,需要使用Adobe Flash或微软的Silverlight等插件,但是插件存在需要用户安装的不便捷和不安全等问题,因此W3C的HTML5标准中定义了一系列新的媒体元素来避免使用插件。以下是媒体开发者常用的HTML元素:
HTML元素
- <
video> 标签用于播放视频或直播流,可以通过JS HTMLVideoElement对象访问,结合媒体API和其他DOM技术可实现更丰富的媒体应用 - <
audio> 标签用于播放音频,可以通过JS HTMLAudioElement对象访问 - <
source> 标签放在 <audio> 或者 <video> 内部,以指定播放的媒体源,可以添加多个不同格式、大小、分辨率的媒体源,通过JS HTMLSourceElement对象访问 - <
track> 标签放在 <audio> 或者 <video> 内部,在媒体播放时提供 WebVTT 格式化字幕或标题轨道。可以通过JS HTMLTrackElement对象访问
浏览器提供的媒体播放功能都是相当简单的,一个 video 或者 audio 标签再加上src就搞定了,但这缺少了诸如视频分段加载、视频码率切换、部分加载、内存管理等现代播放器应该有的功能,需要更定制化的播放需求,或者更丰富的多媒体应用,就要使用媒体API:
媒体API
- 媒体源扩展API
MSE (Media Source Extension) 扩展了浏览器的媒体播放功能,允许用JS动态构造媒体流MediaSource对象,然后喂给<video>和<audio>标签进行更精细化的播放控制。也可以用JS把一些不支持的视频流格式转封装为支持的格式,B站开源的flv.js就是这个技术的一个典型实现,使用MSE技术将FLV源用JS实时转封装成HTML5支持的视频格式。更多信息会在下一节播放场景和解决方案中介绍。
- 网络音频 API
Web Audio API 用于处理和合成Web应用程序中的音频,允许开发者进行声音合成、添加音频特效、音频可视化等,使用Web Audio API 我们几乎可以完成一个专业的Web音频处理软件(如节拍器、调音器等)。
- 媒体捕获和流媒体API
Media Stream API 让开发者可以使用本地摄像头和麦克风来采集录制音视频,或者捕获电脑屏幕,或者读取本地视频做合成,常用于Web摄像头、拍照、录屏、视频通话等,下文视频剪辑章节也有1688使用该技术实现web视频剪辑的案例。MediaStream 是连接 WebRTC API 和底层物理流的中间层,WebRTC将音视频经过Vocie / Video engine进行处理后,再通过MediaStream API暴露给上层使用。
- WebRTC
WebRTC 是一套支持浏览器进行实时音视频对话的 W3C Javascript API,它包括了音视频的采集、编解码、网络传输、显示等功能,使互联网上任意两位用户在无需服务器的情况下实现实时的音频、视频和任意数据的通信。在2010年左右,实时通信只能使用专有软件、插件或Adobe Flash进行;2013年,Chrome 和 Firefox 之间进行了首次跨浏览器视频通话,开启浏览器之间无插件化的音视频通话的序幕。现在WebRTC的使用场景非常丰富,在音视频通信、直播推流、云剪辑、云游戏等场景都可以看到WebRTC的使用。
除了这些媒体API,还有一些技术通常与媒体API共同使用:
与媒体 API 共同使用的技术
- Canvas API
Canvas API 允许在 <canvas> 上绘画、操纵并改变图像内容。这样可以与媒体以多种方式使用,包括设置 <canvas> 元素作为视频播放或摄像头捕获的节点以捕获或操纵视频帧。
- WebGL
WebGL 在已存在的 Canvas API 上提供了与 OpenGL ES 兼容的 API,使得在 Web 上制作强大的 3D 图像成为可能。通过一张画布,用于为媒体内容添加 3D 图像。
- WebVR
Web Virtual Reality API 支持虚拟现实 (VR) 设备,使开发人员能够将用户的位置和移动转换为 3D 场景中的移动,然后在设备上显示。WebVR 有望逐渐被 WebXR 所取代,后者涵盖了更广泛的用例。
- WebXR
WebXR 旨在最终取代 WebVR,是一种支持创建虚拟现实 (VR) 和增强现实 (AR) 内容的技术。混合现实内容可以显示在设备的屏幕上,或者是显示在眼镜或耳机内。
播放场景和解决方案
前文提到浏览器里通过<video>就能实现视频播放了,那还需要我们前端做些什么呢?其实<video>也存在很多限制,我们要先从媒体内容的封装格式和编码格式说起。
媒体内容源文件都是比较大的,为了便于传输和存储,需要对原视频文件通过编码来压缩文件大小,再通过容器封装将压缩后的视音频、字幕组合到一个容器内,这就是 编码 和 容器封装 的过程(可以用 压缩饼干 和 封袋包装 来理解,会出现很多不同的压缩工艺和包装规格)。
那么在播放端进行播放时,就要进行相应的 解封装 和 解码 (先拆开包装拿出饼干,享用饼干可以直接吃、也可以碾碎了吃、也可以泡着牛奶吃)。浏览器自带的播放器<video>标签拥有解封装和解码功能,但对媒体内容的格式是有所限制的(浏览器只会拆开特定的包装方式,只能消化特定的饼干吃法)。那浏览器碰到“不会拆、不消化的饼干”,我们要怎么喂给<video>呢?
除了容器格式、编码格式,还有流媒体协议、渲染容器、多实例播放等等问题需要多媒体前端一一解决,下面来分别介绍:
多协议、多容器格式
随着流媒体业务发展,出现了很多新的传输协议,媒体内容进一步包含在一层传输协议中(以HLS协议为例,增加了m3u8索引文件,并将源文件内容分片后封装到了一个个 TS 容器格式中),这样<video>就无法识别了。要支持多协议、多容器格式的播放,开发者可以通过 MSE 来帮助浏览器识别并处理,将媒体内容 转封装 成可识别的容器格式(如MP4),这样<video>就可以识别并播放各种媒体内容了。
上文提到的B站的flv.js以及社区的hls.js都是利用 MSE 来解决多协议、多容器格式的播放器库。
- flv.js
flv.js是Bilibili网站开源的HTML5 flv播放器,基于HTTP-FLV流媒体协议,通过纯JS实现FLV转封装,使flv格式文件能在Web上进行播放。
但是flv.js也不是所有的flv格式视频都能播放,并且对浏览器环境也有一定的要求,以下是flv.js的使用限制:
- 视频必须是AVC(H.264)编码,音频必须为AAC或者MP3编码
- 浏览器环境必须支持MSE,查看支持列表:caniuse.com/#feat=media…,值得注意的是,ie浏览器中,ie11以上才能正常使用,而ie11浏览器必须在win8系统以上才能运行;移动端上,ios仅支持在iPadOS 13以上系统,ios手机端完全不可运行;android则要求4.4以上系统
- 浏览器必须支持video,fetch api、xhr和websocket支持其一便可
- hls.js
hls.js是基于Http Live Stream协议开发,利用Media Source Extension,用于实现HLS在web上播放的一款js播放库。
由于HLS协议由苹果提出,并且在移动端设备上广泛支持,因此可以被广泛应用于直播场景。而hls.js在pc端只需要支持MSE便可以应用,移动端使用原生video标签设置src便可完成播放。hls.js会先请求m3u8文件,然后读取到文件的分片列表,以及视频的编码格式、时长等。随后会按照顺序去对TS分片进行请求,然后借助MSE将二进制buffer内容进行合流,组成一个可播的媒体资源文件。
阿里内部也有多个团队有类似播放器产品,如阿里云的Aliplayer、淘系的VideoX、优酷的KPlayer,实现思路都基本一致。
- 阿里云 Aliplayer
Aliplayer 经过几个版本的迭代演进,整个架构更加合理,赋予本身和用户更多的扩展能力,具有独立增加播放类型和功能的能力,比如要h5支持flv的播放能力,只需要新增Flv Extend功能扩展模块,而不用修改其他模块的代码,比如HLS Extend等等,确保不影响其他功能的正常工作,保持每个版本发布的稳定性。
- 淘系 VideoX
Videox 底层的播放层也经历了几次变化,从最开始简单的 <video>标签,到通过MSE来扩展各种格式的<video>标签,到通过WebAssembly来对编解码格式进行扩展的<canvas> + Web Audio API的方式,未来可能还有底层通信的扩展及上层互动能力的增强。
- 优酷 KPlayer
KPlayer 目前的方案拆分的较细,包括多个库和组件,主要有KMux 转封装库、KDRM WebDRM插件、KCTRL 播放器控制行为的核心抽象、MediaEngine 解码&渲染&播放的核心抽象、KUI 嵌入式UI框架。KPlayer播放核心设计如下:
多编码格式
上一小节解决了浏览器<video>不支持的协议和格式(不会拆饼干包装)的问题,那遇到不支持的编码格式(不能消化的压缩工艺)该怎么办呢?
新的视频编码标准H.265、AV1等比传统H.264拥有更高压缩率,但浏览器<video>本身并不支持,而业务为了降低成本都全链路切换新的编码格式如H.265,那多媒体前端就要自己实现浏览器端的JS播放器。由于Javascript是一种动态解释型语言,性能比C++等语言差很多,处理多媒体数据时性能短板就体现出来,WebAssembly的出现解决了Javascript的性能短板,极大扩展浏览器端的多媒体处理能力和场景。
对于Web端H.265播放器,阿里内部有多个团队都进行了相应的尝试,包括优酷、阿里云、淘系、ICBU等,思路基本一致,都是通过FFmpeg + Webassembly来实现一个JS播放器,使用JS拉流、解封装,将FFmpeg的265解码能力编译成wasm模块供JS调用,视频经过FFmpeg解码出来YUV帧数据,通过WebGL绘制图像帧数据,而音频方面因为浏览器支持AAC、MP3等主流音频格式,音频数据直接通过Web Audio API进行播放。设计思路如下:
多渲染容器
上面描述的场景主要是PC的Web浏览器,但我们更多的业务场景在移动端,面向消费者的场景对播放的体验和性能要求非常高,于是引入了播放器的跨端支持问题,尤其是跨渲染容器(Webview/Weex/小程序)。端侧的播放器主要使用Native播放器,前端封装成Weex/同层渲染组件,在各渲染容器中运行。
在端侧如果采用原生video方案,存在兼容性及性能问题,播放器本身占用内存高,业务不规范使用会带来稳定性风险,甚至导致APP Crash。以端内H5为例,采用的是和Rax团队、客户端基础团队、客户端播放器团队一起打造的同层渲染方案:
- Native播放器:负责播放器的基础能力,播放器内核fetch流地址后,解封装(demux)后解码(decode),然后输出到上层Naitve同层组件层,Native播放器提供了点播/直播能力
- Native同层组件:为底层播放器实例封装,主要负责联通通信连接层,并控制渲染
- 通信连接层:通过windmix管理渲染通道,再通过windvane绑定事件,传递属性,api能力调动,连接到前端播放器
- 前端播放器核心:担任前端播放器输出,将下层提供的事件/api/属性抹平成W3C制定的播放器标准,同时负责播放器稳定性相关能力
- 业务播放器封装:对下层的前端播放器核心封装播控协议,通过事件中心,管理各个播放器播放能力;同时从业务角度出发,对网络和设备性能进行监测,判断是否降级