audio policy: add support for virtualizer stage output
Add support for specialized output stream supporting virtualizer stage
effect with headtracking.
The HAL implementation indicates support for this feature with a
specific audio profile with flag AUDIO_OUTPUT_FLAG_VIRTUALIZER_STAGE
listed in the audio policy configuration file.
Note: the flag combination DEEP_BUFFER + FAST is used until a new audio
HAL version is introduced with this flag defined.
The audio policy manager implements methods to query if virtualization
is supported for a particular combination of audio attrbutes, audio
configuration and audio devices.
Methods are also available for the audio policy service to open and close
a specialized virtualization output stream and create the corresponding
audio mixer in audio flinger.
Bug: 188502620
Test: atest audiopolicy_tests
Change-Id: I3b1faf22290775dc7d13953a24098d22cb3790de
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index d6dd762..321285f 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -246,9 +246,11 @@
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
- if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) ||
- (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) &&
- (desc->mDirectOpenCount == 0))) {
+ if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE)
+ || (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) &&
+ (desc->mDirectOpenCount == 0))
+ || (((desc->mFlags & AUDIO_OUTPUT_FLAG_VIRTUALIZER_STAGE) != 0) &&
+ (desc != mVirtualizerStageOutput))) {
clearAudioSourcesForOutput(output);
closeOutput(output);
}
@@ -925,6 +927,36 @@
return profile;
}
+sp<IOProfile> AudioPolicyManager::getVirtualizerStageOutputProfile(
+ const audio_config_t *config __unused, const AudioDeviceTypeAddrVector &devices,
+ bool forOpening) const
+{
+ for (const auto& hwModule : mHwModules) {
+ for (const auto& curProfile : hwModule->getOutputProfiles()) {
+ if (curProfile->getFlags() != AUDIO_OUTPUT_FLAG_VIRTUALIZER_STAGE) {
+ continue;
+ }
+ // reject profiles not corresponding to a device currently available
+ DeviceVector supportedDevices = curProfile->getSupportedDevices();
+ if (!mAvailableOutputDevices.containsAtLeastOne(supportedDevices)) {
+ continue;
+ }
+ if (!devices.empty()) {
+ if (supportedDevices.getDevicesFromDeviceTypeAddrVec(devices).size()
+ != devices.size()) {
+ continue;
+ }
+ }
+ if (forOpening && !curProfile->canOpenNewIo()) {
+ continue;
+ }
+ ALOGV("%s found profile %s", __func__, curProfile->getName().c_str());
+ return curProfile;
+ }
+ }
+ return nullptr;
+}
+
audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream)
{
DeviceVector devices = mEngine->getOutputDevicesForStream(stream, false /*fromCache*/);
@@ -1094,7 +1126,7 @@
*output = AUDIO_IO_HANDLE_NONE;
if (!msdDevices.isEmpty()) {
- *output = getOutputForDevices(msdDevices, session, *stream, config, flags);
+ *output = getOutputForDevices(msdDevices, session, resultAttr, config, flags);
if (*output != AUDIO_IO_HANDLE_NONE && setMsdOutputPatches(&outputDevices) == NO_ERROR) {
ALOGV("%s() Using MSD devices %s instead of devices %s",
__func__, msdDevices.toString().c_str(), outputDevices.toString().c_str());
@@ -1103,7 +1135,7 @@
}
}
if (*output == AUDIO_IO_HANDLE_NONE) {
- *output = getOutputForDevices(outputDevices, session, *stream, config,
+ *output = getOutputForDevices(outputDevices, session, resultAttr, config,
flags, resultAttr->flags & AUDIO_FLAG_MUTE_HAPTIC);
}
if (*output == AUDIO_IO_HANDLE_NONE) {
@@ -1301,7 +1333,7 @@
audio_io_handle_t AudioPolicyManager::getOutputForDevices(
const DeviceVector &devices,
audio_session_t session,
- audio_stream_type_t stream,
+ const audio_attributes_t *attr,
const audio_config_t *config,
audio_output_flags_t *flags,
bool forceMutingHaptic)
@@ -1323,6 +1355,9 @@
if ((*flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) {
*flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_DIRECT);
}
+
+ audio_stream_type_t stream = mEngine->getStreamTypeForAttributes(*attr);
+
// only allow deep buffering for music stream type
if (stream != AUDIO_STREAM_MUSIC) {
*flags = (audio_output_flags_t)(*flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER);
@@ -1342,6 +1377,11 @@
ALOGV("Set VoIP and Direct output flags for PCM format");
}
+ if (mVirtualizerStageOutput != nullptr
+ && canBeVirtualized(attr, config, devices.toTypeAddrVector())) {
+ return mVirtualizerStageOutput->mIoHandle;
+ }
+
audio_config_t directConfig = *config;
directConfig.channel_mask = channelMask;
status_t status = openDirectOutput(stream, session, &directConfig, *flags, devices, &output);
@@ -4803,6 +4843,136 @@
return source;
}
+bool AudioPolicyManager::canBeVirtualized(const audio_attributes_t *attr,
+ const audio_config_t *config,
+ const AudioDeviceTypeAddrVector &devices) const
+{
+ // The caller can have the audio attributes criteria ignored by either passing a null ptr or
+ // the AUDIO_ATTRIBUTES_INITIALIZER value.
+ // If attributes are specified, current policy is to only allow virtualization for media
+ // and game usages.
+ if (attr != nullptr && *attr != AUDIO_ATTRIBUTES_INITIALIZER &&
+ attr->usage != AUDIO_USAGE_MEDIA && attr->usage != AUDIO_USAGE_GAME) {
+ return false;
+ }
+
+ // The caller can have the devices criteria ignored by passing and empty vector, and
+ // getVirtualizerStageOutputProfile() will ignore the devices when looking for a match.
+ // Otherwise an output profile supporting a virtualizer stage effect that can be routed
+ // to the specified devices must exist.
+ sp<IOProfile> profile =
+ getVirtualizerStageOutputProfile(config, devices, false /*forOpening*/);
+ if (profile == nullptr) {
+ return false;
+ }
+
+ // The caller can have the audio config criteria ignored by either passing a null ptr or
+ // the AUDIO_CONFIG_INITIALIZER value.
+ // If an audio config is specified, current policy is to only allow virtualization for
+ // 5.1, 7.1and 7.1.4 audio.
+ // If the virtualizer stage output is already opened, only channel masks included in the
+ // virtualizer stage output mixer channel mask are allowed.
+ if (config != nullptr && *config != AUDIO_CONFIG_INITIALIZER) {
+ if (config->channel_mask != AUDIO_CHANNEL_OUT_5POINT1
+ && config->channel_mask != AUDIO_CHANNEL_OUT_7POINT1
+ && config->channel_mask != AUDIO_CHANNEL_OUT_7POINT1POINT4) {
+ return false;
+ }
+ if (mVirtualizerStageOutput != nullptr) {
+ if ((config->channel_mask & mVirtualizerStageOutput->mMixerChannelMask)
+ != config->channel_mask) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void AudioPolicyManager::checkVirtualizerClientRoutes() {
+ std::set<audio_stream_type_t> streamsToInvalidate;
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ const sp<SwAudioOutputDescriptor>& outputDescriptor = mOutputs[i];
+ for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
+ audio_attributes_t attr = client->attributes();
+ DeviceVector devices = mEngine->getOutputDevicesForAttributes(attr, nullptr, false);
+ AudioDeviceTypeAddrVector devicesTypeAddress = devices.toTypeAddrVector();
+ audio_config_base_t clientConfig = client->config();
+ audio_config_t config = audio_config_initializer(&clientConfig);
+ if (canBeVirtualized(&attr, &config, devicesTypeAddress)) {
+ streamsToInvalidate.insert(client->stream());
+ }
+ }
+ }
+
+ for (audio_stream_type_t stream : streamsToInvalidate) {
+ mpClientInterface->invalidateStream(stream);
+ }
+}
+
+status_t AudioPolicyManager::getVirtualizerStageOutput(const audio_config_base_t *mixerConfig,
+ const audio_attributes_t *attr,
+ audio_io_handle_t *output) {
+ *output = AUDIO_IO_HANDLE_NONE;
+
+ if (mVirtualizerStageOutput != nullptr) {
+ return INVALID_OPERATION;
+ }
+
+ DeviceVector devices = mEngine->getOutputDevicesForAttributes(*attr, nullptr, false);
+ AudioDeviceTypeAddrVector devicesTypeAddress = devices.toTypeAddrVector();
+ audio_config_t *configPtr = nullptr;
+ audio_config_t config;
+ if (mixerConfig != nullptr) {
+ config = audio_config_initializer(mixerConfig);
+ configPtr = &config;
+ }
+ if (!canBeVirtualized(attr, configPtr, devicesTypeAddress)) {
+ return BAD_VALUE;
+ }
+
+ sp<IOProfile> profile =
+ getVirtualizerStageOutputProfile(configPtr, devicesTypeAddress, true /*forOpening*/);
+ if (profile == nullptr) {
+ return BAD_VALUE;
+ }
+
+ mVirtualizerStageOutput = new SwAudioOutputDescriptor(profile, mpClientInterface);
+ status_t status = mVirtualizerStageOutput->open(nullptr, mixerConfig, devices,
+ mEngine->getStreamTypeForAttributes(*attr),
+ AUDIO_OUTPUT_FLAG_VIRTUALIZER_STAGE, output);
+ if (status != NO_ERROR) {
+ ALOGV("%s failed opening output: status %d, output %d", __func__, status, *output);
+ if (*output != AUDIO_IO_HANDLE_NONE) {
+ mVirtualizerStageOutput->close();
+ }
+ mVirtualizerStageOutput.clear();
+ *output = AUDIO_IO_HANDLE_NONE;
+ return status;
+ }
+
+ checkVirtualizerClientRoutes();
+
+ addOutput(*output, mVirtualizerStageOutput);
+ mPreviousOutputs = mOutputs;
+ mpClientInterface->onAudioPortListUpdate();
+
+ ALOGV("%s returns new virtualizer stage output %d", __func__, *output);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::releaseVirtualizerStageOutput(audio_io_handle_t output) {
+ if (mVirtualizerStageOutput == nullptr) {
+ return INVALID_OPERATION;
+ }
+ if (mVirtualizerStageOutput->mIoHandle != output) {
+ return BAD_VALUE;
+ }
+ closeOutput(output);
+ mVirtualizerStageOutput.clear();
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
// AudioPolicyManager
// ----------------------------------------------------------------------------
@@ -4852,6 +5022,8 @@
ALOGE("could not load audio policy configuration file, setting defaults");
getConfig().setDefault();
}
+ //TODO: b/193496180 use virtualizer stage flag at audio HAL when available
+ getConfig().convertVirtualizerStageFlag();
}
status_t AudioPolicyManager::initialize() {
@@ -5014,7 +5186,8 @@
outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
mPrimaryOutput = outputDesc;
}
- if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
+ if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0
+ || (outProfile->getFlags() & AUDIO_OUTPUT_FLAG_VIRTUALIZER_STAGE) != 0 ) {
outputDesc->close();
} else {
addOutput(output, outputDesc);