|
近年来,点播、直播场景已成为越来越多产品的核心模块。而主流浏览器自带的原生播放器,难以满足业务提出的日益复杂的需求。从兼容性、性能、可扩展性、安全性等方面考虑,需要结合web端现有API与音视频技术、开发一款独立内核的万能网络播放器。本文从兼容性、可扩展性、性能提升角度分享网络播放器相关技术。01可扩展的分层架构当前主流播放器一般采用组件化内核来实现对多种协议、编码格式的支持。如支持flv.js,hls.js,ffmpeg.wasm的插件化。这种架构模式有一定的扩展能力,但是由于内核的封装和隔离,内核之间相同逻辑和流程难以抽象复用,导致代码存在大量冗余,难以维护和二次开发。针对以上问题,万能播放器从架构设计上打破内核之间的代码隔离,按照工作流将模块分层。各层之间采用统一的API通信,层内选用的技术方案对其他层透明。可扩展的分层架构如图:02Demux模块的多协议支持当前点播直播业务最广泛使用的FLV协议和HLS协议在原生Video标签上支持度都比较差,Demux模块对主流协议的解析支持,是播放器实现万能兼容的重要一环。2.1 FLV DemuxFLV容器格式整体结构如图: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。03Decode模块的兼容性支持解码技术方案的选择首先要考虑兼容性问题。不同操作系统,不同浏览器,浏览器的不同版本,对解码方案的支持程度存在差异。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 ExtensionsMSE允许Web端动态构建媒体流:理论上结合转封装技术这种接口的应用可以让播放器实现所有媒体格式的播放。由于MSE内部使用原生硬件解码,在性能方面对比FFmpeg解码具有很大优势。由于加载数据和构建FMP4的流程可控,相较Video标签直接播放,使用MSE技术的Web播放器在播放控制能力上有明显优势。使用MSE作为硬件解码方案,需要考虑浏览器兼容性问题,主流浏览器对MSE支持如图:3.3 WebCodecsWebCodecs作为另一个硬件解码方案,在兼容性方面和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 & FFmpegWebassembly & FFmpeg是保证播放兼容性的终极方案。理论上只要ffmpeg框架能够支持的解码格式,web播放器就可以播放,FFmpeg.wasm与web Worker交互逻辑如图所示:04性能相关4.1 Decode模块性能播放内核应优先选择性能有优势的硬件解码方案(Video,MSE,webcodec)。当硬解码方案受系统或浏览器兼容性限制,或编码格式不受支持时,再降级到ffmpeg软解,作为兜底策略。使用Webassembly & FFmpeg技术时,性能瓶颈是首要考量指标。可以提升性能的技术包括FFmpeg多线程解码(SharedArrayBuffer)和FFmpeg指令加速(SIMD),性能提升幅度如图表所示:解码方式解码路数(i7_8700k,h265,1080pwasm4wasm 多线程8wasm simd 多线程12 4.2 Web WorkerJS函数由事件触发,Stream,Demux,Decode模块中长耗时的函数可能会阻塞事件循环,导致播放器性能下降、阻塞用户与页面交互、甚至触发无响应异常。使用Web Workers可以将耗时函数放在后台线程中运行,不会阻塞主事件循环和其他任务。利用多线程并发执行特性将显著提高播放器性能。4.3 OffscreenCanvas将渲染逻辑放到Worker中执行是提高播放器性能的可行方案。然而,由于Canvas的绘制功能都与标签绑定在一起,导致Canvas API和DOM是耦合的,而Web Worker中不支持DOM使得Web Worker无法直接支持Canvas的渲染。OffscreenCanvas通过将Canvas移出屏幕来解耦了DOM和Canvas API。由于这种解耦,OffscreenCanvas的渲染比普通Canvas渲染速度提升了一些(不需要DOM同步),更重要的是,通过绑定OffscreenCanvas,Canvas将可以在Web Worker中使用。05总结本文从网络播放器架构的可扩展性;协议解析、音视频解码的兼容性;解码性能、渲染性能、多线程技术对性能的影响等角度介绍了网络播放器涉及的先关技术。除此以外,网络播放器还可以与很多技术相结合,如AI大模型、webrtc低延迟直播、DRM安全加密等。这些技术的结合将进一步提升播放器的表现、性能、和安全性,更好的满足业务需求。更多技术和产品文章,请关注360智汇云是以"汇聚数据价值,助力智能未来"为目标的企业应用开放服务平台,融合360丰富的产品、技术力量,为客户提供平台服务。目前,智汇云提供数据库、中间件、存储、大数据、人工智能、计算、网络、视联物联与通信等多种产品服务以及一站式解决方案,助力客户降本增效,累计服务业务1000+。智汇云致力于为各行各业的业务及应用提供强有力的产品、技术服务,帮助企业和业务实现更大的商业价值。官网:https://zyun.360.cn客服电话:4000052360
|
|