summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2014-06-20 18:31:16 -0700
committerEric Laurent <elaurent@google.com>2014-07-24 02:56:47 +0000
commit83b8808faad1e91690c64d7007348be8d9ebde73 (patch)
treeb541b1172f804e04bd19b29f7878a1becf6205d7
parentc15c265676da2226a18a5373812608b19d4719d7 (diff)
downloadframeworks_av-83b8808faad1e91690c64d7007348be8d9ebde73.zip
frameworks_av-83b8808faad1e91690c64d7007348be8d9ebde73.tar.gz
frameworks_av-83b8808faad1e91690c64d7007348be8d9ebde73.tar.bz2
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
-rw-r--r--include/private/media/AudioTrackShared.h14
-rw-r--r--services/audioflinger/AudioFlinger.cpp227
-rw-r--r--services/audioflinger/AudioFlinger.h21
-rw-r--r--services/audioflinger/PatchPanel.cpp352
-rw-r--r--services/audioflinger/PatchPanel.h30
-rw-r--r--services/audioflinger/PlaybackTracks.h42
-rw-r--r--services/audioflinger/RecordTracks.h35
-rw-r--r--services/audioflinger/Threads.cpp90
-rw-r--r--services/audioflinger/Threads.h15
-rw-r--r--services/audioflinger/TrackBase.h35
-rw-r--r--services/audioflinger/Tracks.cpp224
-rw-r--r--services/audiopolicy/AudioPolicyManager.cpp38
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::PlaybackThread> 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<PlaybackThread> 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<PlaybackThread> 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<PlaybackThread> 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<RecordThread> 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::RecordThread> 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<RecordThread> 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<RecordThread> 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<RecordThread> 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<RecordThread> openInput_l(audio_module_handle_t module,
+ audio_devices_t device,
+ struct audio_config *config,
+ audio_input_flags_t flags);
+ sp<PlaybackThread> openOutput_l(audio_module_handle_t module,
+ audio_devices_t device,
+ struct audio_config *config,
+ audio_output_flags_t flags);
+
+ void closeOutputFinish(sp<PlaybackThread> thread);
+ void closeInputFinish(sp<RecordThread> 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<PlaybackThread> thread);
status_t closeInput_nonvirtual(audio_io_handle_t input);
+ void closeInputInternal_l(sp<RecordThread> 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> 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<ThreadBase> 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<ThreadBase> 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<ThreadBase> 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<ThreadBase> 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<ThreadBase> 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> 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<ThreadBase> 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>& 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<PlaybackThread> mPlaybackThread;
+ sp<PlaybackThread::PatchTrack> mPatchTrack;
+ sp<RecordThread> mRecordThread;
+ sp<RecordThread::PatchRecord> 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<AudioFlinger> mAudioFlinger;
- SortedVector <Patch *> mPatches;
+ const wp<AudioFlinger> mAudioFlinger;
+ SortedVector <Patch *> 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<IMemory>& 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<IMemory> 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<ClientProxy> 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<ClientProxy> 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<EffectModule>& 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::Track> AudioFlinger::PlaybackThread::createTrac
uint32_t strategy = AudioSystem::getStrategyForStream(streamType);
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> 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::Track> 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>& 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>& 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<PatchTrack>& track)
+{
+ Mutex::Autolock _l(mLock);
+ mTracks.add(track);
+}
+
+void AudioFlinger::PlaybackThread::deletePatchTrack(const sp<PatchTrack>& 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>& audioFlinger, AudioStreamOut* output,
@@ -5523,8 +5556,8 @@ sp<AudioFlinger::RecordThread::RecordTrack> 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<PatchRecord>& record)
+{
+ Mutex::Autolock _l(mLock);
+ mTracks.add(record);
+}
+
+void AudioFlinger::RecordThread::deletePatchRecord(const sp<PatchRecord>& 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<PatchTrack>& track);
+ void deletePatchTrack(const sp<PatchTrack>& 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<PatchRecord>& record);
+ void deletePatchRecord(const sp<PatchRecord>& 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<IMemory>& 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<SyncEvent>& event);
sp<IMemory> 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<NBAIO_Sink> mTeeSink;
sp<NBAIO_Source> 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<IMemory>& 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<IMemory>& 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<ThreadBase> 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<ThreadBase> 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<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
@@ -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<AudioOutputDescriptor> 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<AudioInputDescriptor> 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);