随着B站、抖音、快手、淘宝直播等直播视频平台的快速崛起,前端衍生出了多媒体技术方向,各公司的传统前端团队里陆续出现了一支新军:Web多媒体团队。光看团队Title,这应该是一个拥有前端×多媒体交叉领域稀有技能的群体。阿里巴巴内部也存在众多Web多媒体团队,在内部我们针对新人整理了一份多媒体前端技术入门指南,今天将这份入门指南也对外分享出来,让大家了解我们近几年在该领域有哪些方向的工作,有哪些实践和落地,本文将从以下几个方面带着大家一窥究竟:
什么是多媒体前端?
W3C标准的媒体技术
播放场景和解决方案
面向消费:直播视频里的业务体系
面向生产:直播推流、视频剪辑等工具
阿里巴巴前端委员会多媒体方向的发展和规划
利用专业的前端能力,解决多媒体场景下各类技术和业务问题的前端,称之为多媒体前端。目前的多媒体前端主要从两类人群转化而来,一类是学数字媒体专业后从事前端开发的,一类是专业做前端后再学习多媒体的。这是两个成熟的知识体系交叉碰撞后的一个新的工作领域,需要具备高度还原、体验把控、跨端、工程化等前端开发能力,又要具有音视频基础、流媒体协议、播放技术、Web媒体技术等多媒体能力,目前这类人才的缺口还非常大。
最开始接触多媒体前端开发,一般场景比较简单,比如在网页上播放一个视频/音频,实现基础的播放控制(播放、暂停、进度条、音量大小、静音等)。在HTML5标准之前如果要在浏览器里播放视频内容,需要使用Adobe Flash或微软的Silverlight等插件,但是插件存在需要用户安装的不便捷和不安全等问题,因此W3C的HTML5标准中定义了一系列新的媒体元素来避免使用插件。以下是媒体开发者常用的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
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共同使用:
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 来解决多协议、多容器格式的播放器库。
1. flv.js
flv.js是Bilibili网站开源的HTML5 flv播放器,基于HTTP-FLV流媒体协议,通过纯JS实现FLV转封装,使flv格式文件能在Web上进行播放。
但是flv.js也不是所有的flv格式视频都能播放,并且对浏览器环境也有一定的要求,以下是flv.js的使用限制:
视频必须是AVC(H.264)编码,音频必须为AAC或者MP3编码
浏览器环境必须支持MSE,查看支持列表:https://caniuse.com/#feat=mediasource,值得注意的是,ie浏览器中,ie11以上才能正常使用,而ie11浏览器必须在win8系统以上才能运行;移动端上,ios仅支持在iPadOS 13以上系统,ios手机端完全不可运行;android则要求4.4以上系统
浏览器必须支持video,fetch api、xhr和websocket支持其一便可
2. 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,实现思路都基本一致。
3. 阿里云 Aliplayer
Aliplayer 经过几个版本的迭代演进,整个架构更加合理,赋予本身和用户更多的扩展能力,具有独立增加播放类型和功能的能力,比如要h5支持flv的播放能力,只需要新增Flv Extend功能扩展模块,而不用修改其他模块的代码,比如HLS Extend等等,确保不影响其他功能的正常工作,保持每个版本发布的稳定性。
4. 淘系 VideoX
Videox 底层的播放层也经历了几次变化,从最开始简单的 <video>
标签,到通过MSE来扩展各种格式的<video>
标签,到通过WebAssembly来对编解码格式进行扩展的<canvas>
+ Web Audio API的方式,未来可能还有底层通信的扩展及上层互动能力的增强。
5. 优酷 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制定的播放器标准,同时负责播放器稳定性相关能力
业务播放器封装:对下层的前端播放器核心封装播控协议,通过事件中心,管理各个播放器播放能力;同时从业务角度出发,对网络和设备性能进行监测,判断是否降级
多实例控制
前面提到的都是单个视频播放的场景,实际业务中还有同一个页面中多个播放器的场景,比如列表流、版头等,这时候就引入了播放器多实例控制的问题,需要保证页面内只有一个播放器播放(播放甜区)、内存管理等。
Web 直播间架构
Hybrid 直播间架构
<video>
标准。小程序直播间架构
直播推流
视频剪辑
玩法生产