系统内录、录音、AudioSource.REMOTE_SUBMIX、AudioPlaybackCapture API、AOSP、Android源码、Android11
0. 背景介绍
01. 内录两种技术方案
音乐屏保其中一个音波律动样式,需要获取设备当前播放的Media类型音频流实现律动效果。结合Android官方API有两种方式可以实现:
1.系统应用使用AudioSource.REMOTE_SUBMIX方式,无需用户介入(下文统称为系统内录)
2.三方应用使用AudioPlaybackCapture API,需要用户授权(下文统称为Capture API)
两种方式在实际落地过程中都遇到阻塞问题:
1.系统内录,会截断蓝牙设备和扬声器的声音,在我们设备上具体现象是除了内置喇叭,其他通道的声音都会被截断。相关文章介绍很多,比如https://www.51cto.com/article/716095.html
2.Capture API,使用海外主流音乐应用播放时无法获取音频流,但使用国内QQ音乐、网易音乐测试可以正常录音
既然两种方式都有问题,综合用户体验,解决系统内录的优先级更高,本文后续也围绕系统内录方案展开。
02. 系统内录网上解决方案
方案1:网上搜索解决系统内录时截断音频流的问题,主要都是一种修改方式,比如https://juejin.cn/post/6844903705561530382,最早来源应该某个AOSP版本的改动,后续被各种引用,实际在Android11上不生效
方案2:https://mp.weixin.qq.com/s/PCaBiOmyoyCY1lhdo8ueDg 这篇公众号文章分析原理时也提出解决方案,比较靠谱,是最终解决方案中的一环。
综合网上可搜索到的资料,以及LLM给出的建议都无法最终解决,只能深入源码debug。
1. 源码Debug主要过程
1.1 从播放入手解决
通过网上相关原理介绍,方案2的改动是一个关键点,按照建议在Engine中增加Devices的内容,允许remoteSubmix设备和其他设备共存,但是录音时蓝牙设备还是不出声。
/services/audiopolicy/enginedefault/src/Engine.cpp
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 2edecf8e4e..72ea3f6bab 100755
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -371,16 +371,17 @@ DeviceVector Engine::getDevicesForStrategyInt(legacy_strategy strategy,
// FIXME: STRATEGY_REROUTING follow STRATEGY_MEDIA for now
case STRATEGY_REROUTING:
case STRATEGY_MEDIA: {
- DeviceVector devices2;
+ DeviceVector devices8;
if (strategy != STRATEGY_SONIFICATION) {
// no sonification on remote submix (e.g. WFD)
sp<DeviceDescriptor> remoteSubmix;
if ((remoteSubmix = availableOutputDevices.getDevice(
AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8("0"),
AUDIO_FORMAT_DEFAULT)) != nullptr) {
- devices2.add(remoteSubmix);
+ devices8.add(remoteSubmix);
}
}
+ DeviceVector devices2;
if (isInCall() && (strategy == STRATEGY_MEDIA)) {
devices = getDevicesForStrategyInt(
STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
@@ -426,6 +427,10 @@ DeviceVector Engine::getDevicesForStrategyInt(legacy_strategy strategy,
AUDIO_DEVICE_OUT_HDMI_ARC, AUDIO_DEVICE_OUT_SPDIF, AUDIO_DEVICE_OUT_AUX_LINE});
}
+ if (!devices8.isEmpty()) {
+ devices.add(devices8);
+ ALOGI("Thing devices.add(devices8) and size: %zd", devices.size());
+ }
devices2.add(devices3);
// device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or
// STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise
查看logcat日志,发现有E级别日志,audioserver以及三方应用都有报错,异常信息如下
2025-02-06 15:06:53.292 292-3245 AudioFlinger audioserver E createTrack() getOutputForAttr() return error -38 or invalid output handle
2025-02-06 15:06:53.293 292-6220 FMQ audioserver E grantorIdx must be less than 3
2025-02-06 15:06:53.293 1200-6030 IAudioFlinger com.demo.music E createTrack returned error -38
2025-02-06 15:06:53.293 1200-6030 AudioTrack com.demo.music E createTrack_l(60): AudioFlinger could not create track, status: -38 output 0
2025-02-06 15:06:53.293 1200-6030 AudioTrack com.demo.music W restoreTrack_l(60): failed status -38, retries 3
createTrack()出现异常,说明是output播放阶段出现的问题,按照createTrack的调用栈一路加日志调试,发现关键原因:当****remoteSubmix与其他设备共存时,****AudioPolicyManager::getOutputsForDevices 返回结果为空,导致后续createTrack异常,原因参考注释:
/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevices(
const DeviceVector &devices,
const SwAudioOutputCollection& openOutputs)
{
SortedVector<audio_io_handle_t> outputs;
ALOGVV("%s() devices %s", __func__, devices.toString().c_str());
for (size_t i = 0; i < openOutputs.size(); i++) {
ALOGVV("output %zu isDuplicated=%d device=%s",
i, openOutputs.valueAt(i)->isDuplicated(),
openOutputs.valueAt(i)->supportedDevices().toString().c_str());
//此处supportsAllDevices(devices)全部都返回false,outputs没有add设备,空集合返回
if (openOutputs.valueAt(i)->supportsAllDevices(devices)
&& openOutputs.valueAt(i)->devicesSupportEncodedFormats(devices.types())) {
ALOGVV("%s() found output %d", __func__, openOutputs.keyAt(i));
outputs.add(openOutputs.keyAt(i));
}
}
//返回了空集合
return outputs;
}
日志打印到此处,播放链路的关键问题已经定位,期间尝试了几种修改supportsAllDevices的方式,比如:
●方式1:修改audio_policy_configuration.xml,让output设备可以支持remotesubmix
●方式2:修改supportsAllDevices对应逻辑,不需要supportsAll,只要supportsAny就行
过程中使用了方式2进行解决,出现新的情况:录音时蓝牙耳机可以正常播放,但是录音出来的数据为空!由于修改的代码最终不需要,这里就不贴了。
1.2 从录音入手解决
既然录音时蓝牙耳机已经可以正常播放,问题需要调整方向从录音链路入手。录音链路问题排查比播放链路更棘手,因为从logcat日志并没有找到任何E级别或者error相关的信息。唯一比较明显的突破口是“原先系统内录时如果通过设备自带喇叭是可以正常播放的,也能正常录制,但是兼容remoteSubmix设备之后自带喇叭虽然能播放,但也无法录制”,于是一步步增加调试日志一行行对比“可以正常录音”与“无法正常录音”日志区别,最终在AudioPolicyManager::checkOutputsForDevice找到关键线索,相关改动如下:
services/audiopolicy/managerdefault/AudioPolicyManager.cpp
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManage
r.cpp
index a592dea65c..3ece410e46 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -4881,11 +4881,19 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor>& d
audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
//TODO: configure audio effect output stage here
+ DeviceVector devices = mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA
), nullptr, false /*fromCache*/);
+ sp<SwAudioOutputDescriptor> bestOutput = getOutputsForDevicesAnyNoDuplicate(devices, mOutputs);
+ if (bestOutput != nullptr) {
+ ALOGI("AR# bestOutput!!:%s", bestOutput->devices().toString().c_str());
+ } else {
+ bestOutput = mPrimaryOutput;
+ ALOGI("AR# hasPrimaryOutput!");
+ }
// open a duplicating output thread for the new output and the primary output
sp<SwAudioOutputDescriptor> dupOutputDesc =
new SwAudioOutputDescriptor(NULL, mpClientInterface);
- status_t status = dupOutputDesc->openDuplicating(mPrimaryOutput, desc,
+ status_t status = dupOutputDesc->openDuplicating(bestOutput, desc,
&duplicatedOutput);
主要改动逻辑是:修改替换mPrimaryOutput成当前最佳输出output设备进行duplicate操作。至此,播放链路和录音链路两处最关键的改动都已经完成。
1.3 改动总结
综上,Android11系统内录支持蓝牙/功能等通道的关键改动涉及播放和录音两个链路:
1.播放链路,Engine::getDevicesForStrategyInt返回当前所有设备时,允许remoteSubmix设备与蓝牙/功能等设备共存
2.录音链路,AudioPolicyManager::checkOutputsForDevice进行output通道duplicate操作时,替换为当前主输出通道,而不是固定的PrimaryOutput。当进行duplicate操作时,复制出来的虚拟设备会增加AUDIO_DEVICE_OUT_REMOTE_SUBMIX类型的支持,所以前面提到的supportsAllDevices问题也一并解决了。