diff options
author | Eric Laurent <elaurent@google.com> | 2014-08-08 15:13:39 -0700 |
---|---|---|
committer | Eric Laurent <elaurent@google.com> | 2014-08-08 17:07:31 -0700 |
commit | 874c4287a4e49c59ac88767751dce00fcd3edb73 (patch) | |
tree | 2a0e4adc0bd18f26c8488b74d7e3df9751d493df | |
parent | 0912a5738d6baf2df7cd62e877240e3807b4b21f (diff) | |
download | frameworks_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.cpp | 64 | ||||
-rw-r--r-- | services/audiopolicy/AudioPolicyManager.cpp | 146 |
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 |