云控平台的双向音频解决方案
导读
随着移动互联网的发展,行业内衍生了基于移动平台的各类解决方案。其中,设备规模化管理的云控能力是各互联网公司在设备集群控制背景下的诉求。因此涌现了大批提供类似解决方案的平台。如:阿里系的阿里云MQC、阿里无线和菜鸟Nimitz等,阿里之外的有Testin、百度MTC、腾讯WeTest、华为、三星等等。
目前以上平台在云真机的使用上,都存在一个已知的短板 —— 声音。用户看的到画面,能够响应操作,但是涉及到声音播报、语音交互的场景时则无能为力。尤其对于音乐、视听、短视频、直播客户端等这类多媒体属性强的App,在云真机的使用场景上是受限最大的。
现在回到我们自己的产品。高德地图车机/镜版(后面统称Auto)。其中最常见的导航播报、与系统的多媒体混音交互、以及语音助手多轮对话的交互场景中,这些与声音相关的场景占比高达25%以上。所以解决远程场景下的声音双向交互问题,是云真机要成为一个日常化的生产工具之前必须迈过的坎。
挑战
在远程音频的双向通讯解决方案的背景下,满足基本用户体验的方面也存在以下挑战:
- 能力:满足所有车载设备的声音场景的双向交互能力(因为车载设备在声音部分比手机具有更高的定制性,在覆盖车载场景后,手机基本可以无缝适配);
- 延迟:传输延迟低于500ms(基于一定的网络条件);
- 体验:无明显卡顿、杂音问题。
设想
首先通过下面的一张图来了解一下我们的需求是什么:
- 将声音通过电脑传输到远端的车机设备(车机系统能正常解析处理);
- 将车机通过喇叭播报出的声音传输到用户端。
而实现这两条链路,关键核心的两个因素是:
- 如何获取和写入音频数据;
- 如何实现实时的音频数据在车机和用户设备间的传输链路。
音频获取和写入
Android系统音频概要
在思考如何进行设备的音频获取前,我们先来了解下Android的音频系统架构:
上图描述了音频通信从应用层、Libraries、HAL、到Driver,最后到硬件模块各层主要实现。而我们也需要从这条链路中去挖掘获取和写入音频数据的思路。
首先,我们考虑的是Android对应的音频链路中是否有成熟的支持双向音频的能力。即音频数据在OS内部获取到对外传输。
REMOTE_SUBMIX
API 19新加的MediaRecorder. AudioSource. REMOTE_ SUBMIX,用于传输系统混音的音频流到远端(在API 18也存在,只是属于隐藏属性)。
由于要生效REMOTE_SUBMIX,需要Manifest. permission. CAPTURE_ AUDIO_ OUTPUT权限,而该权限只有系统组件才具备。也就是如果第三方App需要的话,需要进行系统签名或者在烧写OS版本时就修改对应的权限。作为系统方是可以这么操作的,但显然对于要适配所有系统方的我们来说不适用。
软件hook
考虑到我们拿到的车载设备中,root比例高达80%以上。因此我们想从在音频数据传输到底层硬件驱动前进行“截胡”。也就是hook HAL定义的往驱动写入和读取对应音频数据的方法,来达到音频数据的双向获取。
- hook HAL hardware
hw hook的是struct audio_hw_device的
音频输出:open_output_stream、close_output_stream
音频输入:open_input_stream、close_input_stream
system/lib/hw/audio.primary.*.so (不同的设备有后缀部分差异)
- hook tinyalsa
在实际的调研测试中,我们发现并不是每台设备都能通过hook hw 来获取到对应的声音数据,尤其是车载设备。于是我们又调研了ALSA(Advanced Linux Sound Architecture)高级Linux声音架构。根据官方的推荐,我们选择了具备GPL-licensed 的external/tinyalsa
hook tinyalsa.so
音频输出:pcm_open、pcm_close、pcm_write、pcm_mmap_write
音频输入:pcm_open、pcm_close、pcm_read、pcm_mmap_read
system/lib/libtinyalsa.so
- 问题
在实际摸底验证中,我们发现车机比手机还复杂的原因在于多了功放的概念,而部分车厂选择在设备的DSP模块去处理混音。带来的问题就是部分设备如果单纯的通过hook播报,对应听到的声音与设备真实通过喇叭播报的效果不同,这也导致我们对于该场景的还原并不真实。
因此,在于root设备覆盖不完全且部分设备存在硬件功放处理混音问题的情况下,软件hook的方案只能适用于部分设备。
- 成本
hook自身也会带来一个问题,即针对不同的车机需要每台都进行hook处理,使得hook带来的成本过高。需要批量一键hook来解决这个问题。
分析到这里,我们回顾下音频传输的链路:
基于以上我们对音频获取的这条传输链路上的分析,现在理论上可行的获取途径,就只有硬件的对接或者具体的接收端(喇叭、蓝牙)。
usb音频
硬件对接部分,在云控场景下,我们的设备通常是通过USB线束与我们的节点PC连接的。因此音频通过USB进行传输的链路,也是一个值得探索的方向。
我们知道,Android设备在连接usb时有三种模式:Host、Development、Accessory Mode:
- 主机模式:可以传输音频,但是Android设备作为主机,无法使用adb的能力;
- 开发者模式:具备adb的能力,但是没有现成的USB音频能力;
- 配件模式:既保留了adb的能力,在Android4.1后的配件模式下,Android 也能自动将其音频输出导向到USB。
思路:通过实现AOA协议,作为主机角色的设备,必须具有能够将Android设备从开发模式切换到配件模式的主机控制能力,然后主机从适当的端点传输音频数据
该方案的局限性在于:1、单向传输;2、配件模式取决于设备硬件,但并非所有设备都支持。实测过程中,车机支持配件模式的比例很低,绝大多数都被“阉割”了。
综上,我们无法靠单纯的某种USB模式来实现音频的双向交互。但如果是手机集群的场景中,这个方案倒是可以作为单向音频传输的一个优选方案。
蓝牙接收
声音除了可以通过usb传输以外,常见的方式还有蓝牙耳机、有线耳机。(这里由于车载设备不存在3.5mm孔,所以我们先不讨论有线方式,具体可参考后面「硬件转发」的方案)。
关于蓝牙接收的基本思路就是PC端通过安装蓝牙接收器与车机通信。其中蓝牙接收器起到类似于蓝牙耳机的作用。然后对蓝牙接收器的收发数据在PC端进行编码处理。
蓝牙耳机:具备了可以听说的能力,也就是双向的音频通信。
摸底验证:部分车机对蓝牙驱动进行了定制,使得蓝牙设备只能作为从设备,无法接入蓝牙耳机功能。我们测试了35台,其中5台可以用,成功率14%,收益太低,成本过高。这个方案如果是面向手机集群,倒是一个不错的选择,理论上成功率应该会大大高于车载设备。
硬件转发
上面提到的有线耳机的思路。在车载设备上,不存在3.5mm孔或者type C口,而是通过主机与功放、音箱外放装置进行连接。在车辆量产上市前的研发阶段,只是一个主机通过线束连接着喇叭的一个过渡状态。所以我们实际是通过将原本接到喇叭上的音频数据通过一种转换装置转接到PC上,在PC端进行音频编码处理。
大致的参考示意效果如下:
上述方案的优势在于:
- 跨平台,不管是Android、Linux、QNX或者iOS的设备都适用;
- 解决了混音问题,由于对接的是最终播报出的声音效果,就不存在软件hook可能还原不真实的问题;
- 支持双向音频通信。
缺点:
- 部分智能车镜设备,由于集成封装性太强,没有暴露出可对接的线束。这类设备不容易通过该方案覆盖;
- 需要针对车机进行线束定制来实现整体的动态封装性;
- 需要配套的硬件成本。
硬件转发方案中存在几个方面的问题需要注意,比如失真问题、声卡识别问题、usb兼容上限、声卡与ehci、xhci的兼容性问题以及整体封装设计等等。
小结
综合以上音频传输的整条链路的所有方案,我们列举对比下这些方案的优劣(特指在车载场景下):
基于上述情况,考虑到车载的应用场景。最终我们的选型是 「软件hook」 +「硬件转发」的组合方案。
音频编码传输
关于音频编码传输这部分的内容,行业中已经有较成熟的解决方案,因此,这部分不展开篇幅讨论,我们仅针对一些方案做选型评估:
综上,从我们的应用场景以及高实时性要求考虑,最终选取了webRtc的方案。
最终选型
结合音频的获取和写入以及整体编码传输的方案,最终的技术方案选型图如下:
对应流程图中,也顺带涵盖了远程画面传输的视频流优化的参考链路。
总结
通过软硬件组合的方案来实现音频数据读写的能力,是一种基于特定背景条件下解决方案。但其基本推演的思路和策略,也是适用于手机平台的。而其中硬件的解决方案,理论上是适用于Android、iOS、Linux、QNX等平台设备的。
相较来说,手机的硬件转发成本更低。而对于软件的方案,实际的播报效果上仍会有很多细节问题,比如播报声音太小,需要对应设备去调节播报音量比例;出现延迟的场景,可能需要修改采样率;或是需要hook的自动化来降低成本等等。最终落地到项目中时,还需要考虑各方面的适配成本,确保整体的投入产出比。
关注高德技术,找到更多出行技术领域专业内容