概述

这里以蓝牙耳机连接手机这一场景为例分析Audio路由策略是如何进行设备切换和管理输出的。蓝牙耳机连接上Android系统后,AudioService的handleDeviceConnection会被调用,然后调用到AudioPolicyManager的核心函数setDeviceConnectionState。

时序图

474846_202004291104530597768467.jpg

说明:连接过程的setDeviceConnectionState可以拆分为以下几个流程:

1.添加设备到mAvailableOutputDevices。

2.通知所有module有新设备连接。

3.创建dup output。

4.invalidate指定的流。

setDeviceConnectionStateInt函数

handleDeviceConnection被调用之后,从时序图可以看出AudioPolicyManager的setDeviceConnectionStateInt会被调用。这里来分析这个函数。

474846_202004291107070552_large.jpgcheckOutputsForDevice是这个非常重要的函数,现在来分析这个函数的第一个阶段

474846_202004291113070396_large.jpg这里我之所以省略了部分代码,是因为这个过程我们前面已经做过类似的分析了Android 8.1 Audio框架(一)初始化分析。我们知道了checkOutputsForDevice前面的处理会找到1个profile,0个output。module和profile的信息我用dumpsys media.audio_policy打印出来给你们看下并作解释

474846_202004291114230833_large.jpg我们来继续看checkOutputsForDevice函数的第二个阶段

474846_202004291115420755_large.jpg可以看出checkOutputsForDevice的第二个阶段就是创建SwAudioOutputDescriptor,然后根据config还有其他参数打开output。这个过程在Android 8.1 Audio框架(一)初始化分析已经有做过分析了,就不做赘述。接下来就是checkOutputsForDevice的第三阶段了,重点来了。

474846_202004291118050068_large.jpg在checkOutputsForDevice的第二阶段的时候,已经为这个a2dp profile创建了一个output,在Android 8.1 Audio框架(一)初始化分析我们已经知道openOutput操作会创建一个MixerThread,它继承于PlaybackThread,用于调用混音器,然后播放混音后的数据到hal设备。所以到了第三阶段的时候已经有两个PlaybackThread,一个是开机初始化的时候为primary创建的,一个是连接蓝牙耳机后为a2dp output创建的PlaybackThread。我们知道PlaybackThread的作用就是把混音之后的pcm数据送给底层hal设备的播放的。在android系统中经常会有一些特殊的用户场景,比如在戴耳机的时候,有些声音必须同时从耳机还有扬声器中输出。基于这种用户场景,DuplicatingThread就诞生了,它继承于MixerThread,可以看出DuplicatingThread就是MixerThread的一个wrapper。我们知道PlaybackThread在播放混音之后的数据就是通过threadLoop_write这个函数往hal写数据的,我们先来看看DuplicatingThread的threadLoop_write会做什么:

474846_202004291119240943_large.jpg我们可以看出DuplicatingThread的threadLoop_write没有像PlaybackThread的threadLoop_write一样有类似mNormalSink->write一样的往hal写音频数据的操作,它是往outputTracks里是write数据的。在这里我们可以大胆的猜测:DuplicatingThread拥有两个outputTracks,一个是给primary的,一个是给a2dp output的。这样通过DuplicatingThread就可以同时让primary还有a2dp out出声音了。

现在我们来具体分析openDuplicateOutput函数,看看我们的猜想是不是对的。

474846_202004291121040255_large.jpg来看看DuplicatingThread的构造函数

474846_202004291122330865_large.jpg看来DuplicatingThread在构造的时候已经为线程thread1创建了一个OutputTrack。然后另外一个是在thread->addOutputTrack(thread2);中创建的。

到了这里我们已经分析完checkOutputsForDevice的三个阶段了,现在返回到setDeviceConnectionStateInt函数,继续分析。

474846_202004291124150490_large.jpgcheckOutputForAllStrategies为每一个strategy调用checkOutputForStrategy,又会调用getDeviceForStrategy去获得对应的audio_devices_t。getDeviceForStrategy会调用mEngine->getDeviceForStrategy(strategy)。来看看这个函数会做什么

474846_202004291126090693_large.jpg

474846_202004291127280193_large.jpg可以看出checkOutputForStrategy最终会调用到getDeviceForStrategyInt,它会根据strategy类型返回不同的device。这样就可以让不同的流走不同的通路了,比如我music流只走a2dp output,来电铃声AUDIO_STREAM_RING走primary和a2dp output同时出声。

474846_202004291130020349_large.jpg来看看这个invalidateStream的过程

474846_202004291131380974_large.jpg可以看出这个invalidateStream的操作就是设置对应Track的状态。比如我要invalidate music流,那么对应的Track就会被设置为CBLK_INVALID,然后AudioTrack在AudioTrack::obtainBuffer、obtainBuffer或者getPosition的时候会检测到CBLK_INVALID这个flag,然后调用restoreTrack_l,restoreTrack_l会调用createTrack_l重新创建track。这样新的track就会按照预期输出音频了。

原文链接:https://blog.csdn.net/qq_27136111/java/article/details/97246184

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐