网络播放器兼容性、扩展性与性能
liebian365 2025-01-11 14:58 21 浏览 0 评论
概述
近年来,点播、直播场景已成为越来越多产品的核心模块。而主流浏览器自带的原生播放器,难以满足业务提出的日益复杂的需求。从兼容性、性能、可扩展性、安全性等方面考虑,需要结合web端现有API与音视频技术、开发一款独立内核的万能网络播放器。
本文从兼容性,可扩展性,性能提升角度分享网络播放器相关技术。
1. 可扩展的分层架构
当前主流播放器一般采用组件化内核来实现对多种协议、编码格式的支持。如支持flv.js,hls.js,ffmpeg.wasm的插件化。这种架构模式有一定的扩展能力,但是由于内核的封装和隔离,内核之间相同逻辑和流程难以抽象复用,导致代码存在大量冗余,难以维护和二次开发。
针对以上问题,万能播放器从架构设计上打破内核之间的代码隔离,按照工作流将模块分层。各层之间采用统一的API通信,层内选用的技术方案对其他层透明。
可扩展的分层架构如图:
2. Demux模块的多协议支持
当前点播直播业务最广泛使用的FLV协议和HLS协议在原生Video标签上支持度都比较差,Demux模块对主流协议的解析支持,是播放器实现万能兼容的重要一环。
2.1 FLV Demux
FLV容器格式整体结构如图:
FLV header格式:
Header固定9个字节,解析中可以通过探测'0x46','0x4c','0x56'来判断流媒体容器格式,在确定格式的场景中也可以直接跳过9个字节进行FLV Body解析。FLV Body由连续的preTagSize和Tag组成,preTagSize标识上一个Tag的长度,用于逆向读取处理。Tag实际负载了FLV的音视频数据和元数据,是FlV Demux的核心任务。
FLV Tag格式:
FLV Tag的前11个字节为TagHeader,可以解析出Tag的TagType,DataSize和TS。TagType取值分为0x08(audio),0x09(video),0x12(scriptData)三种,对应后续Data不同的解析方式。TS是当前tag负载数据的时间戳,需要作为后续解码模块的入参传递。DataSize表示TagData的大小,DataSize + 11(tagHeader)+ 4(preTagSize)得出一次解析执行的offset,整个解析流程按照Offset循环调用解析方法即可完成Demux。
tagHeader解析:
tmp8[3] = 0 const t = yield 15 const type = t[4] tmp8[0] = t[7] tmp8[1] = t[6] tmp8[2] = t[5] const length = tmp32[0] tmp8[0] = t[10] tmp8[1] = t[9] tmp8[2] = t[8] let ts = tmp32[0] if (ts === 0xFFFFFF) { tmp8[3] = t[11] ts = tmp32[0] }
2.2 TS Demux
在TS协议之上通常还有一层m3u8 playlist的解析,限于篇幅略过这部分内容,TS层协议格式如图:
sync_byte=0x47TS标识包的起始位置,解析TS协议首先要找到PID值为0x0000的PAT包,通过PAT表查询获取PMT表。解析PMT就可以找到视频和音频包的PID从而获取负载了音视频数据的ts包。
ts层包大小固定188字节,PAT、PMT包属于特殊的TS包,没有adaptation field结构,直接通过0xff补齐长度:
解析音视频负载数据时,通过ts header中的有效载荷单元起始符(payload_unit_start_indicator),可以判断出ts packet携带的PES是否是一个PES包的第一个分包。如果是PES包的第一个分包,先要找到PES包头,提取时间戳,再跳至ES数据,这就是一帧数据的开始部分。直到解析到下一个payload_unit_start_indicator == 1,将前面的所有ES数据组合成一帧数据。开始下一轮组帧。PES层协议格式如图:
一般一帧的第一个和最后一个ts packet中存在adaptation field。第一个ts packet中的adaptation field包含了时钟参考(PCR_flag=1),而最后一个ts packet中的adaptation field是为了填充ts packet使之达到188 bytes。类似的,只有第一个分包的pes结构中含有pes packet header,后面分包的PES层只有es数据:
2.3 annexB & avcc
对于视频数据,最终传递给解码模块时,需要提前获取视频编码参数用于初始化解码器。以H264编码格式为例,视频分辨率,FPS,profile level等信息,需要解析SPS数据获取。SPS等数据的存储位置在不同容器中存在差异。
annexB:
[start code] NALU | [start code] NALU |...
NALU单元以startCode 0x000001作为开始标志,SPS PPS等也作为一类NALU存储在这个码流中。TS容器格式和H264裸流通常会使用这种排列方式。
avcc:
([extradata]) | ([length] NALU) | ([length] NALU) | ...
这种模式, NALU单元没有起始码,每个帧最前面4个字节是帧长度。NALU没有SPS PPS等参数信息,参数信息作为extraData存在其他地方。
2.4 FMP4 Remux
解码方案选择MSE时,按照API要求,Web端需要将Demux数据重新封装为FMP4容器格式:
以FLV格式为例,Remux过程可简略分为两步:
1. 将从FLV Header、ScriptData Tag、第一个Video Tag、第一个Audio Tag中获取到的信息提取出来,封装成FTYP、MOOV Box,构造出Fmp4 Initialization Segments。
2. 将剩下的Audio Tag,Video Tag demux的音视频数据和参数信息,封装成一组组的moof,mdat Box。
3. Decode模块的兼容性支持
解码技术方案的选择首先要考虑兼容性问题。不同操作系统,不同浏览器,浏览器的不同版本,对解码方案的支持程度存在差异。
3.1 Video标签
在所有解码方案中,原生Video标签解码性能较好,但是对容器格式支持度较差。在没有特殊需求的情况下,视频源容器格式为MP4,或在Safari浏览器中打开HLS-TS流时,播放器直接使用Video标签播放。
_play() { this.$videoElement && this.$videoElement.play().then(() => { this.player.debug.log('Video', '_play success'); setTimeout(() => { if (!this.isPlaying()) { this.player.debug.warn('Video', play failed and retry play) this._play(); } }, 100) }).catch((e) => { this.player.debug.error('Video', '_play error', e); }) }
3.2 Media Source Extensions
MSE允许Web端动态构建媒体流:
理论上结合转封装技术这种接口的应用可以让播放器实现所有媒体格式的播放。 由于MSE内部使用原生硬件解码,在性能方面对比FFmpeg解码具有很大优势。由于加载数据和构建FMP4的流程可控,相较Video标签直接播放,使用MSE技术的Web播放器在播放控制能力上有明显优势。
使用MSE作为硬件解码方案,需要考虑浏览器兼容性问题,主流浏览器对MSE支持如图:
3.3 WebCodecs
WebCodecs作为另一个硬件解码方案,在兼容性方面和MSE形成了互补,在IOS设备上,浏览器不支持MSE API,使用WebCodecs进行视频数据的解码,FFmpeg进行音频数据解码,是可选的一个高性能解码方案。
initDecoder() { const _this = this; this.decoder = new VideoDecoder({ output(videoFrame) { _this.handleDecode(videoFrame) }, error(error) { _this.handleError(error) } }) } this.decoder.configure(config); this.decoder.decode(chunk);
3.4 Webassembly & FFmpeg
Webassembly & FFmpeg是保证播放兼容性的终极方案。理论上只要ffmpeg框架能够支持的解码格式,web播放器就可以播放,FFmpeg.wasm与web Worker交互逻辑如图所示:
4.性能相关
4.1 Decode模块性能
播放内核应优先选择性能有优势的硬件解码方案(Video,MSE,webcodec)。当硬解码方案受系统或浏览器兼容性限制,或编码格式不受支持时,再降级到ffmpeg软解,作为兜底策略。
使用Webassembly & FFmpeg技术时,性能瓶颈是首要考量指标。可以提升性能的技术包括FFmpeg多线程解码(SharedArrayBuffer)和FFmpeg指令加速(SIMD),性能提升幅度如图表所示:
解码方式
解码路数(i7_8700k,h265,1080p
wasm
4
wasm 多线程
8
wasm simd 多线程
12
4.2 Web Worker
JS函数由事件触发,Stream,Demux,Decode模块中长耗时的函数可能会阻塞事件循环,导致播放器性能下降、阻塞用户与页面交互、甚至触发无响应异常。
使用Web Workers可以将耗时函数放在后台线程中运行,不会阻塞主事件循环和其他任务。利用多线程并发执行特性将显著提高播放器性能。
4.3 OffscreenCanvas
将渲染逻辑放到Worker中执行是提高播放器性能的可行方案。然而,由于Canvas的绘制功能都与<canvas>标签绑定在一起,导致Canvas API和DOM是耦合的,而Web Worker中不支持DOM使得Web Worker无法直接支持Canvas的渲染。
OffscreenCanvas通过将Canvas移出屏幕来解耦了DOM和Canvas API。由于这种解耦,OffscreenCanvas的渲染比普通Canvas渲染速度提升了一些(不需要DOM同步),更重要的是,通过绑定OffscreenCanvas,Canvas将可以在Web Worker中使用。
回顾与延展
本文从网络播放器架构的可扩展性;协议解析、音视频解码的兼容性;解码性能、渲染性能、多线程技术对性能的影响等角度介绍了网络播放器涉及的先关技术。
除此以外,网络播放器还可以与很多技术相结合,如AI大模型、webrtc低延迟直播、DRM安全加密等。这些技术的结合将进一步提升播放器的表现、性能、和安全性,更好的满足业务需求。
相关推荐
- 4万多吨豪华游轮遇险 竟是因为这个原因……
-
(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...
- “菜鸟黑客”必用兵器之“渗透测试篇二”
-
"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...
- 科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白
-
作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...
- 麦子陪你做作业(二):KEGG通路数据库的正确打开姿势
-
作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...
- 知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势
-
智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...
- 每日新闻播报(September 14)_每日新闻播报英文
-
AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...
- 香港新巴城巴开放实时到站数据 供科技界研发使用
-
中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...
- 5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper
-
本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...
- Qt动画效果展示_qt显示图片
-
今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...
- 如何从0到1设计实现一门自己的脚本语言
-
作者:dong...
- 三年级语文上册 仿写句子 需要的直接下载打印吧
-
描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...
- C++|那些一看就很简洁、优雅、经典的小代码段
-
目录0等概率随机洗牌:1大小写转换2字符串复制...
- 二年级上册语文必考句子仿写,家长打印,孩子照着练
-
二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)