编者按: 作为一个历经了21个年头的播放器,VLC旺盛的生命力使其在今天仍然有着一席之地。但是21年前的定位所带来的与当今主流媒体播放器的差距依然不可小觑。LiveVideoStackCon2022上海站大会我们邀请到了腾讯云 客户端开发工程师 赵志立,为我们分享他们是如何让VLC走进低延迟的大门的以及VLC的未来是怎样的。
文/赵志立
整理/LiveVideoStack
大家好,我是来自腾讯云音视频的赵志立。本次为大家带来的分享的主要内容是我与VLC以及低延时直播之间的一些故事。
低延时直播是当下大热的话题,一提到VLC,许多人第一反应都是与低延时直播不沾边,确切的说,VLC是低延时直播的对立面。这样的观点无疑是正确的,不过今天我就来为大家介绍下VLC与VideoLan社区是如何将不沾边的VLC做到低延时“及格分数”的。
下面主要从VLC简介、全链路低延时直播以及VideoLan开源社区三个方面进行介绍。
一、VLC简介
VCL是具有悠久历史的播放器。从1996年起,到2001年以GPL协议发布,直到现在已经走过21个年头。VLC几乎支持了所有能用的系统,从广为人知的Windows到鲜为人知的OS2,时至今日仍有开发者在持续不断更新维护着。
虽然VLC通常被作为播放器使用,但历史上的VLC其实是由两个部分组成的。一个是VLC客户端,另一个是VideoLan Server (VLS),在发展的过程中两个部分逐渐合并,才有了今天的VLC。在国外VLC经常用于投屏,例如VLC支持Chromecast投屏协议。
VLC的架构建立在插件化的基础上,其核心很小,仅提供内存管理、网络基础操作、多线程封装和时钟同步等功能,其它例如输入设备、传输协议、封装格式、编码格式、渲染方式等都是通过插件实现的,是M*N*O*P的自由组合方式。
很多人误解VLC只是FFmpeg的简单封装,但事实上VLC诞生比FFmpeg早,所以这种观点不攻自破。VLC和FFmpeg关系十分紧密,FFmpeg是VLC的重要组成部分,但不是必须的,VLC的解码、解封装均有多种方式实现。
VLC和Gstreamer也有着千丝万缕的联系。VLC可以使用Gstreamer的codec进行解码,VLC和Gstreamer都具有插件化的特性,但相比之下,VLC的插件化刚刚好,Gstreamer的插件化就有一些“走火入魔”了。
这是VLC版本演进的过程。现在广泛使用的是3.0稳定版,3.0版本已经可以支持如VR、HDR和AV1等功能。并且3.0版本对移动端的硬件解码进行了全面的加速支持。
4.0开发版还未发布。对于我个人来说,最为重要的升级是重新设计了Clock时钟同步模块。另外用户较为关心的UI界面也进行了较为现代化的设计,在低延时方面也有很多的改进。
下面简单介绍一些VLC的功能。VLC可以在命令行里播放视频,用字符来显示像素;还有幻觉滤镜;还可以在播放视频时暂停,玩一个拼图游戏;VLC可以外挂多个视频同时播放。这样就可以在开发过程中进行视频的对比。下面的两张图是我们在工作时对腾讯云HDR tone-mapping效果的对比。
二、VLC与低延迟
关于VLC和低延迟直播我们主要从低延迟直播行业背景、传统播放器与在线流媒体对比、全链路低延时分析、VLC低延时优化、VLC 0延迟概念验证等几个方面进行介绍。
1、低延迟直播行业背景
这是2020年Bitmovin做的一个调查,排在第一名的毫无疑问就是低延迟。
2、传统播放器vs在线流媒体
因为VLC诞生比较早,所以它的目标定位和现在的播放器定位略有不同。VLC支持的多样场景远超其它播放器,低延迟直播的场景仍然属于小范围内的场景。当前在线流媒体对QoS和QoE十分重视,而VLC是不考虑首帧、快进快退的速度的。VLC支持各种网络传输协议,内置就包括Samba、FTP等的支持。VLC的音视频同步时钟、缓冲设计来自早期DVB时代。又因为其属于开源驱动,在开源的前提下实现向下兼容,对于低延迟这个目标来说是十分困难的。
3、全链路低延迟分析
下面系统地介绍全链路低延迟。端到端在英文中有另一个名称——Glass to Glass。第一个Glass指的是摄像头,第二个Glass指的是显示器。其中经过采集、编码、封装、传输到服务端,再经过传输、解封装、解码、渲染。每一个过程都有很多种技术可以选择。
我们先从两个Glass入手。Camera Latency我本人研究不太多,这里不过多赘述。播放器在进行音视频同步时,大家往往会让某一帧在到一定时间点时才渲染。但从系统拿到这一帧,再渲染输出,这里还存在一部分延迟,而这一部分延迟,往往会被人们所忽略。进行安卓系统开发的同学可以关注一下右下角两个API的差异,下面的API可以提供更为准确的音视频同步以及更低的延迟。
在编解码和延迟方面我想特别指出右边两句话。1.解决问题的技术手段几乎总是伴随着副作用,不存在万能良药。2.掌握其特性,权衡收益与损失,对症下药。例如当一个关键帧过大时,发送该关键帧会对网络产生一定的冲击,虽然可以通过多Slice编码的方法,降低延迟、减少对网络的冲击,但是多Slice编码会降低压缩的效率。就像禁用B帧、减小GOP、开启Periodic Intra Refresh等等方法,在为我们带来便利的同时也或多或少存在一些副作用。
接下来重点介绍一下解码方面的低延迟。一些朋友认为现在硬件设备越来越快,解码不存在延迟,但事实上,不同平台硬件解码的延迟是不同的。另外如果延迟做到秒以内,解码的延迟会非常明显。有两个常见的误区:
第一个是硬件解码比软件解码延迟低。硬件解码虽然吞吐量比软件高,但其延迟也可以会高于软解码。尤其是某些国产手机厂商移动端的延迟可以达到100ms甚至更多。
第二个是FFmpeg软件解码吞吐量与延迟。FFmpeg软解解码有两种并行的方式来加快解码速度。一个是帧级别的多线程并行,另一个是Slice级别的多线程。默认情况下多为帧级别的多线程。随着芯片技术升级,CPU核数越来越多,FFmpeg默认开启的线程数也会增加,而每增加一个线程就多一帧延迟。所以在用FFmpeg进行软件解码时需要控制并行的线程数或者修改并行的方式。
通常我们使用现有的工具进行解封装,而不是从头造轮子,很少有人会考虑解封装对延迟的影响。封装对延迟的影响至少有如下两个方面:
一是媒体封装冗余。封装层也可能占用很大一部分带宽,例如TS封装的冗余可高达15%。封装格式和编码的关系就像过度包装的快递,我们要的只是里面的小东西,却需要很大盒子的“容器”——封装。
另一个是音视频交织的方式对延迟的影响。其中包含交织错位和交织稀疏两方面。交织错位顾名思义,音频和视频错位,音频走到10s而视频走到15s。如果播放端进行了同步,会导致某一个流下载了一堆却没法使用,从而导致延迟的增加。交织稀疏是指类似前两秒只有音频,后两秒只有视频的情况。虽然对于WebRTC和一些音频视频分开的场景影响稍小,但如何在音视频同步的同时做到低延迟仍然需要我们认真思考解决办法。
WebRTC是现在做超低延迟的首选。除了WebRTC,HLS正在Apple的努力下和DASH一起向低延迟靠近,但它们的目标是降低延迟到5秒以内而不是取代WebRTC。国内似乎特别热衷于RTMP,出现了很多基于RTMP的再创造,例如RTMP和QUIC结合、RTMP和SRT结合等等。总而言之,选择一个合适的传输协议是达成低延迟的关键。
上面一列是传统播放器架构的设计模式。任何一级和下一级之间都可能存在缓冲。大家很少会注意到传输到解复用之间缓冲的大小。解复用到解码之间存在几秒,十几秒甚至分钟级别的缓冲。解码到渲染也存在缓冲队列,解码并行时缓冲也会隐藏在解码器内部。多级的缓冲是为了架构灵活播放流畅,但对于延迟来说十分不友好。SRT的一个特性叫做固定延迟,即在传输时其延迟会在设定值附近,不会有太大变动。在之后的操作中不再需要考虑额外的延迟。在传输过程中进行ARQ的丢包恢复时,缓冲区越大,丢包恢复能力越强,但如果缓冲区被分配到Demux之后,则无法用于ARQ丢包恢复。只有全部在传输层时,才会得到最大化的利用。虽然效果明显,但是这一级也存在一定的使用难度,例如其时间戳感知不太强烈。WebRTC因为存在RTP,传输和解封装有一定程度重合,天生具有一定的优势。SRT通过自己设定时间戳来控制延迟。
多链路传输走进人们的视野已经有一段时间了。SRT、RIST、Multipath TCP也支持多链路。多链路传输有多种模式,有的可以降低延迟,有的增加延迟,其中提升传输带宽是通过牺牲延迟实现的。在使用时需要根据产品形态、使用场景等综合考虑,选取一种合适的模式。
4、VLC延迟优化分析
这些是VLC在低延迟方面的优化,关注点主要是传输、解封装、解码和渲染。VLC3.0版本就已经支持低延迟传输SRT和RIST,但在使用过程中存在不少问题,会在4.0版本解决。WebRTC因为存在copyright的考虑以及其过于庞大,导致不太适合放在上游社区。同样的还有FFmpeg,大家通常是基于FFmpeg进行定制来支持WebRTC,但很难推到upstream。
我们在VLC内部新增了low-delay模式,同时影响解封装、解码、缓冲控制等环节。Avcodec解码插件在low-delay模式时,会禁用帧级别多线程FF\_THREAD\_FRAME,打开Slice级别线程并行。
VLC使用PCR进行音视频同步,PCR的主要作用是同步编码端和播放端的时钟。VLC从2004年开始特别依赖PCR,甚至没有PCR的mp4也会“伪造”生成PCR。通过PCR进行缓冲控制,TS的PCR会增加延迟,所以现在特别增加了一个针对TS的配置来降低PCR的影响。这些属于常规优化,已经集成到了VLC内部。我们下一阶段的目标是将VLC的延迟做到可用于远程遥控的场景,目前处于概念验证阶段,还未添加到VLC内部。
5、VLC 0延迟
0延迟是一种夸张的说法,右边是效果展示图片。第一个是屏幕录制,之后一个推流或拉流到VLC播放器,另一个使用scrcpy播放,可以看到后面的效果更好一点,画面差值不到两帧,意味着它做到了两帧以内的延迟。测试时传输是通过USB的方式进行的,结果证明了假设传输时没有延迟,那么播放器本身的延迟其实是非常小的。我们关闭了时钟同步,不管时间戳,直接渲染;甚至还可以关闭所有的缓冲区、解码器和渲染之间的FIFO。做完这些之后,还有一些过去从未关注的环节需要优化。例如在264中,Parser找一帧的结尾是通过找下一帧的开头来实现的。这样会造成,只有下一帧来了之后,才能分割出上一帧进行解码,会带来一帧的延迟。为了去掉这一帧的延迟,需要重新设计一个方式来寻找一帧的结尾。图中是Parser如何找下一帧的示意。
6、总结
总结一下,低延迟不仅仅是指优化传输、服务端。如果播放器自身的延迟控制不好,针对传输、服务端的优化也是没有效果的。必须进行一个系统的优化,对各个环节进行divide and conquer。表格中左边是播放器优化应该做的工作,右边是上行推流和流媒体服务应该做的工作。虽然越往下影响越小,但也往往是越容易被忽略的。
元曲《醉太平•讥贪小利者》正是极致优化的境界——“鹭鸶腿上劈精肉,蚊子腹内刳脂油。”把所有能做到的都做到,才能算得上是极致的优化。VLC低延迟优化已经有了明显的进步,但仍然任重道远。
三、VLC和VideoLan开源社区
下面说说VLC和VideoLan社区之间的故事。
在VLC发展过程中诞生了VideoLan组织,VideoLan组织一开始是为VLC服务的,不过之后也诞生出一批优秀的开源项目。大家相对来说会更熟悉左边部分,例如x264、x265都属于VideoLan社区的产物。目前最新的产物是dav1d,它是当前AV1最快的开源解码器。
VideoLan社区以代码为核心,除此之外还有一个很重要的支持点——商业化。有一个公司专门从事VLC方面的工作,这个公司的开发者同时也是VLC的核心开发者,虽然人数不多,但是正是因为有这么一批人专门维护VLC,VLC才得以支持那么多的特性。大家做过移动端APP的应该是颇有体会,完全靠开源来做是有点困难的,这时候必须要有专职开发者的辅助才能顺利上架谷歌或苹果商店。
从右边可以看到,目前我们运营的项目以代码为核心,以开源社区化和商业化这两个驱动来保持项目的健康发展。社区会保证该项目的独立性,商业化会保证人力的支持。
现阶段大家已经习惯了视频内容的消费平台化、在线化。大家对本地视频播放器的需求虽然还有,但是越来越少。另外流媒体时代QoS、QoE十分被重视,大家对VLC的种种诉求我们也清楚,但由于向下兼容的负担,导致只能一步一步发展。现在很多产品的二次开发很简便,拿到一个产品改几行代码就能使用,但VLC的二次开发是非常困难的,甚至我都不建议大家进行VLC的二次开发。最后,移动端的兼容性越来越重要,VLC移动端做得还不错,但总体上因为缺少人力的支持,开发工作重心在本地播放,在线播放功能不尽如人意。
VLC已经做了21年,其定位是一个播放器,是一个工具,这一点是无论如何都不会改变的,不会为了其它的利益而改变。在发展过程中也会一步一个脚印,虽然走得慢,但是可以走的更远。我们也在尝试一些新的技术,例如WebAssembly,VLC目前有一个测试版本支持WebAssembly,可以把VLC直接编译成WebAssembly,运行在Web端。其它的例如RISC-V、PipeWire等我们也在尝试加入并不断完善。社区也做了一个云储存插件,可以让VLC直接播放云储存的视频,不再需要拉流到本地。
以上是本次分享全部内容,谢谢大家。