summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2014-08-08 15:13:39 -0700
committerEric Laurent <elaurent@google.com>2014-08-08 17:07:31 -0700
commit874c4287a4e49c59ac88767751dce00fcd3edb73 (patch)
tree2a0e4adc0bd18f26c8488b74d7e3df9751d493df
parent0912a5738d6baf2df7cd62e877240e3807b4b21f (diff)
downloadframeworks_av-874c4287a4e49c59ac88767751dce00fcd3edb73.zip
frameworks_av-874c4287a4e49c59ac88767751dce00fcd3edb73.tar.gz
frameworks_av-874c4287a4e49c59ac88767751dce00fcd3edb73.tar.bz2
audio policy: enable more than one sink per audio patch.
Allow creation of audio patches with more than one sink. More than one sink is enabled when: - Connecting an input device to output devices on the same audio HW module. - Connecting an output mix to output devices on the same audio HA module. All other patches are limited to one sink. Bug: 16879363. Change-Id: I95be6948ef29df64e51e5b8ace38c2db7f3e89f2
-rw-r--r--services/audioflinger/PatchPanel.cpp64
-rw-r--r--services/audiopolicy/AudioPolicyManager.cpp146
2 files changed, 126 insertions, 84 deletions
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 49422a9..2d0a25f 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -151,12 +151,15 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
if (handle == NULL || patch == NULL) {
return BAD_VALUE;
}
- // limit number of sources to 1 for now or 2 sources for special cross hw module case.
- // only the audio policy manager can request a patch creation with 2 sources.
- if (patch->num_sources == 0 || patch->num_sources > 2 ||
+ if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX ||
patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) {
return BAD_VALUE;
}
+ // limit number of sources to 1 for now or 2 sources for special cross hw module case.
+ // only the audio policy manager can request a patch creation with 2 sources.
+ if (patch->num_sources > 2) {
+ return INVALID_OPERATION;
+ }
if (*handle != AUDIO_PATCH_HANDLE_NONE) {
for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) {
@@ -173,20 +176,22 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
switch (patch->sources[0].type) {
case AUDIO_PORT_TYPE_DEVICE: {
- // limit number of sinks to 1 for now
- if (patch->num_sinks > 1) {
- status = BAD_VALUE;
- goto exit;
- }
- audio_module_handle_t src_module = patch->sources[0].ext.device.hw_module;
- ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+ audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module;
+ ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
if (index < 0) {
- ALOGW("createAudioPatch() bad src hw module %d", src_module);
+ ALOGW("createAudioPatch() bad src hw module %d", srcModule);
status = BAD_VALUE;
goto exit;
}
AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
for (unsigned int i = 0; i < patch->num_sinks; i++) {
+ // support only one sink if connection to a mix or across HW modules
+ if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX ||
+ patch->sinks[i].ext.mix.hw_module != srcModule) &&
+ patch->num_sinks > 1) {
+ status = INVALID_OPERATION;
+ goto exit;
+ }
// reject connection to different sink types
if (patch->sinks[i].type != patch->sinks[0].type) {
ALOGW("createAudioPatch() different sink types in same patch not supported");
@@ -194,7 +199,7 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
goto exit;
}
// limit to connections between devices and input streams for HAL before 3.0
- if (patch->sinks[i].ext.mix.hw_module == src_module &&
+ if (patch->sinks[i].ext.mix.hw_module == srcModule &&
(audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) &&
(patch->sinks[i].type != AUDIO_PORT_TYPE_MIX)) {
ALOGW("createAudioPatch() invalid sink type %d for device source",
@@ -204,7 +209,7 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
}
}
- if (patch->sinks[0].ext.device.hw_module != src_module) {
+ if (patch->sinks[0].ext.device.hw_module != srcModule) {
// limit to device to device connection if not on same hw module
if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) {
ALOGW("createAudioPatch() invalid sink type for cross hw module");
@@ -258,7 +263,7 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
config.channel_mask = inChannelMask;
config.format = newPatch->mPlaybackThread->format();
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
- newPatch->mRecordThread = audioflinger->openInput_l(src_module,
+ newPatch->mRecordThread = audioflinger->openInput_l(srcModule,
&input,
&config,
device,
@@ -279,10 +284,10 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
- patch->sinks[0].ext.mix.handle);
+ patch->sinks[0].ext.mix.handle);
if (thread == 0) {
ALOGW("createAudioPatch() bad capture I/O handle %d",
- patch->sinks[0].ext.mix.handle);
+ patch->sinks[0].ext.mix.handle);
status = BAD_VALUE;
goto exit;
}
@@ -298,10 +303,10 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
}
} else {
sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
- patch->sinks[0].ext.mix.handle);
+ patch->sinks[0].ext.mix.handle);
if (thread == 0) {
ALOGW("createAudioPatch() bad capture I/O handle %d",
- patch->sinks[0].ext.mix.handle);
+ patch->sinks[0].ext.mix.handle);
status = BAD_VALUE;
goto exit;
}
@@ -326,10 +331,10 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
}
} break;
case AUDIO_PORT_TYPE_MIX: {
- audio_module_handle_t src_module = patch->sources[0].ext.mix.hw_module;
- ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+ audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module;
+ ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
if (index < 0) {
- ALOGW("createAudioPatch() bad src hw module %d", src_module);
+ ALOGW("createAudioPatch() bad src hw module %d", srcModule);
status = BAD_VALUE;
goto exit;
}
@@ -342,7 +347,7 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
goto exit;
}
// limit to connections between sinks and sources on same HW module
- if (patch->sinks[i].ext.device.hw_module != src_module) {
+ if (patch->sinks[i].ext.device.hw_module != srcModule) {
status = BAD_VALUE;
goto exit;
}
@@ -365,6 +370,7 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
}
char *address;
if (strcmp(patch->sinks[0].ext.device.address, "") != 0) {
+ //FIXME: we only support address on first sink with HAL version < 3.0
address = audio_device_address_to_parameter(
patch->sinks[0].ext.device.type,
patch->sinks[0].ext.device.address);
@@ -562,16 +568,16 @@ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle
switch (patch->sources[0].type) {
case AUDIO_PORT_TYPE_DEVICE: {
- audio_module_handle_t src_module = patch->sources[0].ext.device.hw_module;
- ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+ audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module;
+ ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
if (index < 0) {
- ALOGW("releaseAudioPatch() bad src hw module %d", src_module);
+ ALOGW("releaseAudioPatch() bad src hw module %d", srcModule);
status = BAD_VALUE;
break;
}
if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE &&
- patch->sinks[0].ext.device.hw_module != src_module) {
+ patch->sinks[0].ext.device.hw_module != srcModule) {
clearPatchConnections(removedPatch);
break;
}
@@ -609,10 +615,10 @@ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle
}
} break;
case AUDIO_PORT_TYPE_MIX: {
- audio_module_handle_t src_module = patch->sources[0].ext.mix.hw_module;
- ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+ audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module;
+ ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
if (index < 0) {
- ALOGW("releaseAudioPatch() bad src hw module %d", src_module);
+ ALOGW("releaseAudioPatch() bad src hw module %d", srcModule);
status = BAD_VALUE;
break;
}
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index f95b839..850fe86 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -2078,22 +2078,37 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
}
ALOGV("createAudioPatch() num sources %d num sinks %d", patch->num_sources, patch->num_sinks);
- if (patch->num_sources > 1 || patch->num_sinks > 1) {
+ if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX ||
+ patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) {
+ return BAD_VALUE;
+ }
+ // only one source per audio patch supported for now
+ if (patch->num_sources > 1) {
return INVALID_OPERATION;
}
- if (patch->sources[0].role != AUDIO_PORT_ROLE_SOURCE ||
- patch->sinks[0].role != AUDIO_PORT_ROLE_SINK) {
+
+ if (patch->sources[0].role != AUDIO_PORT_ROLE_SOURCE) {
return INVALID_OPERATION;
}
+ for (size_t i = 0; i < patch->num_sinks; i++) {
+ if (patch->sinks[i].role != AUDIO_PORT_ROLE_SINK) {
+ return INVALID_OPERATION;
+ }
+ }
sp<AudioPatch> patchDesc;
ssize_t index = mAudioPatches.indexOfKey(*handle);
- ALOGV("createAudioPatch sink id %d role %d type %d", patch->sinks[0].id, patch->sinks[0].role,
- patch->sinks[0].type);
ALOGV("createAudioPatch source id %d role %d type %d", patch->sources[0].id,
patch->sources[0].role,
patch->sources[0].type);
+#if LOG_NDEBUG == 0
+ for (size_t i = 0; i < patch->num_sinks; i++) {
+ ALOGV("createAudioPatch sink %d: id %d role %d type %d", i, patch->sinks[i].id,
+ patch->sinks[i].role,
+ patch->sinks[i].type);
+ }
+#endif
if (index >= 0) {
patchDesc = mAudioPatches.valueAt(index);
@@ -2107,12 +2122,6 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
}
if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
- // TODO add support for mix to mix connection
- if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) {
- ALOGV("createAudioPatch() source mix sink not device");
- return BAD_VALUE;
- }
- // output mix to output device connection
sp<AudioOutputDescriptor> outputDesc = getOutputFromId(patch->sources[0].id);
if (outputDesc == NULL) {
ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id);
@@ -2127,30 +2136,41 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
return BAD_VALUE;
}
}
- sp<DeviceDescriptor> devDesc =
- mAvailableOutputDevices.getDeviceFromId(patch->sinks[0].id);
- if (devDesc == 0) {
- ALOGV("createAudioPatch() out device not found for id %d", patch->sinks[0].id);
- return BAD_VALUE;
- }
+ DeviceVector devices;
+ for (size_t i = 0; i < patch->num_sinks; i++) {
+ // Only support mix to devices connection
+ // TODO add support for mix to mix connection
+ if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
+ ALOGV("createAudioPatch() source mix but sink is not a device");
+ return INVALID_OPERATION;
+ }
+ sp<DeviceDescriptor> devDesc =
+ mAvailableOutputDevices.getDeviceFromId(patch->sinks[i].id);
+ if (devDesc == 0) {
+ ALOGV("createAudioPatch() out device not found for id %d", patch->sinks[i].id);
+ return BAD_VALUE;
+ }
- if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mDeviceType,
- patch->sources[0].sample_rate,
- NULL, // updatedSamplingRate
- patch->sources[0].format,
- patch->sources[0].channel_mask,
- AUDIO_OUTPUT_FLAG_NONE /*FIXME*/)) {
- ALOGV("createAudioPatch() profile not supported");
+ if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mDeviceType,
+ patch->sources[0].sample_rate,
+ NULL, // updatedSamplingRate
+ patch->sources[0].format,
+ patch->sources[0].channel_mask,
+ AUDIO_OUTPUT_FLAG_NONE /*FIXME*/)) {
+ ALOGV("createAudioPatch() profile not supported for device %08x",
+ devDesc->mDeviceType);
+ return INVALID_OPERATION;
+ }
+ devices.add(devDesc);
+ }
+ if (devices.size() == 0) {
return INVALID_OPERATION;
}
+
// TODO: reconfigure output format and channels here
ALOGV("createAudioPatch() setting device %08x on output %d",
- devDesc->mDeviceType, outputDesc->mIoHandle);
- setOutputDevice(outputDesc->mIoHandle,
- devDesc->mDeviceType,
- true,
- 0,
- handle);
+ devices.types(), outputDesc->mIoHandle);
+ setOutputDevice(outputDesc->mIoHandle, devices.types(), true, 0, handle);
index = mAudioPatches.indexOfKey(*handle);
if (index >= 0) {
if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
@@ -2166,6 +2186,10 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
} else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
// input device to input mix connection
+ // only one sink supported when connecting an input device to a mix
+ if (patch->num_sinks > 1) {
+ return INVALID_OPERATION;
+ }
sp<AudioInputDescriptor> inputDesc = getInputFromId(patch->sinks[0].id);
if (inputDesc == NULL) {
return BAD_VALUE;
@@ -2195,10 +2219,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
// TODO: reconfigure output format and channels here
ALOGV("createAudioPatch() setting device %08x on output %d",
devDesc->mDeviceType, inputDesc->mIoHandle);
- setInputDevice(inputDesc->mIoHandle,
- devDesc->mDeviceType,
- true,
- handle);
+ setInputDevice(inputDesc->mIoHandle, devDesc->mDeviceType, true, handle);
index = mAudioPatches.indexOfKey(*handle);
if (index >= 0) {
if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
@@ -2214,38 +2235,53 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
} else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
// device to device connection
if (patchDesc != 0) {
- if (patchDesc->mPatch.sources[0].id != patch->sources[0].id &&
- patchDesc->mPatch.sinks[0].id != patch->sinks[0].id) {
+ if (patchDesc->mPatch.sources[0].id != patch->sources[0].id) {
return BAD_VALUE;
}
}
-
sp<DeviceDescriptor> srcDeviceDesc =
mAvailableInputDevices.getDeviceFromId(patch->sources[0].id);
- sp<DeviceDescriptor> sinkDeviceDesc =
- mAvailableOutputDevices.getDeviceFromId(patch->sinks[0].id);
- if (srcDeviceDesc == 0 || sinkDeviceDesc == 0) {
- return BAD_VALUE;
- }
+
//update source and sink with our own data as the data passed in the patch may
// be incomplete.
struct audio_patch newPatch = *patch;
srcDeviceDesc->toAudioPortConfig(&newPatch.sources[0], &patch->sources[0]);
- sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[0], &patch->sinks[0]);
-
- if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) {
- SortedVector<audio_io_handle_t> outputs =
- getOutputsForDevice(sinkDeviceDesc->mDeviceType, mOutputs);
- // if the sink device is reachable via an opened output stream, request to go via
- // this output stream by adding a second source to the patch description
- audio_io_handle_t output = selectOutput(outputs, AUDIO_OUTPUT_FLAG_NONE);
- if (output != AUDIO_IO_HANDLE_NONE) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
- if (outputDesc->isDuplicated()) {
+ if (srcDeviceDesc == 0) {
+ return BAD_VALUE;
+ }
+
+ for (size_t i = 0; i < patch->num_sinks; i++) {
+ if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
+ ALOGV("createAudioPatch() source device but one sink is not a device");
+ return INVALID_OPERATION;
+ }
+
+ sp<DeviceDescriptor> sinkDeviceDesc =
+ mAvailableOutputDevices.getDeviceFromId(patch->sinks[i].id);
+ if (sinkDeviceDesc == 0) {
+ return BAD_VALUE;
+ }
+ sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[i], &patch->sinks[i]);
+
+ if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) {
+ // only one sink supported when connected devices across HW modules
+ if (patch->num_sinks > 1) {
return INVALID_OPERATION;
}
- outputDesc->toAudioPortConfig(&newPatch.sources[1], &patch->sources[0]);
- newPatch.num_sources = 2;
+ SortedVector<audio_io_handle_t> outputs =
+ getOutputsForDevice(sinkDeviceDesc->mDeviceType,
+ mOutputs);
+ // if the sink device is reachable via an opened output stream, request to go via
+ // this output stream by adding a second source to the patch description
+ audio_io_handle_t output = selectOutput(outputs, AUDIO_OUTPUT_FLAG_NONE);
+ if (output != AUDIO_IO_HANDLE_NONE) {
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+ if (outputDesc->isDuplicated()) {
+ return INVALID_OPERATION;
+ }
+ outputDesc->toAudioPortConfig(&newPatch.sources[1], &patch->sources[0]);
+ newPatch.num_sources = 2;
+ }
}
}
// TODO: check from routing capabilities in config file and other conflicting patches