HarmonyOS开发者限时福利来啦!最高10w+现金激励等你拿~ 了解详情
写点什么

云控平台的双向音频解决方案

  • 2020-03-04
  • 本文字数:4202 字

    阅读完需:约 14 分钟

云控平台的双向音频解决方案

1. 导读

随着移动互联网的发展,行业内衍生了基于移动平台的各类解决方案。其中,设备规模化管理的云控能力是各互联网公司在设备集群控制背景下的诉求。因此涌现了大批提供类似解决方案的平台。如:阿里系的阿里云 MQC、阿里无线和菜鸟 Nimitz 等,阿里之外的有 Testin、百度 MTC、腾讯 WeTest、华为、三星等等。


目前以上平台在云真机的使用上,都存在一个已知的短板 —— 声音。用户看的到画面,能够响应操作,但是涉及到声音播报、语音交互的场景时则无能为力。尤其对于音乐、视听、短视频、直播客户端等这类多媒体属性强的 App,在云真机的使用场景上是受限最大的。


现在回到我们自己的产品。高德地图车机/镜版(后面统称 Auto)。其中最常见的导航播报、与系统的多媒体混音交互、以及语音助手多轮对话的交互场景中,这些与声音相关的场景占比高达 25%以上。所以解决远程场景下的声音双向交互问题,是云真机要成为一个日常化的生产工具之前必须迈过的坎。


2. 挑战

在远程音频的双向通讯解决方案的背景下,满足基本用户体验的方面也存在以下挑战:


  • 能力:满足所有车载设备的声音场景的双向交互能力(因为车载设备在声音部分比手机具有更高的定制性,在覆盖车载场景后,手机基本可以无缝适配);

  • 延迟:传输延迟低于 500ms(基于一定的网络条件);

  • 体验:无明显卡顿、杂音问题。

3. 设想

首先通过下面的一张图来了解一下我们的需求是什么:



  • 将声音通过电脑传输到远端的车机设备(车机系统能正常解析处理);

  • 将车机通过喇叭播报出的声音传输到用户端。


而实现这两条链路,关键核心的两个因素是:


  • 如何获取和写入音频数据;

  • 如何实现实时的音频数据在车机和用户设备间的传输链路。

4. 音频获取和写入

4.1 Android 系统音频概要

在思考如何进行设备的音频获取前,我们先来了解下 Android 的音频系统架构:



上图描述了音频通信从应用层、Libraries、HAL、到 Driver,最后到硬件模块各层主要实现。而我们也需要从这条链路中去挖掘获取和写入音频数据的思路。


首先,我们考虑的是 Android 对应的音频链路中是否有成熟的支持双向音频的能力。即音频数据在 OS 内部获取到对外传输。

4.2 REMOTE_SUBMIX

API 19 新加的 MediaRecorder. AudioSource. REMOTE_SUBMIX,用于传输系统混音的音频流到远端(在 API 18 也存在,只是属于隐藏属性)。


由于要生效 REMOTE_SUBMIX,需要 Manifest. permission. CAPTURE_AUDIO_OUTPUT 权限,而该权限只有系统组件才具备。也就是如果第三方 App 需要的话,需要进行系统签名或者在烧写 OS 版本时就修改对应的权限。作为系统方是可以这么操作的,但显然对于要适配所有系统方的我们来说不适用。

4.3 软件 hook

考虑到我们拿到的车载设备中,root 比例高达 80%以上。因此我们想从在音频数据传输到底层硬件驱动前进行“截胡”。也就是 hook HAL 定义的往驱动写入和读取对应音频数据的方法,来达到音频数据的双向获取。

4.3.1 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 (不同的设备有后缀部分差异)

4.3.2 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

4.3.3 问题

在实际摸底验证中,我们发现车机比手机还复杂的原因在于多了功放的概念,而部分车厂选择在设备的 DSP 模块去处理混音。带来的问题就是部分设备如果单纯的通过 hook 播报,对应听到的声音与设备真实通过喇叭播报的效果不同,这也导致我们对于该场景的还原并不真实。


因此,在于 root 设备覆盖不完全且部分设备存在硬件功放处理混音问题的情况下,软件 hook 的方案只能适用于部分设备。

4.3.4 成本

hook 自身也会带来一个问题,即针对不同的车机需要每台都进行 hook 处理,使得 hook 带来的成本过高。需要批量一键 hook 来解决这个问题。


分析到这里,我们回顾下音频传输的链路:



基于以上我们对音频获取的这条传输链路上的分析,现在理论上可行的获取途径,就只有硬件的对接或者具体的接收端(喇叭、蓝牙)。

4.4 usb 音频

硬件对接部分,在云控场景下,我们的设备通常是通过 USB 线束与我们的节点 PC 连接的。因此音频通过 USB 进行传输的链路,也是一个值得探索的方向。


我们知道,Android 设备在连接 usb 时有三种模式:Host、Development、Accessory Mode:


  • 主机模式:可以传输音频,但是 Android 设备作为主机,无法使用 adb 的能力;

  • 开发者模式:具备 adb 的能力,但是没有现成的 USB 音频能力;

  • 配件模式:既保留了 adb 的能力,在 Android4.1 后的配件模式下,Android 也能自动将其音频输出导向到 USB。


思路:通过实现 AOA 协议,作为主机角色的设备,必须具有能够将 Android 设备从开发模式切换到配件模式的主机控制能力,然后主机从适当的端点传输音频数据


该方案的局限性在于:1、单向传输;2、配件模式取决于设备硬件,但并非所有设备都支持。实测过程中,车机支持配件模式的比例很低,绝大多数都被“阉割”了。


综上,我们无法靠单纯的某种 USB 模式来实现音频的双向交互。但如果是手机集群的场景中,这个方案倒是可以作为单向音频传输的一个优选方案。

4.5 蓝牙接收

声音除了可以通过 usb 传输以外,常见的方式还有蓝牙耳机、有线耳机。(这里由于车载设备不存在 3.5mm 孔,所以我们先不讨论有线方式,具体可参考后面「硬件转发」的方案)。


关于蓝牙接收的基本思路就是 PC 端通过安装蓝牙接收器与车机通信。其中蓝牙接收器起到类似于蓝牙耳机的作用。然后对蓝牙接收器的收发数据在 PC 端进行编码处理。


蓝牙耳机:具备了可以听说的能力,也就是双向的音频通信。


摸底验证:部分车机对蓝牙驱动进行了定制,使得蓝牙设备只能作为从设备,无法接入蓝牙耳机功能。我们测试了 35 台,其中 5 台可以用,成功率 14%,收益太低,成本过高。这个方案如果是面向手机集群,倒是一个不错的选择,理论上成功率应该会大大高于车载设备。

4.6 硬件转发

上面提到的有线耳机的思路。在车载设备上,不存在 3.5mm 孔或者 type C 口,而是通过主机与功放、音箱外放装置进行连接。在车辆量产上市前的研发阶段,只是一个主机通过线束连接着喇叭的一个过渡状态。所以我们实际是通过将原本接到喇叭上的音频数据通过一种转换装置转接到 PC 上,在 PC 端进行音频编码处理。


大致的参考示意效果如下:



上述方案的优势在于:


  • 跨平台,不管是 Android、Linux、QNX 或者 iOS 的设备都适用;

  • 解决了混音问题,由于对接的是最终播报出的声音效果,就不存在软件 hook 可能还原不真实的问题;

  • 支持双向音频通信。


缺点:


  • 部分智能车镜设备,由于集成封装性太强,没有暴露出可对接的线束。这类设备不容易通过该方案覆盖;

  • 需要针对车机进行线束定制来实现整体的动态封装性;

  • 需要配套的硬件成本。


硬件转发方案中存在几个方面的问题需要注意,比如失真问题、声卡识别问题、usb 兼容上限、声卡与 ehci、xhci 的兼容性问题以及整体封装设计等等。

4.7 小结

综合以上音频传输的整条链路的所有方案,我们列举对比下这些方案的优劣(特指在车载场景下):


方案优势劣势覆盖率
REMOTE_SUBMIX1、现有API
2、同时利于音视频同步编码
3、无硬件成本
1、需要系统签名或root权限。
2、只有音频获取,无音频输入接口。
3、需要Android API 18以上
4、录音时本地无法播放声音,也就是不能同时本地和远程都有声音
软件hook1、无硬件成本。
2、同时利于音视频同步编码
3、满足目前所有车载的系统版本。
1、需要root权限。
2、需要适配不同版本的Android系统,成本较高。
3、开发难度较大。
4、存在混音
理论100%
实际摸底:真实还原的混音效果的占比52%
usb音频1、现有的技术方案
2、无硬件成本
1、只有音频输出,没有音频输入。
2、只有在配件模式下支持adb和音频通信。
3、车机大部分不支持配件模式。
蓝牙接收1、同时支持音频的输入和输出1、pc端需要加个usb hub接入多个蓝牙接收器,pc端需要开发蓝牙接收转发程序。
2、部分车机对蓝牙驱动进行了定制,使蓝牙设备只能作为从设备,无法接入蓝牙耳机功能。
硬件转发1、跨平台(Android、Linux、QNX)
2、同时支持输入和输出。
3、不存在混音还原的问题。
1、部分车镜设备,由于集成封装性太强,没有暴露出可对接的线束。这类设备无法覆盖。
2、需要针对车机进行线束定制来实现整体的动态封装性
3、需要配套的硬件成本
摸底
前装100%
后装 58%


基于上述情况,考虑到车载的应用场景。最终我们的选型是 「软件 hook」 +「硬件转发」的组合方案。

5. 音频编码传输

关于音频编码传输这部分的内容,行业中已经有较成熟的解决方案,因此,这部分不展开篇幅讨论,我们仅针对一些方案做选型评估:


推流方案音频格式丢包情况延迟情况问题
rmtpaac/mp3无丢包有叠加延迟,延迟高 2-5s
httpflvaac/mp3无丢包有叠加延迟,延迟高 2-5s
rtpaac/mp3存在丢包可能测试环境 vlc软件:
1s延迟为 vlc网络缓冲1s。
通过vlc设置 ctrl+p --> 全部 --> 输入/编解码器 --> 网络缓存(ms) 改为100, 太小容易卡顿
浏览器不支持
webrtcopus存在丢包可能500ms需要配合webrtc服务端
少数部分浏览器不兼容


综上,从我们的应用场景以及高实时性要求考虑,最终选取了 webRtc 的方案。

6. 最终选型

结合音频的获取和写入以及整体编码传输的方案,最终的技术方案选型图如下:



对应流程图中,也顺带涵盖了远程画面传输的视频流优化的参考链路。

7. 总结

通过软硬件组合的方案来实现音频数据读写的能力,是一种基于特定背景条件下解决方案。但其基本推演的思路和策略,也是适用于手机平台的。而其中硬件的解决方案,理论上是适用于 Android、iOS、Linux、QNX 等平台设备的。


相较来说,手机的硬件转发成本更低。而对于软件的方案,实际的播报效果上仍会有很多细节问题,比如播报声音太小,需要对应设备去调节播报音量比例;出现延迟的场景,可能需要修改采样率;或是需要 hook 的自动化来降低成本等等。最终落地到项目中时,还需要考虑各方面的适配成本,确保整体的投入产出比。


2020-03-04 14:481517

评论

发布
暂无评论
发现更多内容

基于数组的有界阻塞队列 —— ArrayBlockingQueue

程序员小航

Java 源码 队列 源码阅读 JUC

实践案例丨利用小熊派开发板获取土壤湿度传感器的ADC值

华为云开发者联盟

物联网 IoT 传感

TensorFlow 篇 | TensorFlow 2.x 基于 HParams 的超参数调优

Alex

tensorflow keras hparams tensorboard 超参数调优

学习路线

hasWhere

Http自定义请求头接收不正确

hasWhere

在多架构时代,英特尔扩展高性能计算边界

E科讯

form表单提交get请求

hasWhere

如何避免option请求

hasWhere

c++ 杂谈3

菜鸟小sailor 🐕

《我在你床下》观后感

徐说科技

CICD实战——服务自动构建与部署

TARS基金会

DevOps 后端 jenkins CI/CD TARS

鸿蒙系统究竟是PPT秀还是有真材实料?鸿蒙HarmonyOS开发环境搭建与运行Demo

软测小生

华为 鸿蒙 HarmonyOS

onblur调用alert导致的死循环

hasWhere

java安全编码指南之:敏感类的拷贝

程序那些事

Java java安全编码 java安全 java安全编码指南

一文了解Zookeeper

Java旅途

kafka zookeeper 分布式

栈与队列简介

Java旅途

数据结构 队列

格式化报文输出

hasWhere

ARChatRoom功能介绍手册

anyRTC开发者

音视频 WebRTC 语音 RTC 安卓

架构师训练营第 1 期 第 2 周作业

李循律

极客大学架构师训练营

Java进阶教程、大厂面试真题、项目实战,GitHub上这14个开源项目屌炸天了!

Java架构之路

Java 程序员 面试 编程语言 项目实战

《转》Spring事务传播机制

hasWhere

高难度对话读书笔记—求助的勇气

wo是一棵草

LeetCode题解:641. 设计循环双端队列,使用队列,JavaScript,详细注释

Lee Chen

大前端 LeetCode

《转》POI的XWPFParagraph.getRuns分段问题

hasWhere

阿里P8大牛力荐Java程序员进阶必读的书籍清单(附电子版)

Java架构之路

Java 程序员 面试 编程语言 书籍推荐

揭秘App的财富密码,剖析算法工程师价值来源

峰池

人工智能 互联网 推荐算法 互联网公司

学习思路

hasWhere

Redis 缓存性能实践及总结

vivo互联网技术

redis redis集群 redis监控

数据提交

hasWhere

解读华为云原生数据库设计原则,打破传统数据库上云瓶颈

华为云开发者联盟

数据库 数据

清华架构师整理分布式系统文档:从实现原理到系统实现,收藏吧

小Q

Java 程序员 架构 分布式 微服务

云控平台的双向音频解决方案_文化 & 方法_高德技术_InfoQ精选文章