audio policy: create spatializer mixer only when needed
Only open a spatializer output when spatial audio is enabled
in order to save memory resources consumed by the dedicated mixer and
audio effect when instantiated.
For the special case where the spatializer output is the only route
available to a specific device, leave it open. This can happen when the
software necoder path is used for Bluetooth A2DP.
Bug: 227740874
Test: make and music playback with and without spatial audio
Change-Id: Id149d0ce000c939c7f45e63b47f2fd1d7a98995a
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index fdbbe1b..49a0dde 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -287,9 +287,12 @@
sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(output);
// close unused outputs after device disconnection or direct outputs that have
// been opened by checkOutputsForDevice() to query dynamic parameters
+ // "outputs" vector never contains duplicated outputs
if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE)
|| (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) &&
- (desc->mDirectOpenCount == 0))) {
+ (desc->mDirectOpenCount == 0))
+ || (((desc->mFlags & AUDIO_OUTPUT_FLAG_SPATIALIZER) != 0) &&
+ !isOutputOnlyAvailableRouteToSomeDevice(desc))) {
clearAudioSourcesForOutput(output);
closeOutput(output);
}
@@ -5361,6 +5364,29 @@
}
}
+
+bool AudioPolicyManager::isOutputOnlyAvailableRouteToSomeDevice(
+ const sp<SwAudioOutputDescriptor>& outputDesc) {
+ if (outputDesc->isDuplicated()) {
+ return false;
+ }
+ DeviceVector devices = outputDesc->supportedDevices();
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ if (desc == outputDesc || desc->isDuplicated()) {
+ continue;
+ }
+ DeviceVector sharedDevices = desc->filterSupportedDevices(devices);
+ if (!sharedDevices.isEmpty()
+ && (desc->devicesSupportEncodedFormats(sharedDevices.types())
+ == outputDesc->devicesSupportEncodedFormats(sharedDevices.types()))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
status_t AudioPolicyManager::getSpatializerOutput(const audio_config_base_t *mixerConfig,
const audio_attributes_t *attr,
audio_io_handle_t *output) {
@@ -5376,80 +5402,67 @@
}
if (!canBeSpatializedInt(
attr, configPtr, devicesTypeAddress)) {
- ALOGW("%s provided attributes or mixer config cannot be spatialized", __func__);
+ ALOGV("%s provided attributes or mixer config cannot be spatialized", __func__);
return BAD_VALUE;
}
sp<IOProfile> profile =
getSpatializerOutputProfile(configPtr, devicesTypeAddress);
if (profile == nullptr) {
- ALOGW("%s no suitable output profile for provided attributes or mixer config", __func__);
+ ALOGV("%s no suitable output profile for provided attributes or mixer config", __func__);
return BAD_VALUE;
}
- if (mSpatializerOutput != nullptr && mSpatializerOutput->mProfile == profile
- && configPtr != nullptr
- && configPtr->channel_mask == mSpatializerOutput->mMixerChannelMask) {
- *output = mSpatializerOutput->mIoHandle;
- ALOGV("%s returns current spatializer output %d", __func__, *output);
- return NO_ERROR;
- }
- mSpatializerOutput.clear();
+ std::vector<sp<SwAudioOutputDescriptor>> spatializerOutputs;
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
- if (!desc->isDuplicated() && desc->mProfile == profile) {
- ALOGV("%s found output %d for spatializer profile", __func__, desc->mIoHandle);
- mSpatializerOutput = desc;
- break;
+ if (!desc->isDuplicated()
+ && (desc->mFlags & AUDIO_OUTPUT_FLAG_SPATIALIZER) != 0) {
+ spatializerOutputs.push_back(desc);
+ ALOGV("%s adding opened spatializer Output %d", __func__, desc->mIoHandle);
}
}
- if (mSpatializerOutput == nullptr) {
- ALOGW("%s no opened spatializer output for profile %s",
- __func__, profile->getName().c_str());
- return BAD_VALUE;
+ mSpatializerOutput.clear();
+ bool outputsChanged = false;
+ for (const auto& desc : spatializerOutputs) {
+ if (desc->mProfile == profile
+ && (configPtr == nullptr
+ || configPtr->channel_mask == desc->mMixerChannelMask)) {
+ mSpatializerOutput = desc;
+ ALOGV("%s reusing current spatializer output %d", __func__, desc->mIoHandle);
+ } else {
+ ALOGV("%s closing spatializerOutput output %d to match channel mask %#x"
+ " and devices %s", __func__, desc->mIoHandle,
+ configPtr != nullptr ? configPtr->channel_mask : 0,
+ devices.toString().c_str());
+ closeOutput(desc->mIoHandle);
+ outputsChanged = true;
+ }
}
- if (configPtr != nullptr
- && configPtr->channel_mask != mSpatializerOutput->mMixerChannelMask) {
- audio_config_base_t savedMixerConfig = {
- .sample_rate = mSpatializerOutput->getSamplingRate(),
- .format = mSpatializerOutput->getFormat(),
- .channel_mask = mSpatializerOutput->mMixerChannelMask,
- };
- DeviceVector savedDevices = mSpatializerOutput->devices();
-
- ALOGV("%s reopening spatializer output to match channel mask %#x (current mask %#x)",
- __func__, configPtr->channel_mask, mSpatializerOutput->mMixerChannelMask);
-
- closeOutput(mSpatializerOutput->mIoHandle);
- //from now on mSpatializerOutput is null
-
+ if (mSpatializerOutput == nullptr) {
sp<SwAudioOutputDescriptor> desc =
openOutputWithProfileAndDevice(profile, devices, mixerConfig);
- if (desc == nullptr) {
- // re open the spatializer output with previous channel mask
- desc = openOutputWithProfileAndDevice(profile, savedDevices, &savedMixerConfig);
- if (desc == nullptr) {
- ALOGE("%s failed to restore mSpatializerOutput with previous config", __func__);
- } else {
- mSpatializerOutput = desc;
- }
- mPreviousOutputs = mOutputs;
- mpClientInterface->onAudioPortListUpdate();
- *output = AUDIO_IO_HANDLE_NONE;
- ALOGW("%s could not open spatializer output with requested config", __func__);
- return BAD_VALUE;
+ if (desc != nullptr) {
+ mSpatializerOutput = desc;
+ outputsChanged = true;
}
- mSpatializerOutput = desc;
- mPreviousOutputs = mOutputs;
- mpClientInterface->onAudioPortListUpdate();
}
checkVirtualizerClientRoutes();
+ if (outputsChanged) {
+ mPreviousOutputs = mOutputs;
+ mpClientInterface->onAudioPortListUpdate();
+ }
+
+ if (mSpatializerOutput == nullptr) {
+ ALOGV("%s could not open spatializer output with requested config", __func__);
+ return BAD_VALUE;
+ }
*output = mSpatializerOutput->mIoHandle;
- ALOGV("%s returns new spatializer output %d", __func__, *output);
- return NO_ERROR;
+ ALOGV("%s returning new spatializer output %d", __func__, *output);
+ return OK;
}
status_t AudioPolicyManager::releaseSpatializerOutput(audio_io_handle_t output) {
@@ -5460,9 +5473,12 @@
return BAD_VALUE;
}
- mSpatializerOutput.clear();
-
- checkVirtualizerClientRoutes();
+ if (!isOutputOnlyAvailableRouteToSomeDevice(mSpatializerOutput)) {
+ ALOGV("%s closing spatializer output %d", __func__, mSpatializerOutput->mIoHandle);
+ closeOutput(mSpatializerOutput->mIoHandle);
+ //from now on mSpatializerOutput is null
+ checkVirtualizerClientRoutes();
+ }
return NO_ERROR;
}
@@ -5739,6 +5755,21 @@
inputDesc->close();
}
}
+
+ // Check if spatializer outputs can be closed until used.
+ // mOutputs vector never contains duplicated outputs at this point.
+ std::vector<audio_io_handle_t> outputsClosed;
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ if ((desc->mFlags & AUDIO_OUTPUT_FLAG_SPATIALIZER) != 0
+ && !isOutputOnlyAvailableRouteToSomeDevice(desc)) {
+ outputsClosed.push_back(desc->mIoHandle);
+ desc->close();
+ }
+ }
+ for (auto output : outputsClosed) {
+ removeOutput(output);
+ }
}
void AudioPolicyManager::addOutput(audio_io_handle_t output,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 715ee6d..0d9b5bf 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -1097,6 +1097,16 @@
void checkVirtualizerClientRoutes();
/**
+ * @brief Returns true if at least one device can only be reached via the output passed
+ * as argument. Always returns false for duplicated outputs.
+ * This can be used to decide if an output can be closed without forbidding
+ * playback to any given device.
+ * @param outputDesc the output to consider
+ * @return true if at least one device can only be reached via the output.
+ */
+ bool isOutputOnlyAvailableRouteToSomeDevice(const sp<SwAudioOutputDescriptor>& outputDesc);
+
+ /**
* @brief getInputForDevice selects an input handle for a given input device and
* requester context
* @param device to be used by requester, selected by policy mix rules or engine