From 83b8808faad1e91690c64d7007348be8d9ebde73 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 20 Jun 2014 18:31:16 -0700 Subject: audio flinger: add patch connection between hw modules Add support for audio device connections between different audio hw modules. The patch is performed by creating a bridge between the playback thread connected to the sink device and the record thread connected to the source device using a pair of specialized PlaybackTrack and RecordTrack. - Added PatchTrack and PatchRecord classes. - Added TrackBase type to indicate more clearly the track behavior. - A TrackBase can allocate the buffer or reuse an existing one. - Factored some code in openOutput() and openInput() for internal use by PatchPanel. Bug: 14815883. Change-Id: Ib9515fcda864610458a4bc81fa8f59096ff4d7db --- include/private/media/AudioTrackShared.h | 14 +- services/audioflinger/AudioFlinger.cpp | 227 +++++++++++------- services/audioflinger/AudioFlinger.h | 21 +- services/audioflinger/PatchPanel.cpp | 352 +++++++++++++++++++++++----- services/audioflinger/PatchPanel.h | 30 ++- services/audioflinger/PlaybackTracks.h | 42 +++- services/audioflinger/RecordTracks.h | 35 ++- services/audioflinger/Threads.cpp | 90 +++++-- services/audioflinger/Threads.h | 15 +- services/audioflinger/TrackBase.h | 35 ++- services/audioflinger/Tracks.cpp | 224 +++++++++++++++--- services/audiopolicy/AudioPolicyManager.cpp | 38 ++- 12 files changed, 890 insertions(+), 233 deletions(-) diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h index 5116d1e..fa1b20a 100644 --- a/include/private/media/AudioTrackShared.h +++ b/include/private/media/AudioTrackShared.h @@ -175,12 +175,11 @@ protected: // Proxy seen by AudioTrack client and AudioRecord client class ClientProxy : public Proxy { -protected: +public: ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize, bool isOut, bool clientInServer); virtual ~ClientProxy() { } -public: static const struct timespec kForever; static const struct timespec kNonBlocking; @@ -394,8 +393,10 @@ protected: class AudioTrackServerProxy : public ServerProxy { public: AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, - size_t frameSize, bool clientInServer = false) - : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer) { } + size_t frameSize, bool clientInServer = false, uint32_t sampleRate = 0) + : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer) { + mCblk->mSampleRate = sampleRate; + } protected: virtual ~AudioTrackServerProxy() { } @@ -458,9 +459,8 @@ private: class AudioRecordServerProxy : public ServerProxy { public: AudioRecordServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, - size_t frameSize) - : ServerProxy(cblk, buffers, frameCount, frameSize, false /*isOut*/, - false /*clientInServer*/) { } + size_t frameSize, bool clientInServer) + : ServerProxy(cblk, buffers, frameCount, frameSize, false /*isOut*/, clientInServer) { } protected: virtual ~AudioRecordServerProxy() { } }; diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 1ad6285..8bf709e 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1531,7 +1531,7 @@ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name) } audio_module_handle_t handle = nextUniqueId(); - mAudioHwDevs.add(handle, new AudioHwDevice(name, dev, flags)); + mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags)); ALOGI("loadHwModule() Loaded %s audio interface from %s (%s) handle %d", name, dev->common.module->name, dev->common.module->id, handle); @@ -1575,41 +1575,13 @@ status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice) // ---------------------------------------------------------------------------- -audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, - audio_devices_t *pDevices, - uint32_t *pSamplingRate, - audio_format_t *pFormat, - audio_channel_mask_t *pChannelMask, - uint32_t *pLatencyMs, - audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo) -{ - struct audio_config config; - memset(&config, 0, sizeof(config)); - config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0; - config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0; - config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT; - if (offloadInfo != NULL) { - config.offload_info = *offloadInfo; - } - - ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %#08x, Channels %x, flags %x", - module, - (pDevices != NULL) ? *pDevices : 0, - config.sample_rate, - config.format, - config.channel_mask, - flags); - ALOGV("openOutput(), offloadInfo %p version 0x%04x", - offloadInfo, offloadInfo == NULL ? -1 : offloadInfo->version); - - if (pDevices == NULL || *pDevices == AUDIO_DEVICE_NONE) { - return AUDIO_IO_HANDLE_NONE; - } - - Mutex::Autolock _l(mLock); - AudioHwDevice *outHwDev = findSuitableHwDev_l(module, *pDevices); +sp AudioFlinger::openOutput_l(audio_module_handle_t module, + audio_devices_t device, + struct audio_config *config, + audio_output_flags_t flags) +{ + AudioHwDevice *outHwDev = findSuitableHwDev_l(module, device); if (outHwDev == NULL) { return AUDIO_IO_HANDLE_NONE; } @@ -1635,18 +1607,18 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, status_t status = hwDevHal->open_output_stream(hwDevHal, id, - *pDevices, - (audio_output_flags_t)flags, - &config, + device, + flags, + config, &outStream); mHardwareStatus = AUDIO_HW_IDLE; - ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %#08x, " + ALOGV("openOutput_l() openOutputStream returned output %p, SamplingRate %d, Format %#08x, " "Channels %x, status %d", outStream, - config.sample_rate, - config.format, - config.channel_mask, + config->sample_rate, + config->format, + config->channel_mask, status); if (status == NO_ERROR && outStream != NULL) { @@ -1654,19 +1626,60 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, PlaybackThread *thread; if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { - thread = new OffloadThread(this, output, id, *pDevices); + thread = new OffloadThread(this, output, id, device); ALOGV("openOutput() created offload output: ID %d thread %p", id, thread); } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) - || !isValidPcmSinkFormat(config.format) - || (config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) { - thread = new DirectOutputThread(this, output, id, *pDevices); + || !isValidPcmSinkFormat(config->format) + || (config->channel_mask != AUDIO_CHANNEL_OUT_STEREO)) { + thread = new DirectOutputThread(this, output, id, device); ALOGV("openOutput() created direct output: ID %d thread %p", id, thread); } else { - thread = new MixerThread(this, output, id, *pDevices); + thread = new MixerThread(this, output, id, device); ALOGV("openOutput() created mixer output: ID %d thread %p", id, thread); } mPlaybackThreads.add(id, thread); + return thread; + } + + return 0; +} + +audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + struct audio_config config; + memset(&config, 0, sizeof(config)); + config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0; + config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0; + config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT; + if (offloadInfo != NULL) { + config.offload_info = *offloadInfo; + } + ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %#08x, Channels %x, flags %x", + module, + (pDevices != NULL) ? *pDevices : 0, + config.sample_rate, + config.format, + config.channel_mask, + flags); + ALOGV("openOutput(), offloadInfo %p version 0x%04x", + offloadInfo, offloadInfo == NULL ? -1 : offloadInfo->version); + + if (pDevices == NULL || *pDevices == AUDIO_DEVICE_NONE) { + return AUDIO_IO_HANDLE_NONE; + } + + Mutex::Autolock _l(mLock); + + sp thread = openOutput_l(module, *pDevices, &config, flags); + if (thread != 0) { if (pSamplingRate != NULL) { *pSamplingRate = config.sample_rate; } @@ -1686,16 +1699,16 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, // the first primary output opened designates the primary hw device if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) { ALOGI("Using module %d has the primary audio interface", module); - mPrimaryHardwareDev = outHwDev; + mPrimaryHardwareDev = thread->getOutput()->audioHwDev; AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_HW_SET_MODE; - hwDevHal->set_mode(hwDevHal, mMode); + mPrimaryHardwareDev->hwDevice()->set_mode(mPrimaryHardwareDev->hwDevice(), mMode); mHardwareStatus = AUDIO_HW_IDLE; mPrimaryOutputSampleRate = config.sample_rate; } - return id; + return thread->id(); } return AUDIO_IO_HANDLE_NONE; @@ -1776,15 +1789,29 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output) // but the ThreadBase container still exists. if (thread->type() != ThreadBase::DUPLICATING) { - AudioStreamOut *out = thread->clearOutput(); - ALOG_ASSERT(out != NULL, "out shouldn't be NULL"); - // from now on thread->mOutput is NULL - out->hwDev()->close_output_stream(out->hwDev(), out->stream); - delete out; + closeOutputFinish(thread); } + + thread.clear(); return NO_ERROR; } +void AudioFlinger::closeOutputFinish(sp thread) +{ + AudioStreamOut *out = thread->clearOutput(); + ALOG_ASSERT(out != NULL, "out shouldn't be NULL"); + // from now on thread->mOutput is NULL + out->hwDev()->close_output_stream(out->hwDev(), out->stream); + delete out; +} + +void AudioFlinger::closeOutputInternal_l(sp thread) +{ + mPlaybackThreads.removeItem(thread->mId); + thread->exit(); + closeOutputFinish(thread); +} + status_t AudioFlinger::suspendOutput(audio_io_handle_t output) { Mutex::Autolock _l(mLock); @@ -1823,6 +1850,12 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, audio_channel_mask_t *pChannelMask, audio_input_flags_t flags) { + Mutex::Autolock _l(mLock); + + if (pDevices == NULL || *pDevices == AUDIO_DEVICE_NONE) { + return AUDIO_IO_HANDLE_NONE; + } + struct audio_config config; memset(&config, 0, sizeof(config)); config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0; @@ -1833,13 +1866,36 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, audio_format_t reqFormat = config.format; audio_channel_mask_t reqChannelMask = config.channel_mask; - if (pDevices == NULL || *pDevices == AUDIO_DEVICE_NONE) { - return 0; + sp thread = openInput_l(module, *pDevices, &config, flags); + + if (thread != 0) { + if (pSamplingRate != NULL) { + *pSamplingRate = reqSamplingRate; + } + if (pFormat != NULL) { + *pFormat = config.format; + } + if (pChannelMask != NULL) { + *pChannelMask = reqChannelMask; + } + + // notify client processes of the new input creation + thread->audioConfigChanged(AudioSystem::INPUT_OPENED); + return thread->id(); } + return AUDIO_IO_HANDLE_NONE; +} - Mutex::Autolock _l(mLock); +sp AudioFlinger::openInput_l(audio_module_handle_t module, + audio_devices_t device, + struct audio_config *config, + audio_input_flags_t flags) +{ + uint32_t reqSamplingRate = config->sample_rate; + audio_format_t reqFormat = config->format; + audio_channel_mask_t reqChannelMask = config->channel_mask; - AudioHwDevice *inHwDev = findSuitableHwDev_l(module, *pDevices); + AudioHwDevice *inHwDev = findSuitableHwDev_l(module, device); if (inHwDev == NULL) { return 0; } @@ -1848,14 +1904,14 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, audio_io_handle_t id = nextUniqueId(); audio_stream_in_t *inStream = NULL; - status_t status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, + status_t status = inHwHal->open_input_stream(inHwHal, id, device, config, &inStream, flags); ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %#x, Channels %x, " "flags %#x, status %d", inStream, - config.sample_rate, - config.format, - config.channel_mask, + config->sample_rate, + config->format, + config->channel_mask, flags, status); @@ -1863,14 +1919,14 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, // conversion internally, try to open again with the proposed parameters. The AudioFlinger can // resample the input and do mono to stereo or stereo to mono conversions on 16 bit PCM inputs. if (status == BAD_VALUE && - reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT && - (config.sample_rate <= 2 * reqSamplingRate) && - (audio_channel_count_from_in_mask(config.channel_mask) <= FCC_2) && + reqFormat == config->format && config->format == AUDIO_FORMAT_PCM_16_BIT && + (config->sample_rate <= 2 * reqSamplingRate) && + (audio_channel_count_from_in_mask(config->channel_mask) <= FCC_2) && (audio_channel_count_from_in_mask(reqChannelMask) <= FCC_2)) { // FIXME describe the change proposed by HAL (save old values so we can log them here) ALOGV("openInput() reopening with proposed sampling rate and channel mask"); inStream = NULL; - status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, &inStream, flags); + status = inHwHal->open_input_stream(inHwHal, id, device, config, &inStream, flags); // FIXME log this new status; HAL should not propose any further changes } @@ -1931,30 +1987,18 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, // Start record thread // RecordThread requires both input and output device indication to forward to audio // pre processing modules - RecordThread *thread = new RecordThread(this, + sp thread = new RecordThread(this, input, id, primaryOutputDevice_l(), - *pDevices + device #ifdef TEE_SINK , teeSink #endif ); mRecordThreads.add(id, thread); - ALOGV("openInput() created record thread: ID %d thread %p", id, thread); - if (pSamplingRate != NULL) { - *pSamplingRate = reqSamplingRate; - } - if (pFormat != NULL) { - *pFormat = config.format; - } - if (pChannelMask != NULL) { - *pChannelMask = reqChannelMask; - } - - // notify client processes of the new input creation - thread->audioConfigChanged(AudioSystem::INPUT_OPENED); - return id; + ALOGV("openInput() created record thread: ID %d thread %p", id, thread.get()); + return thread; } return 0; @@ -1981,17 +2025,26 @@ status_t AudioFlinger::closeInput_nonvirtual(audio_io_handle_t input) audioConfigChanged(AudioSystem::INPUT_CLOSED, input, NULL); mRecordThreads.removeItem(input); } - thread->exit(); - // The thread entity (active unit of execution) is no longer running here, - // but the ThreadBase container still exists. + // FIXME: calling thread->exit() without mLock held should not be needed anymore now that + // we have a different lock for notification client + closeInputFinish(thread); + return NO_ERROR; +} +void AudioFlinger::closeInputFinish(sp thread) +{ + thread->exit(); AudioStreamIn *in = thread->clearInput(); ALOG_ASSERT(in != NULL, "in shouldn't be NULL"); // from now on thread->mInput is NULL in->hwDev()->close_input_stream(in->hwDev(), in->stream); delete in; +} - return NO_ERROR; +void AudioFlinger::closeInputInternal_l(sp thread) +{ + mRecordThreads.removeItem(thread->mId); + closeInputFinish(thread); } status_t AudioFlinger::invalidateStream(audio_stream_type_t stream) diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index bae18fd..2830e6d 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -489,6 +489,18 @@ private: PlaybackThread *checkPlaybackThread_l(audio_io_handle_t output) const; MixerThread *checkMixerThread_l(audio_io_handle_t output) const; RecordThread *checkRecordThread_l(audio_io_handle_t input) const; + sp openInput_l(audio_module_handle_t module, + audio_devices_t device, + struct audio_config *config, + audio_input_flags_t flags); + sp openOutput_l(audio_module_handle_t module, + audio_devices_t device, + struct audio_config *config, + audio_output_flags_t flags); + + void closeOutputFinish(sp thread); + void closeInputFinish(sp thread); + // no range check, AudioFlinger::mLock held bool streamMute_l(audio_stream_type_t stream) const { return mStreamTypes[stream].mute; } @@ -530,10 +542,11 @@ private: AHWD_CAN_SET_MASTER_MUTE = 0x2, }; - AudioHwDevice(const char *moduleName, + AudioHwDevice(audio_module_handle_t handle, + const char *moduleName, audio_hw_device_t *hwDevice, Flags flags) - : mModuleName(strdup(moduleName)) + : mHandle(handle), mModuleName(strdup(moduleName)) , mHwDevice(hwDevice) , mFlags(flags) { } /*virtual*/ ~AudioHwDevice() { free((void *)mModuleName); } @@ -546,11 +559,13 @@ private: return (0 != (mFlags & AHWD_CAN_SET_MASTER_MUTE)); } + audio_module_handle_t handle() const { return mHandle; } const char *moduleName() const { return mModuleName; } audio_hw_device_t *hwDevice() const { return mHwDevice; } uint32_t version() const { return mHwDevice->common.version; } private: + audio_module_handle_t mHandle; const char * const mModuleName; audio_hw_device_t * const mHwDevice; const Flags mFlags; @@ -669,7 +684,9 @@ private: // for use from destructor status_t closeOutput_nonvirtual(audio_io_handle_t output); + void closeOutputInternal_l(sp thread); status_t closeInput_nonvirtual(audio_io_handle_t input); + void closeInputInternal_l(sp thread); #ifdef TEE_SINK // all record threads serially share a common tee sink, which is re-created on format change diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 6d84296..bf509e7 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -142,102 +142,172 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa ALOGV("createAudioPatch() num_sources %d num_sinks %d handle %d", patch->num_sources, patch->num_sinks, *handle); status_t status = NO_ERROR; - audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE; - sp audioflinger = mAudioFlinger.promote(); if (audioflinger == 0) { return NO_INIT; } + if (handle == NULL || patch == NULL) { return BAD_VALUE; } - // limit number of sources to 1 for now - if (patch->num_sources == 0 || patch->num_sources > 1 || + // 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 || patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) { return BAD_VALUE; } - for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) { - if (*handle == mPatches[index]->mHandle) { - ALOGV("createAudioPatch() removing patch handle %d", *handle); - halHandle = mPatches[index]->mHalHandle; - mPatches.removeAt(index); - break; + if (*handle != AUDIO_PATCH_HANDLE_NONE) { + for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) { + if (*handle == mPatches[index]->mHandle) { + ALOGV("createAudioPatch() removing patch handle %d", *handle); + halHandle = mPatches[index]->mHalHandle; + mPatches.removeAt(index); + break; + } } } + Patch *newPatch = new Patch(patch); + switch (patch->sources[0].type) { case AUDIO_PORT_TYPE_DEVICE: { // limit number of sinks to 1 for now if (patch->num_sinks > 1) { - return BAD_VALUE; + 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); if (index < 0) { ALOGW("createAudioPatch() bad src hw module %d", src_module); - return BAD_VALUE; + status = BAD_VALUE; + goto exit; } AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); for (unsigned int i = 0; i < patch->num_sinks; i++) { // 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"); - return BAD_VALUE; - } - // limit to connections between sinks and sources on same HW module - if (patch->sinks[i].ext.mix.hw_module != src_module) { - ALOGW("createAudioPatch() cannot connect source on module %d to " - "sink on module %d", src_module, patch->sinks[i].ext.mix.hw_module); - return BAD_VALUE; + status = BAD_VALUE; + goto exit; } - - // limit to connections between devices and output streams for HAL before 3.0 - if ((audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) && + // limit to connections between devices and input streams for HAL before 3.0 + if (patch->sinks[i].ext.mix.hw_module == src_module && + (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", patch->sinks[i].type); - return BAD_VALUE; + status = BAD_VALUE; + goto exit; } } - if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { - if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { - sp thread = audioflinger->checkRecordThread_l( - patch->sinks[0].ext.mix.handle); + if (patch->sinks[0].ext.device.hw_module != src_module) { + // 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"); + status = INVALID_OPERATION; + goto exit; + } + // special case num sources == 2 -=> reuse an exiting output mix to connect to the + // sink + if (patch->num_sources == 2) { + if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX || + patch->sinks[0].ext.device.hw_module != + patch->sources[1].ext.mix.hw_module) { + ALOGW("createAudioPatch() invalid source combination"); + status = INVALID_OPERATION; + goto exit; + } + + sp thread = + audioflinger->checkPlaybackThread_l(patch->sources[1].ext.mix.handle); + newPatch->mPlaybackThread = (MixerThread *)thread.get(); if (thread == 0) { - ALOGW("createAudioPatch() bad capture I/O handle %d", - patch->sinks[0].ext.mix.handle); - return BAD_VALUE; + ALOGW("createAudioPatch() cannot get playback thread"); + status = INVALID_OPERATION; + goto exit; } - status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); } else { - audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); - status = hwDevice->create_audio_patch(hwDevice, - patch->num_sources, - patch->sources, - patch->num_sinks, - patch->sinks, - &halHandle); + struct audio_config config; + config.sample_rate = 0; + config.channel_mask = AUDIO_CHANNEL_NONE; + config.format = AUDIO_FORMAT_DEFAULT; + newPatch->mPlaybackThread = audioflinger->openOutput_l( + patch->sinks[0].ext.device.hw_module, + patch->sinks[0].ext.device.type, + &config, + AUDIO_OUTPUT_FLAG_NONE); + ALOGV("audioflinger->openOutput_l() returned %p", + newPatch->mPlaybackThread.get()); + if (newPatch->mPlaybackThread == 0) { + status = NO_MEMORY; + goto exit; + } + } + uint32_t channelCount = newPatch->mPlaybackThread->channelCount(); + audio_devices_t device = patch->sources[0].ext.device.type; + struct audio_config config; + audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount); + config.sample_rate = newPatch->mPlaybackThread->sampleRate(); + config.channel_mask = inChannelMask; + config.format = newPatch->mPlaybackThread->format(); + newPatch->mRecordThread = audioflinger->openInput_l(src_module, + device, + &config, + AUDIO_INPUT_FLAG_NONE); + ALOGV("audioflinger->openInput_l() returned %p inChannelMask %08x", + newPatch->mRecordThread.get(), inChannelMask); + if (newPatch->mRecordThread == 0) { + status = NO_MEMORY; + goto exit; + } + status = createPatchConnections(newPatch, patch); + if (status != NO_ERROR) { + goto exit; } } else { - sp thread = audioflinger->checkRecordThread_l( - patch->sinks[0].ext.mix.handle); - if (thread == 0) { - ALOGW("createAudioPatch() bad capture I/O handle %d", - patch->sinks[0].ext.mix.handle); - return BAD_VALUE; + if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { + sp thread = audioflinger->checkRecordThread_l( + patch->sinks[0].ext.mix.handle); + if (thread == 0) { + ALOGW("createAudioPatch() bad capture I/O handle %d", + patch->sinks[0].ext.mix.handle); + status = BAD_VALUE; + goto exit; + } + status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); + } else { + audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); + status = hwDevice->create_audio_patch(hwDevice, + patch->num_sources, + patch->sources, + patch->num_sinks, + patch->sinks, + &halHandle); + } + } else { + sp thread = audioflinger->checkRecordThread_l( + patch->sinks[0].ext.mix.handle); + if (thread == 0) { + ALOGW("createAudioPatch() bad capture I/O handle %d", + patch->sinks[0].ext.mix.handle); + status = BAD_VALUE; + goto exit; + } + AudioParameter param; + param.addInt(String8(AudioParameter::keyRouting), + (int)patch->sources[0].ext.device.type); + param.addInt(String8(AudioParameter::keyInputSource), + (int)patch->sinks[0].ext.mix.usecase.source); + + ALOGV("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s", + param.toString().string()); + status = thread->setParameters(param.toString()); } - AudioParameter param; - param.addInt(String8(AudioParameter::keyRouting), - (int)patch->sources[0].ext.device.type); - param.addInt(String8(AudioParameter::keyInputSource), - (int)patch->sinks[0].ext.mix.usecase.source); - - ALOGV("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s", - param.toString().string()); - status = thread->setParameters(param.toString()); } } break; case AUDIO_PORT_TYPE_MIX: { @@ -245,18 +315,21 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module); if (index < 0) { ALOGW("createAudioPatch() bad src hw module %d", src_module); - return BAD_VALUE; + status = BAD_VALUE; + goto exit; } // limit to connections between devices and output streams for (unsigned int i = 0; i < patch->num_sinks; i++) { if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) { - ALOGW("createAudioPatch() invalid sink type %d for bus source", + ALOGW("createAudioPatch() invalid sink type %d for mix source", patch->sinks[i].type); - return BAD_VALUE; + status = BAD_VALUE; + goto exit; } // limit to connections between sinks and sources on same HW module if (patch->sinks[i].ext.device.hw_module != src_module) { - return BAD_VALUE; + status = BAD_VALUE; + goto exit; } } AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); @@ -265,7 +338,8 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa if (thread == 0) { ALOGW("createAudioPatch() bad playback I/O handle %d", patch->sources[0].ext.mix.handle); - return BAD_VALUE; + status = BAD_VALUE; + goto exit; } if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); @@ -281,20 +355,162 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa } break; default: - return BAD_VALUE; + status = BAD_VALUE; + goto exit; } +exit: ALOGV("createAudioPatch() status %d", status); if (status == NO_ERROR) { *handle = audioflinger->nextUniqueId(); - Patch *newPatch = new Patch(patch); newPatch->mHandle = *handle; newPatch->mHalHandle = halHandle; mPatches.add(newPatch); ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle); + } else { + clearPatchConnections(newPatch); + delete newPatch; + } + return status; +} + +status_t AudioFlinger::PatchPanel::createPatchConnections(Patch *patch, + const struct audio_patch *audioPatch) +{ + // create patch from source device to record thread input + struct audio_patch subPatch; + subPatch.num_sources = 1; + subPatch.sources[0] = audioPatch->sources[0]; + subPatch.num_sinks = 1; + + patch->mRecordThread->getAudioPortConfig(&subPatch.sinks[0]); + subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC; + + status_t status = createAudioPatch(&subPatch, &patch->mRecordPatchHandle); + if (status != NO_ERROR) { + patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; + return status; + } + + // create patch from playback thread output to sink device + patch->mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]); + subPatch.sinks[0] = audioPatch->sinks[0]; + status = createAudioPatch(&subPatch, &patch->mPlaybackPatchHandle); + if (status != NO_ERROR) { + patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; + return status; } + + // use a pseudo LCM between input and output framecount + size_t playbackFrameCount = patch->mPlaybackThread->frameCount(); + int playbackShift = __builtin_ctz(playbackFrameCount); + size_t recordFramecount = patch->mRecordThread->frameCount(); + int shift = __builtin_ctz(recordFramecount); + if (playbackShift < shift) { + shift = playbackShift; + } + size_t frameCount = (playbackFrameCount * recordFramecount) >> shift; + ALOGV("createPatchConnections() playframeCount %d recordFramecount %d frameCount %d ", + playbackFrameCount, recordFramecount, frameCount); + + // create a special record track to capture from record thread + uint32_t channelCount = patch->mPlaybackThread->channelCount(); + audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount); + audio_channel_mask_t outChannelMask = patch->mPlaybackThread->channelMask(); + uint32_t sampleRate = patch->mPlaybackThread->sampleRate(); + audio_format_t format = patch->mPlaybackThread->format(); + + patch->mPatchRecord = new RecordThread::PatchRecord( + patch->mRecordThread.get(), + sampleRate, + inChannelMask, + format, + frameCount, + NULL, + IAudioFlinger::TRACK_DEFAULT); + if (patch->mPatchRecord == 0) { + return NO_MEMORY; + } + status = patch->mPatchRecord->initCheck(); + if (status != NO_ERROR) { + return status; + } + patch->mRecordThread->addPatchRecord(patch->mPatchRecord); + + // create a special playback track to render to playback thread. + // this track is given the same buffer as the PatchRecord buffer + patch->mPatchTrack = new PlaybackThread::PatchTrack( + patch->mPlaybackThread.get(), + sampleRate, + outChannelMask, + format, + frameCount, + patch->mPatchRecord->buffer(), + IAudioFlinger::TRACK_DEFAULT); + if (patch->mPatchTrack == 0) { + return NO_MEMORY; + } + status = patch->mPatchTrack->initCheck(); + if (status != NO_ERROR) { + return status; + } + patch->mPlaybackThread->addPatchTrack(patch->mPatchTrack); + + // tie playback and record tracks together + patch->mPatchRecord->setPeerProxy(patch->mPatchTrack.get()); + patch->mPatchTrack->setPeerProxy(patch->mPatchRecord.get()); + + // start capture and playback + patch->mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, 0); + patch->mPatchTrack->start(); + return status; } +void AudioFlinger::PatchPanel::clearPatchConnections(Patch *patch) +{ + sp audioflinger = mAudioFlinger.promote(); + if (audioflinger == 0) { + return; + } + + ALOGV("clearPatchConnections() patch->mRecordPatchHandle %d patch->mPlaybackPatchHandle %d", + patch->mRecordPatchHandle, patch->mPlaybackPatchHandle); + + if (patch->mPatchRecord != 0) { + patch->mPatchRecord->stop(); + } + if (patch->mPatchTrack != 0) { + patch->mPatchTrack->stop(); + } + if (patch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) { + releaseAudioPatch(patch->mRecordPatchHandle); + patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; + } + if (patch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { + releaseAudioPatch(patch->mPlaybackPatchHandle); + patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; + } + if (patch->mRecordThread != 0) { + if (patch->mPatchRecord != 0) { + patch->mRecordThread->deletePatchRecord(patch->mPatchRecord); + patch->mPatchRecord.clear(); + } + audioflinger->closeInputInternal_l(patch->mRecordThread); + patch->mRecordThread.clear(); + } + if (patch->mPlaybackThread != 0) { + if (patch->mPatchTrack != 0) { + patch->mPlaybackThread->deletePatchTrack(patch->mPatchTrack); + patch->mPatchTrack.clear(); + } + // if num sources == 2 we are reusing an existing playback thread so we do not close it + if (patch->mAudioPatch.num_sources != 2) { + audioflinger->closeOutputInternal_l(patch->mPlaybackThread); + } + patch->mPlaybackThread.clear(); + } +} + /* Disconnect a patch */ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle) { @@ -315,8 +531,10 @@ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle if (index == mPatches.size()) { return BAD_VALUE; } + Patch *removedPatch = mPatches[index]; + mPatches.removeAt(index); - struct audio_patch *patch = &mPatches[index]->mAudioPatch; + struct audio_patch *patch = &removedPatch->mAudioPatch; switch (patch->sources[0].type) { case AUDIO_PORT_TYPE_DEVICE: { @@ -327,13 +545,20 @@ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle status = BAD_VALUE; break; } + + if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE && + patch->sinks[0].ext.device.hw_module != src_module) { + clearPatchConnections(removedPatch); + break; + } + AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { sp thread = audioflinger->checkRecordThread_l( patch->sinks[0].ext.mix.handle); if (thread == 0) { - ALOGW("createAudioPatch() bad capture I/O handle %d", + ALOGW("releaseAudioPatch() bad capture I/O handle %d", patch->sinks[0].ext.mix.handle); status = BAD_VALUE; break; @@ -389,8 +614,7 @@ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle break; } - delete (mPatches[index]); - mPatches.removeAt(index); + delete removedPatch; return status; } diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h index 7f78621..e31179c 100644 --- a/services/audioflinger/PatchPanel.h +++ b/services/audioflinger/PatchPanel.h @@ -21,6 +21,9 @@ class PatchPanel : public RefBase { public: + + class Patch; + PatchPanel(const sp& audioFlinger); virtual ~PatchPanel(); @@ -45,16 +48,31 @@ public: /* Set audio port configuration */ status_t setAudioPortConfig(const struct audio_port_config *config); + status_t createPatchConnections(Patch *patch, + const struct audio_patch *audioPatch); + void clearPatchConnections(Patch *patch); + class Patch { public: Patch(const struct audio_patch *patch) : - mAudioPatch(*patch), mHandle(0), mHalHandle(0) {} + mAudioPatch(*patch), mHandle(AUDIO_PATCH_HANDLE_NONE), + mHalHandle(AUDIO_PATCH_HANDLE_NONE), mRecordPatchHandle(AUDIO_PATCH_HANDLE_NONE), + mPlaybackPatchHandle(AUDIO_PATCH_HANDLE_NONE) {} + ~Patch() {} + + struct audio_patch mAudioPatch; + audio_patch_handle_t mHandle; + audio_patch_handle_t mHalHandle; + sp mPlaybackThread; + sp mPatchTrack; + sp mRecordThread; + sp mPatchRecord; + audio_patch_handle_t mRecordPatchHandle; + audio_patch_handle_t mPlaybackPatchHandle; - struct audio_patch mAudioPatch; - audio_patch_handle_t mHandle; - audio_patch_handle_t mHalHandle; }; + private: - const wp mAudioFlinger; - SortedVector mPatches; + const wp mAudioFlinger; + SortedVector mPatches; }; diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 79bdfe8..ee48276 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -29,10 +29,12 @@ public: audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, + void *buffer, const sp& sharedBuffer, int sessionId, int uid, - IAudioFlinger::track_flags_t flags); + IAudioFlinger::track_flags_t flags, + track_type type); virtual ~Track(); virtual status_t initCheck() const; @@ -100,10 +102,6 @@ protected: bool isResumePending(); void resumeAck(); - bool isOutputTrack() const { - return (mStreamType == AUDIO_STREAM_CNT); - } - sp sharedBuffer() const { return mSharedBuffer; } // framesWritten is cumulative, never reset, and is shared all tracks @@ -115,7 +113,6 @@ public: void triggerEvents(AudioSystem::sync_event_t type); void invalidate(); bool isInvalid() const { return mIsInvalid; } - virtual bool isTimedTrack() const { return false; } int fastIndex() const { return mFastIndex; } protected: @@ -163,7 +160,6 @@ private: bool mPreviousValid; uint32_t mPreviousFramesWritten; AudioTimestamp mPreviousTimestamp; - }; // end of Track class TimedTrack : public Track { @@ -195,7 +191,6 @@ class TimedTrack : public Track { }; // Mixer facing methods. - virtual bool isTimedTrack() const { return true; } virtual size_t framesReady() const; // AudioBufferProvider interface @@ -296,3 +291,34 @@ private: DuplicatingThread* const mSourceThread; // for waitTimeMs() in write() AudioTrackClientProxy* mClientProxy; }; // end of OutputTrack + +// playback track, used by PatchPanel +class PatchTrack : public Track, public PatchProxyBufferProvider { +public: + + PatchTrack(PlaybackThread *playbackThread, + uint32_t sampleRate, + audio_channel_mask_t channelMask, + audio_format_t format, + size_t frameCount, + void *buffer, + IAudioFlinger::track_flags_t flags); + virtual ~PatchTrack(); + + // AudioBufferProvider interface + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, + int64_t pts); + virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); + + // PatchProxyBufferProvider interface + virtual status_t obtainBuffer(Proxy::Buffer* buffer, + const struct timespec *timeOut = NULL); + virtual void releaseBuffer(Proxy::Buffer* buffer); + + void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; } + +private: + sp mProxy; + PatchProxyBufferProvider* mPeerProxy; + struct timespec mPeerTimeout; +}; // end of PatchTrack diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index fe15571..204a9d6 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -28,9 +28,11 @@ public: audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, + void *buffer, int sessionId, int uid, - IAudioFlinger::track_flags_t flags); + IAudioFlinger::track_flags_t flags, + track_type type); virtual ~RecordTrack(); virtual status_t start(AudioSystem::sync_event_t event, int triggerSession); @@ -93,3 +95,34 @@ private: // used by resampler to find source frames ResamplerBufferProvider *mResamplerBufferProvider; }; + +// playback track, used by PatchPanel +class PatchRecord : virtual public RecordTrack, public PatchProxyBufferProvider { +public: + + PatchRecord(RecordThread *recordThread, + uint32_t sampleRate, + audio_channel_mask_t channelMask, + audio_format_t format, + size_t frameCount, + void *buffer, + IAudioFlinger::track_flags_t flags); + virtual ~PatchRecord(); + + // AudioBufferProvider interface + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, + int64_t pts); + virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); + + // PatchProxyBufferProvider interface + virtual status_t obtainBuffer(Proxy::Buffer *buffer, + const struct timespec *timeOut = NULL); + virtual void releaseBuffer(Proxy::Buffer *buffer); + + void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; } + +private: + sp mProxy; + PatchProxyBufferProvider* mPeerProxy; + struct timespec mPeerTimeout; +}; // end of PatchRecord diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index e0b664b..651c6ea 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -1146,6 +1146,18 @@ void AudioFlinger::ThreadBase::disconnectEffect(const sp& effect, } } +void AudioFlinger::ThreadBase::getAudioPortConfig(struct audio_port_config *config) +{ + config->type = AUDIO_PORT_TYPE_MIX; + config->ext.mix.handle = mId; + config->sample_rate = mSampleRate; + config->format = mFormat; + config->channel_mask = mChannelMask; + config->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| + AUDIO_PORT_CONFIG_FORMAT; +} + + // ---------------------------------------------------------------------------- // Playback // ---------------------------------------------------------------------------- @@ -1482,7 +1494,7 @@ sp AudioFlinger::PlaybackThread::createTrac uint32_t strategy = AudioSystem::getStrategyForStream(streamType); for (size_t i = 0; i < mTracks.size(); ++i) { sp t = mTracks[i]; - if (t != 0 && !t->isOutputTrack()) { + if (t != 0 && t->isExternalTrack()) { uint32_t actual = AudioSystem::getStrategyForStream(t->streamType()); if (sessionId == t->sessionId() && strategy != actual) { ALOGE("createTrack_l() mismatched strategy; expected %u but found %u", @@ -1495,7 +1507,8 @@ sp AudioFlinger::PlaybackThread::createTrac if (!isTimed) { track = new Track(this, client, streamType, sampleRate, format, - channelMask, frameCount, sharedBuffer, sessionId, uid, *flags); + channelMask, frameCount, NULL, sharedBuffer, + sessionId, uid, *flags, TrackBase::TYPE_DEFAULT); } else { track = TimedTrack::create(this, client, streamType, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId, uid); @@ -1608,7 +1621,7 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp& track) // the track is newly added, make sure it fills up all its // buffers before playing. This is to ensure the client will // effectively get the latency it requested. - if (!track->isOutputTrack()) { + if (track->isExternalTrack()) { TrackBase::track_state state = track->mState; mLock.unlock(); status = AudioSystem::startOutput(mId, track->streamType(), track->sessionId()); @@ -2044,7 +2057,7 @@ void AudioFlinger::PlaybackThread::threadLoop_removeTracks( if (count > 0) { for (size_t i = 0 ; i < count ; i++) { const sp& track = tracksToRemove.itemAt(i); - if (!track->isOutputTrack()) { + if (track->isExternalTrack()) { AudioSystem::stopOutput(mId, track->streamType(), track->sessionId()); #ifdef ADD_BATTERY_DATA // to track the speaker usage @@ -2713,6 +2726,26 @@ status_t AudioFlinger::PlaybackThread::releaseAudioPatch_l(const audio_patch_han return status; } +void AudioFlinger::PlaybackThread::addPatchTrack(const sp& track) +{ + Mutex::Autolock _l(mLock); + mTracks.add(track); +} + +void AudioFlinger::PlaybackThread::deletePatchTrack(const sp& track) +{ + Mutex::Autolock _l(mLock); + destroyTrack_l(track); +} + +void AudioFlinger::PlaybackThread::getAudioPortConfig(struct audio_port_config *config) +{ + ThreadBase::getAudioPortConfig(config); + config->role = AUDIO_PORT_ROLE_SOURCE; + config->ext.mix.hw_module = mOutput->audioHwDev->handle(); + config->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT; +} + // ---------------------------------------------------------------------------- AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, AudioStreamOut* output, @@ -5523,8 +5556,8 @@ sp AudioFlinger::RecordThread::createRe Mutex::Autolock _l(mLock); track = new RecordTrack(this, client, sampleRate, - format, channelMask, frameCount, sessionId, uid, - *flags); + format, channelMask, frameCount, NULL, sessionId, uid, + *flags, TrackBase::TYPE_DEFAULT); lStatus = track->initCheck(); if (lStatus != NO_ERROR) { @@ -5601,15 +5634,19 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac recordTrack->mState = TrackBase::STARTING_1; mActiveTracks.add(recordTrack); mActiveTracksGen++; - mLock.unlock(); - status_t status = AudioSystem::startInput(mId); - mLock.lock(); - // FIXME should verify that recordTrack is still in mActiveTracks - if (status != NO_ERROR) { - mActiveTracks.remove(recordTrack); - mActiveTracksGen++; - recordTrack->clearSyncStartEvent(); - return status; + status_t status = NO_ERROR; + if (recordTrack->isExternalTrack()) { + mLock.unlock(); + status = AudioSystem::startInput(mId); + mLock.lock(); + // FIXME should verify that recordTrack is still in mActiveTracks + if (status != NO_ERROR) { + mActiveTracks.remove(recordTrack); + mActiveTracksGen++; + recordTrack->clearSyncStartEvent(); + ALOGV("RecordThread::start error %d", status); + return status; + } } // Catch up with current buffer indices if thread is already running. // This is what makes a new client discard all buffered data. If the track's mRsmpInFront @@ -5634,7 +5671,9 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac } startError: - AudioSystem::stopInput(mId); + if (recordTrack->isExternalTrack()) { + AudioSystem::stopInput(mId); + } recordTrack->clearSyncStartEvent(); // FIXME I wonder why we do not reset the state here? return status; @@ -6177,5 +6216,24 @@ status_t AudioFlinger::RecordThread::releaseAudioPatch_l(const audio_patch_handl return status; } +void AudioFlinger::RecordThread::addPatchRecord(const sp& record) +{ + Mutex::Autolock _l(mLock); + mTracks.add(record); +} + +void AudioFlinger::RecordThread::deletePatchRecord(const sp& record) +{ + Mutex::Autolock _l(mLock); + destroyTrack_l(record); +} + +void AudioFlinger::RecordThread::getAudioPortConfig(struct audio_port_config *config) +{ + ThreadBase::getAudioPortConfig(config); + config->role = AUDIO_PORT_ROLE_SINK; + config->ext.mix.hw_module = mInput->audioHwDev->handle(); + config->ext.mix.usecase.source = mAudioSource; +} }; // namespace android diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 3b7257b..648502b 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -235,6 +235,7 @@ public: uint32_t sampleRate() const { return mSampleRate; } audio_channel_mask_t channelMask() const { return mChannelMask; } audio_format_t format() const { return mHALFormat; } + uint32_t channelCount() const { return mChannelCount; } // Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects, // and returns the [normal mix] buffer's frame count. virtual size_t frameCount() const = 0; @@ -264,6 +265,7 @@ public: virtual status_t createAudioPatch_l(const struct audio_patch *patch, audio_patch_handle_t *handle) = 0; virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle) = 0; + virtual void getAudioPortConfig(struct audio_port_config *config) = 0; // see note at declaration of mStandby, mOutDevice and mInDevice @@ -589,7 +591,12 @@ public: // Return's the HAL's frame count i.e. fast mixer buffer size. size_t frameCountHAL() const { return mFrameCount; } - status_t getTimestamp_l(AudioTimestamp& timestamp); + status_t getTimestamp_l(AudioTimestamp& timestamp); + + void addPatchTrack(const sp& track); + void deletePatchTrack(const sp& track); + + virtual void getAudioPortConfig(struct audio_port_config *config); protected: // updated by readOutputParameters_l() @@ -876,6 +883,7 @@ public: ALOG_ASSERT(fastIndex < FastMixerState::kMaxFastTracks); return mFastMixerDumpState.mTracks[fastIndex].mUnderruns; } + }; class DirectOutputThread : public PlaybackThread { @@ -1103,6 +1111,10 @@ public: virtual status_t createAudioPatch_l(const struct audio_patch *patch, audio_patch_handle_t *handle); virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle); + + void addPatchRecord(const sp& record); + void deletePatchRecord(const sp& record); + void readInputParameters_l(); virtual uint32_t getInputFramesLost(); @@ -1122,6 +1134,7 @@ public: virtual size_t frameCount() const { return mFrameCount; } bool hasFastCapture() const { return mFastCapture != 0; } + virtual void getAudioPortConfig(struct audio_port_config *config); private: // Enter standby if not already in standby, and set mStandby flag diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index 4cba3fd..864daa5 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -44,6 +44,15 @@ public: ALLOC_CBLK, // allocate immediately after control block ALLOC_READONLY, // allocate from a separate read-only heap per thread ALLOC_PIPE, // do not allocate; use the pipe buffer + ALLOC_LOCAL, // allocate a local buffer + ALLOC_NONE, // do not allocate:use the buffer passed to TrackBase constructor + }; + + enum track_type { + TYPE_DEFAULT, + TYPE_TIMED, + TYPE_OUTPUT, + TYPE_PATCH, }; TrackBase(ThreadBase *thread, @@ -52,14 +61,15 @@ public: audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, - const sp& sharedBuffer, + void *buffer, int sessionId, int uid, IAudioFlinger::track_flags_t flags, bool isOut, - alloc_type alloc = ALLOC_CBLK); + alloc_type alloc = ALLOC_CBLK, + track_type type = TYPE_DEFAULT); virtual ~TrackBase(); - virtual status_t initCheck() const { return getCblk() != 0 ? NO_ERROR : NO_MEMORY; } + virtual status_t initCheck() const; virtual status_t start(AudioSystem::sync_event_t event, int triggerSession) = 0; @@ -71,7 +81,12 @@ public: virtual status_t setSyncEvent(const sp& event); sp getBuffers() const { return mBufferMemory; } + void* buffer() const { return mBuffer; } bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; } + bool isTimedTrack() const { return (mType == TYPE_TIMED); } + bool isOutputTrack() const { return (mType == TYPE_OUTPUT); } + bool isPatchTrack() const { return (mType == TYPE_PATCH); } + bool isExternalTrack() const { return !isOutputTrack() && !isPatchTrack(); } protected: TrackBase(const TrackBase&); @@ -150,4 +165,18 @@ protected: sp mTeeSink; sp mTeeSource; bool mTerminated; + track_type mType; // must be one of TYPE_DEFAULT, TYPE_OUTPUT, TYPE_PATCH ... +}; + +// PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord. +// it provides buffer access methods that map those of a ClientProxy (see AudioTrackShared.h) +class PatchProxyBufferProvider +{ +public: + + virtual ~PatchProxyBufferProvider() {} + + virtual status_t obtainBuffer(Proxy::Buffer* buffer, + const struct timespec *requested = NULL) = 0; + virtual void releaseBuffer(Proxy::Buffer* buffer) = 0; }; diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index af761e4..e81697f 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -68,12 +68,13 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, - const sp& sharedBuffer, + void *buffer, int sessionId, int clientUid, IAudioFlinger::track_flags_t flags, bool isOut, - alloc_type alloc) + alloc_type alloc, + track_type type) : RefBase(), mThread(thread), mClient(client), @@ -94,7 +95,8 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( mIsOut(isOut), mServerProxy(NULL), mId(android_atomic_inc(&nextTrackId)), - mTerminated(false) + mTerminated(false), + mType(type) { // if the caller is us, trust the specified uid if (IPCThreadState::self()->getCallingPid() != getpid_cached || clientUid == -1) { @@ -108,16 +110,10 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( // battery usage on it. mUid = clientUid; - // client == 0 implies sharedBuffer == 0 - ALOG_ASSERT(!(client == 0 && sharedBuffer != 0)); - - ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), - sharedBuffer->size()); - // ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize); size_t size = sizeof(audio_track_cblk_t); - size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize; - if (sharedBuffer == 0 && alloc == ALLOC_CBLK) { + size_t bufferSize = (buffer == NULL ? roundup(frameCount) : frameCount) * mFrameSize; + if (buffer == NULL && alloc == ALLOC_CBLK) { size += bufferSize; } @@ -166,16 +162,22 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( break; case ALLOC_CBLK: // clear all buffers - if (sharedBuffer == 0) { + if (buffer == NULL) { mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); memset(mBuffer, 0, bufferSize); } else { - mBuffer = sharedBuffer->pointer(); + mBuffer = buffer; #if 0 mCblk->mFlags = CBLK_FORCEREADY; // FIXME hack, need to fix the track ready logic #endif } break; + case ALLOC_LOCAL: + mBuffer = calloc(1, bufferSize); + break; + case ALLOC_NONE: + mBuffer = buffer; + break; } #ifdef TEE_SINK @@ -200,6 +202,17 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( } } +status_t AudioFlinger::ThreadBase::TrackBase::initCheck() const +{ + status_t status; + if (mType == TYPE_OUTPUT || mType == TYPE_PATCH) { + status = cblk() != NULL ? NO_ERROR : NO_MEMORY; + } else { + status = getCblk() != 0 ? NO_ERROR : NO_MEMORY; + } + return status; +} + AudioFlinger::ThreadBase::TrackBase::~TrackBase() { #ifdef TEE_SINK @@ -364,12 +377,17 @@ AudioFlinger::PlaybackThread::Track::Track( audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, + void *buffer, const sp& sharedBuffer, int sessionId, int uid, - IAudioFlinger::track_flags_t flags) - : TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer, - sessionId, uid, flags, true /*isOut*/), + IAudioFlinger::track_flags_t flags, + track_type type) + : TrackBase(thread, client, sampleRate, format, channelMask, frameCount, + (sharedBuffer != 0) ? sharedBuffer->pointer() : buffer, + sessionId, uid, flags, true /*isOut*/, + (type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK, + type), mFillingUpStatus(FS_INVALID), // mRetryCount initialized later when needed mSharedBuffer(sharedBuffer), @@ -389,13 +407,19 @@ AudioFlinger::PlaybackThread::Track::Track( mPreviousFramesWritten(0) // mPreviousTimestamp { + // client == 0 implies sharedBuffer == 0 + ALOG_ASSERT(!(client == 0 && sharedBuffer != 0)); + + ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), + sharedBuffer->size()); + if (mCblk == NULL) { return; } if (sharedBuffer == 0) { mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount, - mFrameSize); + mFrameSize, !isExternalTrack(), sampleRate); } else { mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount, mFrameSize); @@ -463,7 +487,7 @@ void AudioFlinger::PlaybackThread::Track::destroy() Mutex::Autolock _l(thread->mLock); PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); bool wasActive = playbackThread->destroyTrack_l(this); - if (!isOutputTrack() && !wasActive) { + if (isExternalTrack() && !wasActive) { AudioSystem::releaseOutput(thread->id()); } } @@ -1122,7 +1146,8 @@ AudioFlinger::PlaybackThread::TimedTrack::TimedTrack( int sessionId, int uid) : Track(thread, client, streamType, sampleRate, format, channelMask, - frameCount, sharedBuffer, sessionId, uid, IAudioFlinger::TRACK_TIMED), + frameCount, (sharedBuffer != 0) ? sharedBuffer->pointer() : NULL, sharedBuffer, + sessionId, uid, IAudioFlinger::TRACK_TIMED, TYPE_TIMED), mQueueHeadInFlight(false), mTrimQueueHeadOnRelease(false), mFramesPendingInQueue(0), @@ -1617,7 +1642,7 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( size_t frameCount, int uid) : Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, - NULL, 0, uid, IAudioFlinger::TRACK_DEFAULT), + NULL, 0, 0, uid, IAudioFlinger::TRACK_DEFAULT, TYPE_OUTPUT), mActive(false), mSourceThread(sourceThread), mClientProxy(NULL) { @@ -1825,6 +1850,75 @@ void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue() } +AudioFlinger::PlaybackThread::PatchTrack::PatchTrack(PlaybackThread *playbackThread, + uint32_t sampleRate, + audio_channel_mask_t channelMask, + audio_format_t format, + size_t frameCount, + void *buffer, + IAudioFlinger::track_flags_t flags) + : Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, + buffer, 0, 0, getuid(), flags, TYPE_PATCH), + mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true)) +{ + uint64_t mixBufferNs = ((uint64_t)2 * playbackThread->frameCount() * 1000000000) / + playbackThread->sampleRate(); + mPeerTimeout.tv_sec = mixBufferNs / 1000000000; + mPeerTimeout.tv_nsec = (int) (mixBufferNs % 1000000000); + + ALOGV("PatchTrack %p sampleRate %d mPeerTimeout %d.%03d sec", + this, sampleRate, + (int)mPeerTimeout.tv_sec, + (int)(mPeerTimeout.tv_nsec / 1000000)); +} + +AudioFlinger::PlaybackThread::PatchTrack::~PatchTrack() +{ +} + +// AudioBufferProvider interface +status_t AudioFlinger::PlaybackThread::PatchTrack::getNextBuffer( + AudioBufferProvider::Buffer* buffer, int64_t pts) +{ + ALOG_ASSERT(mPeerProxy != 0, "PatchTrack::getNextBuffer() called without peer proxy"); + Proxy::Buffer buf; + buf.mFrameCount = buffer->frameCount; + status_t status = mPeerProxy->obtainBuffer(&buf, &mPeerTimeout); + ALOGV_IF(status != NO_ERROR, "PatchTrack() %p getNextBuffer status %d", this, status); + if (buf.mFrameCount == 0) { + return WOULD_BLOCK; + } + buffer->frameCount = buf.mFrameCount; + status = Track::getNextBuffer(buffer, pts); + return status; +} + +void AudioFlinger::PlaybackThread::PatchTrack::releaseBuffer(AudioBufferProvider::Buffer* buffer) +{ + ALOG_ASSERT(mPeerProxy != 0, "PatchTrack::releaseBuffer() called without peer proxy"); + Proxy::Buffer buf; + buf.mFrameCount = buffer->frameCount; + buf.mRaw = buffer->raw; + mPeerProxy->releaseBuffer(&buf); + TrackBase::releaseBuffer(buffer); +} + +status_t AudioFlinger::PlaybackThread::PatchTrack::obtainBuffer(Proxy::Buffer* buffer, + const struct timespec *timeOut) +{ + return mProxy->obtainBuffer(buffer, timeOut); +} + +void AudioFlinger::PlaybackThread::PatchTrack::releaseBuffer(Proxy::Buffer* buffer) +{ + mProxy->releaseBuffer(buffer); + if (android_atomic_and(~CBLK_DISABLED, &mCblk->mFlags) & CBLK_DISABLED) { + ALOGW("PatchTrack::releaseBuffer() disabled due to previous underrun, restarting"); + start(); + } + android_atomic_or(CBLK_FORCEREADY, &mCblk->mFlags); +} + // ---------------------------------------------------------------------------- // Record // ---------------------------------------------------------------------------- @@ -1872,13 +1966,18 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, + void *buffer, int sessionId, int uid, - IAudioFlinger::track_flags_t flags) + IAudioFlinger::track_flags_t flags, + track_type type) : TrackBase(thread, client, sampleRate, format, - channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, + channelMask, frameCount, buffer, sessionId, uid, flags, false /*isOut*/, - flags & IAudioFlinger::TRACK_FAST ? ALLOC_PIPE : ALLOC_CBLK), + (type == TYPE_DEFAULT) ? + ((flags & IAudioFlinger::TRACK_FAST) ? ALLOC_PIPE : ALLOC_CBLK) : + ((buffer == NULL) ? ALLOC_LOCAL : ALLOC_NONE), + type), mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0), // See real initialization of mRsmpInFront at RecordThread::start() mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL) @@ -1887,7 +1986,8 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( return; } - mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount, mFrameSize); + mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount, + mFrameSize, !isExternalTrack()); uint32_t channelCount = audio_channel_count_from_in_mask(channelMask); // FIXME I don't understand either of the channel count checks @@ -1949,7 +2049,7 @@ void AudioFlinger::RecordThread::RecordTrack::stop() sp thread = mThread.promote(); if (thread != 0) { RecordThread *recordThread = (RecordThread *)thread.get(); - if (recordThread->stop(this)) { + if (recordThread->stop(this) && isExternalTrack()) { AudioSystem::stopInput(recordThread->id()); } } @@ -1962,10 +2062,12 @@ void AudioFlinger::RecordThread::RecordTrack::destroy() { sp thread = mThread.promote(); if (thread != 0) { - if (mState == ACTIVE || mState == RESUMING) { - AudioSystem::stopInput(thread->id()); + if (isExternalTrack()) { + if (mState == ACTIVE || mState == RESUMING) { + AudioSystem::stopInput(thread->id()); + } + AudioSystem::releaseInput(thread->id()); } - AudioSystem::releaseInput(thread->id()); Mutex::Autolock _l(thread->mLock); RecordThread *recordThread = (RecordThread *) thread.get(); recordThread->destroyTrack_l(this); @@ -2027,4 +2129,70 @@ void AudioFlinger::RecordThread::RecordTrack::clearSyncStartEvent() mFramesToDrop = 0; } + +AudioFlinger::RecordThread::PatchRecord::PatchRecord(RecordThread *recordThread, + uint32_t sampleRate, + audio_channel_mask_t channelMask, + audio_format_t format, + size_t frameCount, + void *buffer, + IAudioFlinger::track_flags_t flags) + : RecordTrack(recordThread, NULL, sampleRate, format, channelMask, frameCount, + buffer, 0, getuid(), flags, TYPE_PATCH), + mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true)) +{ + uint64_t mixBufferNs = ((uint64_t)2 * recordThread->frameCount() * 1000000000) / + recordThread->sampleRate(); + mPeerTimeout.tv_sec = mixBufferNs / 1000000000; + mPeerTimeout.tv_nsec = (int) (mixBufferNs % 1000000000); + + ALOGV("PatchRecord %p sampleRate %d mPeerTimeout %d.%03d sec", + this, sampleRate, + (int)mPeerTimeout.tv_sec, + (int)(mPeerTimeout.tv_nsec / 1000000)); +} + +AudioFlinger::RecordThread::PatchRecord::~PatchRecord() +{ +} + +// AudioBufferProvider interface +status_t AudioFlinger::RecordThread::PatchRecord::getNextBuffer( + AudioBufferProvider::Buffer* buffer, int64_t pts) +{ + ALOG_ASSERT(mPeerProxy != 0, "PatchRecord::getNextBuffer() called without peer proxy"); + Proxy::Buffer buf; + buf.mFrameCount = buffer->frameCount; + status_t status = mPeerProxy->obtainBuffer(&buf, &mPeerTimeout); + ALOGV_IF(status != NO_ERROR, + "PatchRecord() %p mPeerProxy->obtainBuffer status %d", this, status); + if (buf.mFrameCount == 0) { + return WOULD_BLOCK; + } + buffer->frameCount = buf.mFrameCount; + status = RecordTrack::getNextBuffer(buffer, pts); + return status; +} + +void AudioFlinger::RecordThread::PatchRecord::releaseBuffer(AudioBufferProvider::Buffer* buffer) +{ + ALOG_ASSERT(mPeerProxy != 0, "PatchRecord::releaseBuffer() called without peer proxy"); + Proxy::Buffer buf; + buf.mFrameCount = buffer->frameCount; + buf.mRaw = buffer->raw; + mPeerProxy->releaseBuffer(&buf); + TrackBase::releaseBuffer(buffer); +} + +status_t AudioFlinger::RecordThread::PatchRecord::obtainBuffer(Proxy::Buffer* buffer, + const struct timespec *timeOut) +{ + return mProxy->obtainBuffer(buffer, timeOut); +} + +void AudioFlinger::RecordThread::PatchRecord::releaseBuffer(Proxy::Buffer* buffer) +{ + mProxy->releaseBuffer(buffer); +} + }; // namespace android diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp index e449863..bd5427d 100644 --- a/services/audiopolicy/AudioPolicyManager.cpp +++ b/services/audiopolicy/AudioPolicyManager.cpp @@ -1875,6 +1875,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, patch->sources[0].format, patch->sources[0].channel_mask, AUDIO_OUTPUT_FLAG_NONE)) { + ALOGV("createAudioPatch() profile not supported"); return INVALID_OPERATION; } // TODO: reconfigure output format and channels here @@ -1963,9 +1964,20 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, srcDeviceDesc->toAudioPortConfig(&newPatch.sources[0], &patch->sources[0]); sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[0], &patch->sinks[0]); - // TODO: add support for devices on different HW modules if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) { - return INVALID_OPERATION; + SortedVector 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 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 @@ -2270,12 +2282,17 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa continue; } - audio_devices_t profileTypes = outProfile->mSupportedDevices.types(); - if ((profileTypes & outputDeviceTypes) && + audio_devices_t profileType = outProfile->mSupportedDevices.types(); + if ((profileType & mDefaultOutputDevice->mDeviceType) != AUDIO_DEVICE_NONE) { + profileType = mDefaultOutputDevice->mDeviceType; + } else { + profileType = outProfile->mSupportedDevices[0]->mDeviceType; + } + if ((profileType & outputDeviceTypes) && ((outProfile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) { sp outputDesc = new AudioOutputDescriptor(outProfile); - outputDesc->mDevice = (audio_devices_t)(mDefaultOutputDevice->mDeviceType & profileTypes); + outputDesc->mDevice = profileType; audio_io_handle_t output = mpClientInterface->openOutput( outProfile->mModule->mHandle, &outputDesc->mDevice, @@ -2322,12 +2339,12 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa continue; } - audio_devices_t profileTypes = inProfile->mSupportedDevices.types(); - if (profileTypes & inputDeviceTypes) { + audio_devices_t profileType = inProfile->mSupportedDevices[0]->mDeviceType; + if (profileType & inputDeviceTypes) { sp inputDesc = new AudioInputDescriptor(inProfile); inputDesc->mInputSource = AUDIO_SOURCE_MIC; - inputDesc->mDevice = inProfile->mSupportedDevices[0]->mDeviceType; + inputDesc->mDevice = profileType; audio_io_handle_t input = mpClientInterface->openInput( inProfile->mModule->mHandle, &inputDesc->mDevice, @@ -2659,7 +2676,8 @@ status_t AudioPolicyManager::checkOutputsForDevice(audio_devices_t device, continue; } - ALOGV("opening output for device %08x with params %s", device, address.string()); + ALOGV("opening output for device %08x with params %s profile %p", + device, address.string(), profile.get()); desc = new AudioOutputDescriptor(profile); desc->mDevice = device; audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER; @@ -5942,7 +5960,7 @@ void AudioPolicyManager::DeviceDescriptor::toAudioPortConfig( void AudioPolicyManager::DeviceDescriptor::toAudioPort(struct audio_port *port) const { - ALOGV("DeviceVector::toAudioPort() handle %d type %x", mId, mDeviceType); + ALOGV("DeviceDescriptor::toAudioPort() handle %d type %x", mId, mDeviceType); AudioPort::toAudioPort(port); port->id = mId; toAudioPortConfig(&port->active_config); -- cgit v1.1