patch顾名思义路径,audiopatch就是构建一条声音流的路径。为什么要有这样的一条路径?
可以从两个方面考虑
降低音频数据的传输延时,比如一个场景耳返也就是录音的数据(麦克分收音)需要重新播放的耳机里面。 假设框架里面没有一条从录音直接到放音的路径,那么数据需要从框架录制到应用,然后从应用重新拷贝到播放,而有了audiopatch之后 框架里面就可以直接将录音的数据写到输出设备了。
支持外部设备的放音,比如FM收音机,声音从FM设备进来 可以通过hal层建立的patch,直接输出到设备中。
在audioflinger 中每种情况路径连接都可以定义为一个patch 路径。
代码位置:packages/services/Car/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/CarAudioZoneInputFragment.java
在AAOS的kitchensink测试项中的CarAudioZoneInpuFragment有使用patch来创建录音到播放的路径:
int result = AudioManager.createAudioPatch(patch,
new AudioPortConfig[] { sourceConfig },
new AudioPortConfig[] { sinkConfig });
首先调用carAudioService的createAudioPatch,传递的是录音设备的地址和播放的usage media、其中录音设备的地址 也就是patch 中的source设备是作为输入,而patch中的sink 输出只要根据usage找到可以播的sink设备即可。
CarAudioService 需要根据sourceAddress构建 AudioPortConfig sinkConfig 和sourceConfig,这两个Config都是通过audioPolicyManager的getDevice返回的结构体中通过address/usage进行筛选构建的的,然后调用AudioManager的createAudioPatch 。
AudioMannger中会调用AudioSystem的createAudioPatch,这个通过jni调用,在jni 中会把sinkConfig 和 sourceConfig转换为path结构体中的sources 和sinks,并传递的handle指针。
AudioSystem会通过binder一路调用到AudioPolicyMangaer的createAudioPatchInternal
AudioPolicyMangaer会调用到patchPanel的createAudioPatch,在这边会为sink创建playthread 为source创建recordthread。同时为这两个线程创建软件bridge
软件的bridge创建好之后 就调用track start 将track 启动,也就构造了一个录音设备录制的声音会写入到播放设备的路径的测试。
代码位置:
frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
frameworks/av/media/libmedia/include/media/PatchBuilder.h
frameworks/av/services/audioflinger/PatchPanel.cpp
在AudioPolicyManager打开输出的设备后 在setOutputDevice 会为每对deviceport和mixport创建一个patch。这边的patch创建时为mixport流和deviceport 设备之间构建一个数据的通路。
PatchBuilder patchBuilder;
patchBuilder.addSource(outputDesc);
ALOG_ASSERT(filteredDevices.size() <= AUDIO_PATCH_PORTS_MAX, "Too many sink ports");
for (const auto &filteredDevice : filteredDevices) {
patchBuilder.addSink(filteredDevice);
}
installPatch(__func__, patchHandle, outputDesc.get(), patchBuilder.patch(),
(muteWaitMs == 0 && outputDesc->isActive()) ? (delayMs + (outputDesc->latency() / 2)) : delayMs);
其中addSource 和addSink的实现,具体在PatchBuilder.h中
template
audio_port_config& add(const sp& entity) {
audio_port_config* added = advance();
entity->toAudioPortConfig(added);
return *added;
}
PortCfgs sinks() { return PortCfgs(&mPatch.num_sinks, mPatch.sinks); }
PortCfgs sources() { return PortCfgs(&mPatch.num_sources, mPatch.sources}
installpatch的执行流程
creatPatch流程
首先根据传递audio_patch_handle_t, 确认当前存储mPatches是不是已经有了,有的话要先把patch的连接remove, 然后断开连接。
对于sink输出是device类型的 调用audioflinger的 openOutput_l创建一个播放的线程,并加入到newPatch.mPlayback.
sp thread = mAudioFlinger.openOutput_l(
patch->sinks[0].ext.device.hw_module,
&output,
&config,
&mixerConfig,
outputDevice,
outputDeviceAddress,
flags);
ALOGE("mAudioFlinger.openOutput_l() returned %p", thread.get());
if (thread == 0) {
status = NO_MEMORY;
goto exit;
}
newPatch.mPlayback.setThread(reinterpret_cast(thread.get()));
// start capture and playback
mRecord.track()->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE);
mPlayback.track()->start();
硬件实现方式 通过alsa的控件实现, 根据sink 和source的type的不同创建的流程也不一样。
这两个类型是patchBuilder addSource 和 addSink 将加入的sink和source转换为port 时设置的。
addsource 参数对应的类是SwAudioOutputDescriptor
然后会调用其对应的toAudioPortConfig 将source赋值为AUDIO_PORT_TYPE_MIX
addSink 参数对应的类是DeviceVector会调用
DeviceDescriptorBase 的toAudioPortConfig 将sink赋值为AUDIO_PORT_TYPE_DEVICE
也就是TYPE_DEVICE 可以理解为音频xml定义的deviceport, 而TYPE_MIX理解为之前定义的mixport。
SwAudioOutputDescriptor类型的port转换:
(const sp& outputDesc,
const DeviceVector &devices
DeviceVector filteredDevices = outputDesc->filterSupportedDevices(devices);
patchBuilder.addSource(outputDesc);
patchBuilder.addSink(filteredDevice);
void AudioOutputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig,
const struct audio_port_config *srcConfig) const
{
dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN;
if (srcConfig != NULL) {
dstConfig->config_mask |= srcConfig->config_mask;
}
AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig);
toPolicyAudioPortConfig(dstConfig, srcConfig);
dstConfig->role = AUDIO_PORT_ROLE_SOURCE;
dstConfig->type = AUDIO_PORT_TYPE_MIX;
dstConfig->ext.mix.hw_module = getModuleHandle();
dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
}
device类型的toPort 转换
void DeviceDescriptorBase::toAudioPortConfig(struct audio_port_config *dstConfig,
const struct audio_port_config *srcConfig) const
{
dstConfig->config_mask = AUDIO_PORT_CONFIG_GAIN;
if (mSamplingRate != 0) {
dstConfig->config_mask |= AUDIO_PORT_CONFIG_SAMPLE_RATE;
}
if (mChannelMask != AUDIO_CHANNEL_NONE) {
dstConfig->config_mask |= AUDIO_PORT_CONFIG_CHANNEL_MASK;
}
if (mFormat != AUDIO_FORMAT_INVALID) {
dstConfig->config_mask |= AUDIO_PORT_CONFIG_FORMAT;
}
if (srcConfig != NULL) {
dstConfig->config_mask |= srcConfig->config_mask;
}
AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig);
dstConfig->role = audio_is_output_device(mDeviceTypeAddr.mType) ?
AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE;
dstConfig->type = AUDIO_PORT_TYPE_DEVICE;
dstConfig->ext.device.type = mDeviceTypeAddr.mType;
(void)audio_utils_strlcpy_zerofill(dstConfig->ext.device.address, mDeviceTypeAddr.getAddress());
}