背景
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 来获取视频流数据,则可以降低开发和适配成本。
本文转载自公众号高德技术(ID:amap_tech)。
原文链接:
评论