H.265/HEVC Web端直播播放器开发

H5视频播放器内核的开发对于前端工程师来说算是一个比较少涉足的领域,恰好工作中有所研究,分享出来给感兴趣的同学。简单的说,播放器内核的功能类似于一个video标签,它负责视频资源的解封装、解码和播放。

视频播放器架构

一个典型的现代播放器可以分为三个部分:UI、多媒体引擎和解码器[1]。本文要讲的视频播放器内核,其主要工作在图1中处于下面两层,主要包括:解码器、渲染器、网络管理引擎和媒体管理引擎。

相关背景技术

直播是目前最流行的一种信息传播和社交方式,区别于录制的点播视频,它要求播放器可以实时的获取并播放流式视频数据。为了实现直播播放,播放器内核需要借助一些HTML5的技术。如下图所示:

其中Audio MSE Controller依赖于Media Source Extension API,Stream Loader依赖于Stream标准,H.265 Decoder依赖于WebAssembly技术,而各个模块被划分在不同的线程中,依赖于Web Workers。具体来说:

Media Source Extensions(简称MSE), 提供了实现无插件且基于 Web 的流媒体的功能。使用MSE API(主要包括:Media Source,Source Buffer等),媒体流能够通过 JavaScript 创建,并且能通过HTMLMediaElement元素(包括:video和audio元素)进行播放。IE11(win8+)及其他现代浏览器都支持。

Streams[2]标准提供了一套API,来创建和操作流数据,具体地,包括ReadableStream, WritableStream, 以及TransformStream。这允许我们可以增量地处理数据,而不必将所有数据缓存到内存中统一处理。我们可以采用Fetch API[3]获取视频数据,返回的body是一个ReadableStream对象。该对象代表一个数据源,内部维护了一个队列来记录尚未被读取的底层数据源。可以通过ReadableStream的getReader()接口读取内部队列中chunk数据。

Web Workers[4] 让单线程的JavaScript具备了多线程编程的能力,让视频播放器内核可以分离解复用、解码、渲染、UI操作监听等任务到不同的线程中,并行地处理计算密集型任务和界面显示等。worker间通信是通过MessageChannel进行。IE10+及其他现代浏览器都支持。

WebAssembly[5]是Web端的字节码技术,它定义了一个通用的、体积紧凑、加载迅捷的二进制格式为编译目标,能发挥通用硬件的性能,以更接近原生应用的速度运行。在浏览器中对H.265编码的视频进行软件解码,是一项对性能非常有挑战的任务,JavaScript等脚本语言无法胜任此项工作。因此可以将C/C++语言编写的高性能解码库编译成字节码,再通过JavaScript调用来运行。目前此项技术在Chrome、Firefox、Safari和Edge浏览器的较新版本中都可以使用(如Chrome57+,Firefox 52+)。

H.265/HEVC编码与硬解平台支持

随着 4K 视频越来越流行,Apple公司的最新的操作系统版本(Mac Hight Sierra和iOS 11)迎来了 HEVC (高效视频编码,也称 H.265) 这一新的行业标准[6]。与现行的 H.264 视频压缩标准相比,它的视频压缩率最高可提升 40% 之多。使用H.265,在保持视频画质不变的情况下,视频流媒体传输效果更好。而在相同码率下,能给质量带来近两倍的提升。下图是两张相同码率相同分辨率(400kpbs 1080p)的图片,左边的采用H.265编码,右边的采用H.264编码。

一般来说操作系统借助硬件(显卡)进行H.265编码视频的解码工作,其好处是硬解的功耗低,解码速度快。但目前H.265编码在浏览器中的硬件解码支持情况并不普及。经测试只在定制的Chromium[7]及Edge 14浏览器中支持,可以通过此页面,测试浏览器对H.265编码的点播视频的播放情况。

需要注意的是硬件解码需要用户的显卡支持H.265 codec, 目前支持H.265解码的显卡主要包括:Intel HD Graphic 4400/4600/5000/5500/6000/620, Iris Graphics 5100/5200/6100,NVIDIA GeForce GTX 745, GTX 750, GTX 750 Ti, GTX 850M, GTX 860M,GeForce 830M, 840M,GeForce GTX 970, GTX 980, GTX 970M, GTX 980M,GeForce GTX TITAN X, GeForce GTX 980 Ti, GeForce GTX 750 SE, GTX 950, GTX 960, GeForce GTX 1070, GTX 1080, GeForce GTX 1060, NVIDIA TITAN XP, GeForce GTX 1050, GTX 1050 Ti

在直播场景下,视频流需要通过MSE接口发送给HTMLMediaElement播放,因而需要MediaSource接口也支持H.265的媒体类型字符串。目前只有Edge浏览器宣称支持H.265编码的流媒体播放,实测MediaSource.isTypeSupported('video/mp4; codecs="hvc1.xxx"')也确实返回true,但实际通过video标签播放H.265编码mp4封装的视频数据时,黑屏无画面。另外测试了其他两个宣传支持HEVC编码的Web播放器Radiant Media Player[9]和Bitmovin Video Player[10],也是同样的现象。

Web端软解方案

由于直播的Web端硬解方案目前限于浏览器支持情况无法实现,软件解码便成为了唯一的选择。由于H.265视频的解码是一个对性能要求很高的CPU密集任务,Web端脚本语言实现的解码器的性能很难达到要求。我们团队之前尝试过基于Flash的H.265解码方案,即通过FlasCC[11]编译器把C语言编写的解码器编译成swc库,然后在Flash播放器中用Action Script调用swc库。测试结果显示1080p高清视频每秒只能解码渲染10帧左右,无法达到应用要求。

另一种方案是基于HTML5的,即通过WebAssembly技术将金山云自研的高性能解码器编译为wasm库,wasm文件是以二进制形式存在的,其中包含平台无关的虚拟指令(类似汇编指令)。目前WebAssembly技术已经被四大主流浏览器的新版本所支持。基于该技术的播放器内核的实现如下图所示(图中虚线表示对象引用关系,实现表示数据传递):


该实现中包含4个线程,分别是转封装/转复用worker(TransmuxingWorker)、解码worker(H265decodingWorker)、颜色空间转换worker(RGBConvertingWorker)和负责渲染和播放控制的主线程。在TransmuxingWorker中,通过DownloadController下载流式媒体数据(在Safari、Chrome和Edge浏览器中我们使用Stream API实现下载,而在Firefox中可以基于XMLHttpRequest下载数据),然后根据视频封装格式的不同通过解复用器(Demuxer)分离音视频数据(http-flv流的解复用方法可参照flv.js相关代码,HLS流的解复用请参考hls.js相关代码[12])。之后将音频数据重新封装成Fragmented MP4格式【(其媒体类型为mp4a)。最后将mp4a音频数据发回主线程交给AudioMSEController,AudioMSEController将音频数据通过MSE接口传递给audio标签并播放。

另一方面,解封装出的视频数据通过MessageChannel发送到解码worker(H265decodingWorker),放入H265Decoder的待解码数据队列。H265Decoder基于WebAssembly技术调用底层解码器解码,输出YUV[14]数据。然后再发送给RGBConvertingWorker中的RGBConverter,进行YUV到RGB颜色空间的转换。最后将RGB数据帧发送给主线程中的音视频同步渲染器(AVSyncRender),保存到待渲染队列中。

音视频同步渲染采用视频同步到音频的策略,即音频正常播放,不断地检查待渲染视频队列中队首的视频帧,比较视频帧的展示时间戳(pts)与音频当前时间戳(currentTime),如果视频时间戳小于音频时间戳,就渲染当前待渲染队列队首的视频帧。否则要等到满足上述条件时再渲染该帧视频。如果待渲染队列空了,则等待固定时间(例如10ms)再恢复前述操作。这里的图像渲染是在canvas上渲染,目前在主流浏览器上canvas渲染是有GPU硬件加速的。经测试其渲染帧率在该场景下,甚至优于 WebGL [15] 渲染。另外出于性能上的原因,在worker间通信传递数据时需要采用零拷贝方式(又称Transferable Objects[16])。

本文介绍了Web端直播播放器的相关背景技术,及H.265 codec的Web端软硬解方案的探索,并对软解方案的一种实现进行了说明。

猜你喜欢

转载自blog.csdn.net/xiehuanbin/article/details/133311036