背景
ARC(高德车机云控平台)是一个基于车载设备业务深度定制的云控平台,通过该平台我们能够实现远程使用不同类型的车载设备。为了让远程使用者像在本地一样使用车载设备,需要将车载设备的画面及时的传回给使用者。因此,画面传输能力是ARC平台的一个核心组件。
起初我们采用行业内普遍在用的画面传输开源方案(minicap)。该方案获取到屏幕数据后压缩生成JPG图像,逐帧传输到Web端进行展示。由于车机性能比手机差很多,压缩图片消耗CPU性能大,在部分低端车机设备上压缩图片能消耗80%左右的CPU,容易使设备使用出现卡顿。同时图像压缩率不算很高,传输消耗带宽大,在低带宽下造成用户看到的画面过度延迟。
因此,我们需要一个解决方案能够平衡传回的画面质量和车机端的CPU资源消耗。本文将小结本次云控平台画面传输的视频流方案。
思路方法
基于图像数据的基本传输链路,为了能够不消耗设备端CPU资源,首先想到了图像不进行压缩,先传输到服务端进行处理。但是经过调研,车机的USB带宽传输根本无法满足高清图像不压缩进行传输,高清原始数据非常大,基本1秒只能传输三帧左右的数据。
另一个思路是采用设备端的硬件编码器减少CPU资源的消耗。经过调研Android 4.1开始基本都自带了H264视频编码器。因此,决定尝试采用视频流的方案,在设备端通过硬件编码器编码成视频流,通过服务端转发到Web端进行解码展示。
实现方案
整个实现方案可以分为以下三个部分:
-
设备端:负责画面的获取和编码。
-
服务端:处理视频流的传输和控制。
-
Web端:视频流的解码和展示。
画面的获取和编码
图像画面的获取直接采用的是Android的Virtual Display。编码方式有多种实现方法:
由于Java方案只能支持Android 5.0以上机器, 而目前车载市场Android 4.x的占比还比较大,无法忽略。因此只能使用cpp的方案,最低可兼容Android 4.3版本。
视频流的传输和控制
Web端最常见的直播方案是rtmp/hls/flvjs等。但是这些方案最低都有1-3s的延迟。对于一般直播平台没有影响,但是对于有实时交互场景的云控平台,要求达到毫秒级延迟。所以,最终决定采用H264裸流通过Socket传输的方案,设备端编码H264视频流直接传输到Web端进行播放。
同时,为了提高使用体验,对视频流的传输增加了弹性控制。通过在服务端加入缓存队列用来监控前端带宽负载情况,根据带宽状况自动调节帧率和码率,优先保证使用者的流畅感。
Web端展示和解码
Web端展示使用media source extensiton(MSE) + fragment mp4的方案, 把H264裸流封装成fragment mp4后,通过MSE api进行解码播放, 具体实现是参考了开源的Jmuxer方案。
丢帧和补帧
默认情况下Android Virtual Display产生的最大帧率是60fps,而我们肉眼30fps就能感觉流畅。为了能够节省带宽,我们定义了视频流最大输出帧率是30fps。在网络带宽较差的情况下,我们还能够降低帧率来最大限度的避免延迟。同时,Android MediaCodec不支持控制帧率,帧率是由每秒送入的帧数量决定的。因此,我们需要通过实现丢帧来进行帧率控制。
Win7硬件解码器没有低延迟模式,需要大概10帧左右数据才能开始播放,而VirtualDisplay是画面有变化才会产生图像帧,因此需要实现补帧来消除解码延迟。
我们通过创建一个EglSurface对丢帧补帧进行处理,通过时间间隔控制eglTexture绘入EglSurface的频度进行丢帧,通过重复绘入最后一帧数据进行补帧。
总结
该方案在ARC平台上的使用,在保证传输质量的同时,有效的提升了使用者操作的流畅感。该方案理论上也可以应用于其他类似的云控平台上,如果不需要支持Android 4.x设备,采用Java层API来获取视频流数据,则可以降低开发和适配成本。