audiopolicy: Add support for the MSD prototype
MSD is a Multi-Stream Decoder. It's an optional module
that allows mixing of encoded surround streams with
PCM streams.
This patch adds elementary support for setting up
a patch between the MSD module and actual output device,
and prioritizing the MSD module's device for output.
The patch is always set up, and is automatically
re-establishes itself if the actual output device changes.
Bug: 63901775
Test: MSD prototype
Change-Id: I9efa714455dae10b58bd5cc5cdf5557c0227b40c
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index f343bec..b31e396 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -31,6 +31,7 @@
#include <inttypes.h>
#include <math.h>
+#include <vector>
#include <AudioPolicyManagerInterface.h>
#include <AudioPolicyEngineInstance.h>
@@ -38,7 +39,6 @@
#include <utils/Log.h>
#include <media/AudioParameter.h>
#include <media/AudioPolicyHelper.h>
-#include <media/PatchBuilder.h>
#include <private/android_filesystem_config.h>
#include <soundtrigger/SoundTrigger.h>
#include <system/audio.h>
@@ -82,6 +82,16 @@
AUDIO_FORMAT_AAC_XHE,
};
+// Compressed formats for MSD module, ordered from most preferred to least preferred.
+static const std::vector<audio_format_t> compressedFormatsOrder = {{
+ AUDIO_FORMAT_MAT_2_1, AUDIO_FORMAT_MAT_2_0, AUDIO_FORMAT_E_AC3,
+ AUDIO_FORMAT_AC3, AUDIO_FORMAT_PCM_16_BIT }};
+// Channel masks for MSD module, 3D > 2D > 1D ordering (most preferred to least preferred).
+static const std::vector<audio_channel_mask_t> surroundChannelMasksOrder = {{
+ AUDIO_CHANNEL_OUT_3POINT1POINT2, AUDIO_CHANNEL_OUT_3POINT0POINT2,
+ AUDIO_CHANNEL_OUT_2POINT1POINT2, AUDIO_CHANNEL_OUT_2POINT0POINT2,
+ AUDIO_CHANNEL_OUT_5POINT1, AUDIO_CHANNEL_OUT_STEREO }};
+
// ----------------------------------------------------------------------------
// AudioPolicyInterface implementation
// ----------------------------------------------------------------------------
@@ -225,6 +235,7 @@
audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/);
updateCallRouting(newDevice);
}
+ const audio_devices_t msdOutDevice = getMsdAudioOutDeviceTypes();
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (desc != mPrimaryOutput)) {
@@ -232,7 +243,8 @@
// do not force device change on duplicated output because if device is 0, it will
// also force a device 0 for the two outputs it is duplicated to which may override
// a valid device selection on those outputs.
- bool force = !desc->isDuplicated()
+ bool force = (msdOutDevice == AUDIO_DEVICE_NONE || msdOutDevice != desc->device())
+ && !desc->isDuplicated()
&& (!device_distinguishes_on_address(device)
// always force when disconnecting (a non-duplicated device)
|| (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
@@ -525,7 +537,7 @@
}
sp<DeviceDescriptor> AudioPolicyManager::findDevice(
- const DeviceVector& devices, audio_devices_t device) {
+ const DeviceVector& devices, audio_devices_t device) const {
DeviceVector deviceList = devices.getDevicesFromTypeMask(device);
ALOG_ASSERT(!deviceList.isEmpty(),
"%s() selected device type %#x is not in devices list", __func__, device);
@@ -777,6 +789,7 @@
routing_strategy strategy;
audio_devices_t device;
audio_port_handle_t requestedDeviceId = *selectedDeviceId;
+ audio_devices_t msdDevice = getMsdAudioOutDeviceTypes();
if (attr != NULL) {
if (!isValidAttributes(attr)) {
@@ -869,7 +882,20 @@
"flags %#x",
device, config->sample_rate, config->format, config->channel_mask, *flags);
- *output = getOutputForDevice(device, session, *stream, config, flags);
+ *output = AUDIO_IO_HANDLE_NONE;
+ if (msdDevice != AUDIO_DEVICE_NONE) {
+ *output = getOutputForDevice(msdDevice, session, *stream, config, flags);
+ if (*output != AUDIO_IO_HANDLE_NONE && setMsdPatch(device) == NO_ERROR) {
+ ALOGV("%s() Using MSD device 0x%x instead of device 0x%x",
+ __func__, msdDevice, device);
+ device = msdDevice;
+ } else {
+ *output = AUDIO_IO_HANDLE_NONE;
+ }
+ }
+ if (*output == AUDIO_IO_HANDLE_NONE) {
+ *output = getOutputForDevice(device, session, *stream, config, flags);
+ }
if (*output == AUDIO_IO_HANDLE_NONE) {
mOutputRoutes.removeRoute(session);
return INVALID_OPERATION;
@@ -1052,6 +1078,164 @@
return output;
}
+sp<DeviceDescriptor> AudioPolicyManager::getMsdAudioInDevice() const {
+ sp<HwModule> msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD);
+ if (msdModule != 0) {
+ DeviceVector msdInputDevices = mAvailableInputDevices.getDevicesFromHwModule(
+ msdModule->getHandle());
+ if (!msdInputDevices.isEmpty()) return msdInputDevices.itemAt(0);
+ }
+ return 0;
+}
+
+audio_devices_t AudioPolicyManager::getMsdAudioOutDeviceTypes() const {
+ sp<HwModule> msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD);
+ if (msdModule != 0) {
+ return mAvailableOutputDevices.getDeviceTypesFromHwModule(msdModule->getHandle());
+ }
+ return AUDIO_DEVICE_NONE;
+}
+
+const AudioPatchCollection AudioPolicyManager::getMsdPatches() const {
+ AudioPatchCollection msdPatches;
+ audio_module_handle_t msdModuleHandle = mHwModules.getModuleFromName(
+ AUDIO_HARDWARE_MODULE_ID_MSD)->getHandle();
+ if (msdModuleHandle == AUDIO_MODULE_HANDLE_NONE) return msdPatches;
+ for (size_t i = 0; i < mAudioPatches.size(); ++i) {
+ sp<AudioPatch> patch = mAudioPatches.valueAt(i);
+ for (size_t j = 0; j < patch->mPatch.num_sources; ++j) {
+ const struct audio_port_config *source = &patch->mPatch.sources[j];
+ if (source->type == AUDIO_PORT_TYPE_DEVICE &&
+ source->ext.device.hw_module == msdModuleHandle) {
+ msdPatches.addAudioPatch(patch->mHandle, patch);
+ }
+ }
+ }
+ return msdPatches;
+}
+
+status_t AudioPolicyManager::getBestMsdAudioProfileFor(audio_devices_t outputDevice,
+ bool hwAvSync, audio_port_config *sourceConfig, audio_port_config *sinkConfig) const
+{
+ sp<HwModule> msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD);
+ if (msdModule == nullptr) {
+ ALOGE("%s() unable to get MSD module", __func__);
+ return NO_INIT;
+ }
+ sp<HwModule> deviceModule = mHwModules.getModuleForDevice(outputDevice);
+ if (deviceModule == nullptr) {
+ ALOGE("%s() unable to get module for %#x", __func__, outputDevice);
+ return NO_INIT;
+ }
+ const InputProfileCollection &inputProfiles = msdModule->getInputProfiles();
+ if (inputProfiles.isEmpty()) {
+ ALOGE("%s() no input profiles for MSD module", __func__);
+ return NO_INIT;
+ }
+ const OutputProfileCollection &outputProfiles = deviceModule->getOutputProfiles();
+ if (outputProfiles.isEmpty()) {
+ ALOGE("%s() no output profiles for device %#x", __func__, outputDevice);
+ return NO_INIT;
+ }
+ AudioProfileVector msdProfiles;
+ // Each IOProfile represents a MixPort from audio_policy_configuration.xml
+ for (const auto &inProfile : inputProfiles) {
+ if (hwAvSync == ((inProfile->getFlags() & AUDIO_INPUT_FLAG_HW_AV_SYNC) != 0)) {
+ msdProfiles.appendVector(inProfile->getAudioProfiles());
+ }
+ }
+ AudioProfileVector deviceProfiles;
+ for (const auto &outProfile : outputProfiles) {
+ if (hwAvSync == ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0)) {
+ deviceProfiles.appendVector(outProfile->getAudioProfiles());
+ }
+ }
+ struct audio_config_base bestSinkConfig;
+ status_t result = msdProfiles.findBestMatchingOutputConfig(deviceProfiles,
+ compressedFormatsOrder, surroundChannelMasksOrder, true /*preferHigherSamplingRates*/,
+ &bestSinkConfig);
+ if (result != NO_ERROR) {
+ return result;
+ ALOGD("%s() no matching profiles found for device: %#x, hwAvSync: %d",
+ __func__, outputDevice, hwAvSync);
+ }
+ sinkConfig->sample_rate = bestSinkConfig.sample_rate;
+ sinkConfig->channel_mask = bestSinkConfig.channel_mask;
+ sinkConfig->format = bestSinkConfig.format;
+ // For encoded streams force direct flag to prevent downstream mixing.
+ sinkConfig->flags.output = static_cast<audio_output_flags_t>(
+ sinkConfig->flags.output | AUDIO_OUTPUT_FLAG_DIRECT);
+ sourceConfig->sample_rate = bestSinkConfig.sample_rate;
+ // Specify exact channel mask to prevent guessing by bit count in PatchPanel.
+ sourceConfig->channel_mask = audio_channel_mask_out_to_in(bestSinkConfig.channel_mask);
+ sourceConfig->format = bestSinkConfig.format;
+ // Copy input stream directly without any processing (e.g. resampling).
+ sourceConfig->flags.input = static_cast<audio_input_flags_t>(
+ sourceConfig->flags.input | AUDIO_INPUT_FLAG_DIRECT);
+ if (hwAvSync) {
+ sinkConfig->flags.output = static_cast<audio_output_flags_t>(
+ sinkConfig->flags.output | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
+ sourceConfig->flags.input = static_cast<audio_input_flags_t>(
+ sourceConfig->flags.input | AUDIO_INPUT_FLAG_HW_AV_SYNC);
+ }
+ const unsigned int config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE |
+ AUDIO_PORT_CONFIG_CHANNEL_MASK | AUDIO_PORT_CONFIG_FORMAT | AUDIO_PORT_CONFIG_FLAGS;
+ sinkConfig->config_mask |= config_mask;
+ sourceConfig->config_mask |= config_mask;
+ return NO_ERROR;
+}
+
+PatchBuilder AudioPolicyManager::buildMsdPatch(audio_devices_t outputDevice) const
+{
+ PatchBuilder patchBuilder;
+ patchBuilder.addSource(getMsdAudioInDevice()).
+ addSink(findDevice(mAvailableOutputDevices, outputDevice));
+ audio_port_config sourceConfig = patchBuilder.patch()->sources[0];
+ audio_port_config sinkConfig = patchBuilder.patch()->sinks[0];
+ // TODO: Figure out whether MSD module has HW_AV_SYNC flag set in the AP config file.
+ // For now, we just forcefully try with HwAvSync first.
+ status_t res = getBestMsdAudioProfileFor(outputDevice, true /*hwAvSync*/,
+ &sourceConfig, &sinkConfig) == NO_ERROR ? NO_ERROR :
+ getBestMsdAudioProfileFor(
+ outputDevice, false /*hwAvSync*/, &sourceConfig, &sinkConfig);
+ if (res == NO_ERROR) {
+ // Found a matching profile for encoded audio. Re-create PatchBuilder with this config.
+ return (PatchBuilder()).addSource(sourceConfig).addSink(sinkConfig);
+ }
+ ALOGV("%s() no matching profile found. Fall through to default PCM patch"
+ " supporting PCM format conversion.", __func__);
+ return patchBuilder;
+}
+
+status_t AudioPolicyManager::setMsdPatch(audio_devices_t outputDevice) {
+ ALOGV("%s() for outputDevice %#x", __func__, outputDevice);
+ if (outputDevice == AUDIO_DEVICE_NONE) {
+ // Use media strategy for unspecified output device. This should only
+ // occur on checkForDeviceAndOutputChanges(). Device connection events may
+ // therefore invalidate explicit routing requests.
+ outputDevice = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/);
+ }
+ PatchBuilder patchBuilder = buildMsdPatch(outputDevice);
+ const struct audio_patch* patch = patchBuilder.patch();
+ const AudioPatchCollection msdPatches = getMsdPatches();
+ if (!msdPatches.isEmpty()) {
+ LOG_ALWAYS_FATAL_IF(msdPatches.size() > 1,
+ "The current MSD prototype only supports one output patch");
+ sp<AudioPatch> currentPatch = msdPatches.valueAt(0);
+ if (audio_patches_are_equal(¤tPatch->mPatch, patch)) {
+ return NO_ERROR;
+ }
+ releaseAudioPatch(currentPatch->mHandle, mUidCached);
+ }
+ status_t status = installPatch(__func__, -1 /*index*/, nullptr /*patchHandle*/,
+ patch, 0 /*delayMs*/, mUidCached, nullptr /*patchDescPtr*/);
+ ALOGE_IF(status != NO_ERROR, "%s() error %d creating MSD audio patch", __func__, status);
+ ALOGI_IF(status == NO_ERROR, "%s() Patch created from MSD_IN to "
+ "device:%#x (format:%#x channels:%#x samplerate:%d)", __func__, outputDevice,
+ patch->sources[0].format, patch->sources[0].channel_mask, patch->sources[0].sample_rate);
+ return status;
+}
+
audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_handle_t>& outputs,
audio_output_flags_t flags,
audio_format_t format)
@@ -4600,19 +4784,17 @@
return outputs;
}
-void AudioPolicyManager::checkForDeviceAndOutputChanges()
-{
- checkForDeviceAndOutputChanges([](){ return false; });
-}
-
void AudioPolicyManager::checkForDeviceAndOutputChanges(std::function<bool()> onOutputsChecked)
{
// checkA2dpSuspend must run before checkOutputForAllStrategies so that A2DP
// output is suspended before any tracks are moved to it
checkA2dpSuspend();
checkOutputForAllStrategies();
- if (onOutputsChecked()) checkA2dpSuspend();
+ if (onOutputsChecked != nullptr && onOutputsChecked()) checkA2dpSuspend();
updateDevicesAndOutputs();
+ if (mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD) != 0) {
+ setMsdPatch();
+ }
}
void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy)