summaryrefslogtreecommitdiffstats
path: root/services/audioflinger
diff options
context:
space:
mode:
Diffstat (limited to 'services/audioflinger')
-rw-r--r--services/audioflinger/Android.mk21
-rw-r--r--services/audioflinger/AudioFlinger.cpp281
-rw-r--r--services/audioflinger/AudioFlinger.h46
-rw-r--r--services/audioflinger/AudioMixer.cpp1
-rw-r--r--services/audioflinger/AudioPolicyService.cpp158
-rw-r--r--services/audioflinger/AudioPolicyService.h39
-rw-r--r--services/audioflinger/AudioResampler.h11
-rw-r--r--services/audioflinger/AudioWatchdog.cpp5
-rw-r--r--services/audioflinger/Configuration.h47
-rw-r--r--services/audioflinger/Effects.cpp116
-rw-r--r--services/audioflinger/Effects.h14
-rw-r--r--services/audioflinger/FastMixer.cpp246
-rw-r--r--services/audioflinger/FastMixer.h19
-rw-r--r--services/audioflinger/FastMixerState.cpp1
-rw-r--r--services/audioflinger/PlaybackTracks.h22
-rw-r--r--services/audioflinger/RecordTracks.h2
-rw-r--r--services/audioflinger/ServiceUtilities.cpp16
-rw-r--r--services/audioflinger/ServiceUtilities.h2
-rw-r--r--services/audioflinger/StateQueue.cpp1
-rw-r--r--services/audioflinger/StateQueue.h8
-rw-r--r--services/audioflinger/StateQueueInstantiations.cpp1
-rw-r--r--services/audioflinger/Threads.cpp1316
-rw-r--r--services/audioflinger/Threads.h186
-rw-r--r--services/audioflinger/TrackBase.h23
-rw-r--r--services/audioflinger/Tracks.cpp683
25 files changed, 2393 insertions, 872 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 061a079..54377f1 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -27,9 +27,6 @@ LOCAL_SRC_FILES:= \
LOCAL_SRC_FILES += StateQueue.cpp
-# uncomment for debugging timing problems related to StateQueue::push()
-LOCAL_CFLAGS += -DSTATE_QUEUE_DUMP
-
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils)
@@ -56,24 +53,10 @@ LOCAL_STATIC_LIBRARIES := \
LOCAL_MODULE:= libaudioflinger
-LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp
-
-LOCAL_CFLAGS += -DFAST_MIXER_STATISTICS
-
-# uncomment to display CPU load adjusted for CPU frequency
-# LOCAL_CFLAGS += -DCPU_FREQUENCY_STATISTICS
+LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp AudioWatchdog.cpp
LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"'
-LOCAL_CFLAGS += -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
-
-# uncomment to allow tee sink debugging to be enabled by property
-# LOCAL_CFLAGS += -DTEE_SINK
-
-# uncomment to enable the audio watchdog
-# LOCAL_SRC_FILES += AudioWatchdog.cpp
-# LOCAL_CFLAGS += -DAUDIO_WATCHDOG
-
# Define ANDROID_SMP appropriately. Used to get inline tracing fast-path.
ifeq ($(TARGET_CPU_SMP),true)
LOCAL_CFLAGS += -DANDROID_SMP=1
@@ -81,6 +64,8 @@ else
LOCAL_CFLAGS += -DANDROID_SMP=0
endif
+LOCAL_CFLAGS += -fvisibility=hidden
+
include $(BUILD_SHARED_LIBRARY)
#
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 87eb6aa..a9c9b56 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -19,6 +19,7 @@
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <dirent.h>
#include <math.h>
#include <signal.h>
@@ -36,10 +37,6 @@
#include <cutils/bitops.h>
#include <cutils/properties.h>
-#include <cutils/compiler.h>
-
-//#include <private/media/AudioTrackShared.h>
-//#include <private/media/AudioEffectShared.h>
#include <system/audio.h>
#include <hardware/audio.h>
@@ -58,12 +55,13 @@
#include <powermanager/PowerManager.h>
#include <common_time/cc_helper.h>
-//#include <common_time/local_clock.h>
#include <media/IMediaLogService.h>
#include <media/nbaio/Pipe.h>
#include <media/nbaio/PipeReader.h>
+#include <media/AudioParameter.h>
+#include <private/android_filesystem_config.h>
// ----------------------------------------------------------------------------
@@ -100,6 +98,10 @@ size_t AudioFlinger::mTeeSinkOutputFrames = kTeeSinkOutputFramesDefault;
size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault;
#endif
+// In order to avoid invalidating offloaded tracks each time a Visualizer is turned on and off
+// we define a minimum time during which a global effect is considered enabled.
+static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200);
+
// ----------------------------------------------------------------------------
static int load_audio_interface(const char *if_name, audio_hw_device_t **dev)
@@ -141,7 +143,10 @@ AudioFlinger::AudioFlinger()
mMasterMute(false),
mNextUniqueId(1),
mMode(AUDIO_MODE_INVALID),
- mBtNrecIsOff(false)
+ mBtNrecIsOff(false),
+ mIsLowRamDevice(true),
+ mIsDeviceTypeKnown(false),
+ mGlobalEffectEnableTime(0)
{
getpid_cached = getpid();
char value[PROPERTY_VALUE_MAX];
@@ -259,6 +264,12 @@ void AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
}
}
+ result.append("Notification Clients:\n");
+ for (size_t i = 0; i < mNotificationClients.size(); ++i) {
+ snprintf(buffer, SIZE, " pid: %d\n", mNotificationClients.keyAt(i));
+ result.append(buffer);
+ }
+
result.append("Global session refs:\n");
result.append(" session pid count\n");
for (size_t i = 0; i < mAudioSessionRefs.size(); i++) {
@@ -436,6 +447,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
audio_io_handle_t output,
pid_t tid,
int *sessionId,
+ String8& name,
status_t *status)
{
sp<PlaybackThread::Track> track;
@@ -524,6 +536,9 @@ sp<IAudioTrack> AudioFlinger::createTrack(
}
}
if (lStatus == NO_ERROR) {
+ // s for server's pid, n for normal mixer name, f for fast index
+ name = String8::format("s:%d;n:%d;f:%d", getpid_cached, track->name() - AudioMixer::TRACK0,
+ track->fastIndex());
trackHandle = new TrackHandle(track);
} else {
// remove local strong reference to Client before deleting the Track so that the Client
@@ -981,11 +996,12 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t form
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
- struct audio_config config = {
- sample_rate: sampleRate,
- channel_mask: channelMask,
- format: format,
- };
+ struct audio_config config;
+ memset(&config, 0, sizeof(config));
+ config.sample_rate = sampleRate;
+ config.channel_mask = channelMask;
+ config.format = format;
+
audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
size_t size = dev->get_input_buffer_size(dev, &config);
mHardwareStatus = AUDIO_HW_IDLE;
@@ -1201,13 +1217,17 @@ void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who)
// ----------------------------------------------------------------------------
+static bool deviceRequiresCaptureAudioOutputPermission(audio_devices_t inDevice) {
+ return audio_is_remote_submix_device(inDevice);
+}
+
sp<IAudioRecord> AudioFlinger::openRecord(
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
- IAudioFlinger::track_flags_t flags,
+ IAudioFlinger::track_flags_t *flags,
pid_t tid,
int *sessionId,
status_t *status)
@@ -1222,19 +1242,34 @@ sp<IAudioRecord> AudioFlinger::openRecord(
// check calling permissions
if (!recordingAllowed()) {
+ ALOGE("openRecord() permission denied: recording not allowed");
lStatus = PERMISSION_DENIED;
goto Exit;
}
+ if (format != AUDIO_FORMAT_PCM_16_BIT) {
+ ALOGE("openRecord() invalid format %d", format);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
// add client to list
{ // scope for mLock
Mutex::Autolock _l(mLock);
thread = checkRecordThread_l(input);
if (thread == NULL) {
+ ALOGE("openRecord() checkRecordThread_l failed");
lStatus = BAD_VALUE;
goto Exit;
}
+ if (deviceRequiresCaptureAudioOutputPermission(thread->inDevice())
+ && !captureAudioOutputAllowed()) {
+ ALOGE("openRecord() permission denied: capture not allowed");
+ lStatus = PERMISSION_DENIED;
+ goto Exit;
+ }
+
pid_t pid = IPCThreadState::self()->getCallingPid();
client = registerPid_l(pid);
@@ -1251,6 +1286,7 @@ sp<IAudioRecord> AudioFlinger::openRecord(
// The record track uses one track in mHardwareMixerThread by convention.
recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
frameCount, lSessionId, flags, tid, &lStatus);
+ LOG_ALWAYS_FATAL_IF((recordTrack != 0) != (lStatus == NO_ERROR));
}
if (lStatus != NO_ERROR) {
// remove local strong reference to Client before deleting the RecordTrack so that the
@@ -1382,31 +1418,53 @@ size_t AudioFlinger::getPrimaryOutputFrameCount()
// ----------------------------------------------------------------------------
+status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice)
+{
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (uid != AID_SYSTEM) {
+ return PERMISSION_DENIED;
+ }
+ Mutex::Autolock _l(mLock);
+ if (mIsDeviceTypeKnown) {
+ return INVALID_OPERATION;
+ }
+ mIsLowRamDevice = isLowRamDevice;
+ mIsDeviceTypeKnown = true;
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
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)
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo)
{
- status_t status;
PlaybackThread *thread = NULL;
- struct audio_config config = {
- sample_rate: pSamplingRate ? *pSamplingRate : 0,
- channel_mask: pChannelMask ? *pChannelMask : 0,
- format: pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT,
- };
+ struct audio_config 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) {
+ config.offload_info = *offloadInfo;
+ }
+
audio_stream_out_t *outStream = NULL;
AudioHwDevice *outHwDev;
- ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %d, Channels %x, flags %x",
+ 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 == 0) {
return 0;
@@ -1423,7 +1481,7 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
- status = hwDevHal->open_output_stream(hwDevHal,
+ status_t status = hwDevHal->open_output_stream(hwDevHal,
id,
*pDevices,
(audio_output_flags_t)flags,
@@ -1431,7 +1489,7 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
&outStream);
mHardwareStatus = AUDIO_HW_IDLE;
- ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %d, "
+ ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %#08x, "
"Channels %x, status %d",
outStream,
config.sample_rate,
@@ -1440,9 +1498,12 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
status);
if (status == NO_ERROR && outStream != NULL) {
- AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream);
+ AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream, flags);
- if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||
+ if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+ thread = new OffloadThread(this, output, id, *pDevices);
+ ALOGV("openOutput() created offload output: ID %d thread %p", id, thread);
+ } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||
(config.format != AUDIO_FORMAT_PCM_16_BIT) ||
(config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {
thread = new DirectOutputThread(this, output, id, *pDevices);
@@ -1453,10 +1514,18 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
}
mPlaybackThreads.add(id, thread);
- if (pSamplingRate != NULL) *pSamplingRate = config.sample_rate;
- if (pFormat != NULL) *pFormat = config.format;
- if (pChannelMask != NULL) *pChannelMask = config.channel_mask;
- if (pLatencyMs != NULL) *pLatencyMs = thread->latency();
+ if (pSamplingRate != NULL) {
+ *pSamplingRate = config.sample_rate;
+ }
+ if (pFormat != NULL) {
+ *pFormat = config.format;
+ }
+ if (pChannelMask != NULL) {
+ *pChannelMask = config.channel_mask;
+ }
+ if (pLatencyMs != NULL) {
+ *pLatencyMs = thread->latency();
+ }
// notify client processes of the new output creation
thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
@@ -1524,11 +1593,28 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output)
DuplicatingThread *dupThread =
(DuplicatingThread *)mPlaybackThreads.valueAt(i).get();
dupThread->removeOutputTrack((MixerThread *)thread.get());
+
}
}
}
- audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL);
+
+
mPlaybackThreads.removeItem(output);
+ // save all effects to the default thread
+ if (mPlaybackThreads.size()) {
+ PlaybackThread *dstThread = checkPlaybackThread_l(mPlaybackThreads.keyAt(0));
+ if (dstThread != NULL) {
+ // audioflinger lock is held here so the acquisition order of thread locks does not
+ // matter
+ Mutex::Autolock _dl(dstThread->mLock);
+ Mutex::Autolock _sl(thread->mLock);
+ Vector< sp<EffectChain> > effectChains = thread->getEffectChains_l();
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ moveEffectChain_l(effectChains[i]->sessionId(), thread.get(), dstThread, true);
+ }
+ }
+ }
+ audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL);
}
thread->exit();
// The thread entity (active unit of execution) is no longer running here,
@@ -1583,11 +1669,11 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
{
status_t status;
RecordThread *thread = NULL;
- struct audio_config config = {
- sample_rate: pSamplingRate ? *pSamplingRate : 0,
- channel_mask: pChannelMask ? *pChannelMask : 0,
- format: pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT,
- };
+ struct audio_config config;
+ config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0;
+ config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0;
+ config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT;
+
uint32_t reqSamplingRate = config.sample_rate;
audio_format_t reqFormat = config.format;
audio_channel_mask_t reqChannels = config.channel_mask;
@@ -1683,7 +1769,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
AudioStreamIn *input = new AudioStreamIn(inHwDev, inStream);
// Start record thread
- // RecorThread require both input and output device indication to forward to audio
+ // RecordThread requires both input and output device indication to forward to audio
// pre processing modules
thread = new RecordThread(this,
input,
@@ -1698,9 +1784,15 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
);
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 = reqChannels;
+ if (pSamplingRate != NULL) {
+ *pSamplingRate = reqSamplingRate;
+ }
+ if (pFormat != NULL) {
+ *pFormat = config.format;
+ }
+ if (pChannelMask != NULL) {
+ *pChannelMask = reqChannels;
+ }
// notify client processes of the new input creation
thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED);
@@ -1768,6 +1860,16 @@ void AudioFlinger::acquireAudioSessionId(int audioSession)
Mutex::Autolock _l(mLock);
pid_t caller = IPCThreadState::self()->getCallingPid();
ALOGV("acquiring %d from %d", audioSession, caller);
+
+ // Ignore requests received from processes not known as notification client. The request
+ // is likely proxied by mediaserver (e.g CameraService) and releaseAudioSessionId() can be
+ // called from a different pid leaving a stale session reference. Also we don't know how
+ // to clear this reference if the client process dies.
+ if (mNotificationClients.indexOfKey(caller) < 0) {
+ ALOGV("acquireAudioSessionId() unknown client %d for session %d", caller, audioSession);
+ return;
+ }
+
size_t num = mAudioSessionRefs.size();
for (size_t i = 0; i< num; i++) {
AudioSessionRef *ref = mAudioSessionRefs.editItemAt(i);
@@ -1800,7 +1902,9 @@ void AudioFlinger::releaseAudioSessionId(int audioSession)
return;
}
}
- ALOGW("session id %d not found for pid %d", audioSession, caller);
+ // If the caller is mediaserver it is likely that the session being released was acquired
+ // on behalf of a process not in notification clients and we ignore the warning.
+ ALOGW_IF(caller != getpid_cached, "session id %d not found for pid %d", audioSession, caller);
}
void AudioFlinger::purgeStaleEffects_l() {
@@ -2001,24 +2105,7 @@ sp<IEffect> AudioFlinger::createEffect(
goto Exit;
}
- if (io == 0) {
- if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
- // output must be specified by AudioPolicyManager when using session
- // AUDIO_SESSION_OUTPUT_STAGE
- lStatus = BAD_VALUE;
- goto Exit;
- } else if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
- // if the output returned by getOutputForEffect() is removed before we lock the
- // mutex below, the call to checkPlaybackThread_l(io) below will detect it
- // and we will exit safely
- io = AudioSystem::getOutputForEffect(&desc);
- }
- }
-
{
- Mutex::Autolock _l(mLock);
-
-
if (!EffectIsNullUuid(&pDesc->uuid)) {
// if uuid is specified, request effect descriptor
lStatus = EffectGetDescriptor(&pDesc->uuid, &desc);
@@ -2091,6 +2178,15 @@ sp<IEffect> AudioFlinger::createEffect(
// return effect descriptor
*pDesc = desc;
+ if (io == 0 && sessionId == AUDIO_SESSION_OUTPUT_MIX) {
+ // if the output returned by getOutputForEffect() is removed before we lock the
+ // mutex below, the call to checkPlaybackThread_l(io) below will detect it
+ // and we will exit safely
+ io = AudioSystem::getOutputForEffect(&desc);
+ ALOGV("createEffect got output %d", io);
+ }
+
+ Mutex::Autolock _l(mLock);
// If output is not specified try to find a matching audio session ID in one of the
// output threads.
@@ -2098,6 +2194,12 @@ sp<IEffect> AudioFlinger::createEffect(
// because of code checking output when entering the function.
// Note: io is never 0 when creating an effect on an input
if (io == 0) {
+ if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
+ // output must be specified by AudioPolicyManager when using session
+ // AUDIO_SESSION_OUTPUT_STAGE
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
// look for the thread where the specified audio session is present
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) {
@@ -2171,9 +2273,7 @@ status_t AudioFlinger::moveEffects(int sessionId, audio_io_handle_t srcOutput,
Mutex::Autolock _dl(dstThread->mLock);
Mutex::Autolock _sl(srcThread->mLock);
- moveEffectChain_l(sessionId, srcThread, dstThread, false);
-
- return NO_ERROR;
+ return moveEffectChain_l(sessionId, srcThread, dstThread, false);
}
// moveEffectChain_l must be called with both srcThread and dstThread mLocks held
@@ -2200,13 +2300,18 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId,
// transfer all effects one by one so that new effect chain is created on new thread with
// correct buffer sizes and audio parameters and effect engines reconfigured accordingly
- audio_io_handle_t dstOutput = dstThread->id();
sp<EffectChain> dstChain;
uint32_t strategy = 0; // prevent compiler warning
sp<EffectModule> effect = chain->getEffectFromId_l(0);
+ Vector< sp<EffectModule> > removed;
+ status_t status = NO_ERROR;
while (effect != 0) {
srcThread->removeEffect_l(effect);
- dstThread->addEffect_l(effect);
+ removed.add(effect);
+ status = dstThread->addEffect_l(effect);
+ if (status != NO_ERROR) {
+ break;
+ }
// removeEffect_l() has stopped the effect if it was active so it must be restarted
if (effect->state() == EffectModule::ACTIVE ||
effect->state() == EffectModule::STOPPING) {
@@ -2218,15 +2323,15 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId,
dstChain = effect->chain().promote();
if (dstChain == 0) {
ALOGW("moveEffectChain_l() cannot get chain from effect %p", effect.get());
- srcThread->addEffect_l(effect);
- return NO_INIT;
+ status = NO_INIT;
+ break;
}
strategy = dstChain->strategy();
}
if (reRegister) {
AudioSystem::unregisterEffect(effect->id());
AudioSystem::registerEffect(&effect->desc(),
- dstOutput,
+ dstThread->id(),
strategy,
sessionId,
effect->id());
@@ -2234,7 +2339,53 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId,
effect = chain->getEffectFromId_l(0);
}
- return NO_ERROR;
+ if (status != NO_ERROR) {
+ for (size_t i = 0; i < removed.size(); i++) {
+ srcThread->addEffect_l(removed[i]);
+ if (dstChain != 0 && reRegister) {
+ AudioSystem::unregisterEffect(removed[i]->id());
+ AudioSystem::registerEffect(&removed[i]->desc(),
+ srcThread->id(),
+ strategy,
+ sessionId,
+ removed[i]->id());
+ }
+ }
+ }
+
+ return status;
+}
+
+bool AudioFlinger::isNonOffloadableGlobalEffectEnabled_l()
+{
+ if (mGlobalEffectEnableTime != 0 &&
+ ((systemTime() - mGlobalEffectEnableTime) < kMinGlobalEffectEnabletimeNs)) {
+ return true;
+ }
+
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ sp<EffectChain> ec =
+ mPlaybackThreads.valueAt(i)->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
+ if (ec != 0 && ec->isNonOffloadableEnabled()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void AudioFlinger::onNonOffloadableGlobalEffectEnable()
+{
+ Mutex::Autolock _l(mLock);
+
+ mGlobalEffectEnableTime = systemTime();
+
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
+ if (t->mType == ThreadBase::OFFLOAD) {
+ t->invalidateTracks(AUDIO_STREAM_MUSIC);
+ }
+ }
+
}
struct Entry {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index b0efef6..2aeb263 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -24,6 +24,8 @@
#include <common_time/cc_helper.h>
+#include <cutils/compiler.h>
+
#include <media/IAudioFlinger.h>
#include <media/IAudioFlingerClient.h>
#include <media/IAudioTrack.h>
@@ -54,6 +56,7 @@
#include <powermanager/IPowerManager.h>
#include <media/nbaio/NBLog.h>
+#include <private/media/AudioTrackShared.h>
namespace android {
@@ -89,7 +92,7 @@ class AudioFlinger :
{
friend class BinderService<AudioFlinger>; // for AudioFlinger()
public:
- static const char* getServiceName() { return "media.audio_flinger"; }
+ static const char* getServiceName() ANDROID_API { return "media.audio_flinger"; }
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -105,6 +108,7 @@ public:
audio_io_handle_t output,
pid_t tid,
int *sessionId,
+ String8& name,
status_t *status);
virtual sp<IAudioRecord> openRecord(
@@ -113,7 +117,7 @@ public:
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
- IAudioFlinger::track_flags_t flags,
+ IAudioFlinger::track_flags_t *flags,
pid_t tid,
int *sessionId,
status_t *status);
@@ -157,7 +161,8 @@ public:
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_output_flags_t flags);
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo);
virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
audio_io_handle_t output2);
@@ -216,6 +221,8 @@ public:
virtual uint32_t getPrimaryOutputSamplingRate();
virtual size_t getPrimaryOutputFrameCount();
+ virtual status_t setLowRamDevice(bool isLowRamDevice);
+
virtual status_t onTransact(
uint32_t code,
const Parcel& data,
@@ -278,7 +285,7 @@ private:
bool btNrecIsOff() const { return mBtNrecIsOff; }
- AudioFlinger();
+ AudioFlinger() ANDROID_API;
virtual ~AudioFlinger();
// call in any IAudioFlinger method that accesses mPrimaryHardwareDev
@@ -359,7 +366,9 @@ private:
class PlaybackThread;
class MixerThread;
class DirectOutputThread;
+ class OffloadThread;
class DuplicatingThread;
+ class AsyncCallbackThread;
class Track;
class RecordTrack;
class EffectModule;
@@ -401,8 +410,13 @@ private:
int64_t pts);
virtual status_t setMediaTimeTransform(const LinearTransform& xform,
int target);
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual status_t getTimestamp(AudioTimestamp& timestamp);
+ virtual void signal(); // signal playback thread for a change in control block
+
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
+
private:
const sp<PlaybackThread::Track> mTrack;
};
@@ -424,6 +438,7 @@ private:
void stop_nonvirtual();
};
+
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;
@@ -452,6 +467,9 @@ private:
void removeClient_l(pid_t pid);
void removeNotificationClient(pid_t pid);
+ bool isNonOffloadableGlobalEffectEnabled_l();
+ void onNonOffloadableGlobalEffectEnable();
+
class AudioHwDevice {
public:
enum Flags {
@@ -490,11 +508,12 @@ private:
struct AudioStreamOut {
AudioHwDevice* const audioHwDev;
audio_stream_out_t* const stream;
+ audio_output_flags_t flags;
audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); }
- AudioStreamOut(AudioHwDevice *dev, audio_stream_out_t *out) :
- audioHwDev(dev), stream(out) {}
+ AudioStreamOut(AudioHwDevice *dev, audio_stream_out_t *out, audio_output_flags_t flags) :
+ audioHwDev(dev), stream(out), flags(flags) {}
};
struct AudioStreamIn {
@@ -588,12 +607,11 @@ private:
status_t closeOutput_nonvirtual(audio_io_handle_t output);
status_t closeInput_nonvirtual(audio_io_handle_t input);
-// do not use #ifdef here, since AudioFlinger.h is included by more than one module
-//#ifdef TEE_SINK
+#ifdef TEE_SINK
// all record threads serially share a common tee sink, which is re-created on format change
sp<NBAIO_Sink> mRecordTeeSink;
sp<NBAIO_Source> mRecordTeeSource;
-//#endif
+#endif
public:
@@ -618,6 +636,16 @@ public:
static const size_t kTeeSinkTrackFramesDefault = 0x1000;
#endif
+ // This method reads from a variable without mLock, but the variable is updated under mLock. So
+ // we might read a stale value, or a value that's inconsistent with respect to other variables.
+ // In this case, it's safe because the return value isn't used for making an important decision.
+ // The reason we don't want to take mLock is because it could block the caller for a long time.
+ bool isLowRamDevice() const { return mIsLowRamDevice; }
+
+private:
+ bool mIsLowRamDevice;
+ bool mIsDeviceTypeKnown;
+ nsecs_t mGlobalEffectEnableTime; // when a global effect was last enabled
};
#undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 7d38f80..df4e029 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "AudioMixer"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 2706880..35e816b 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "AudioPolicyService"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#undef __STRICT_ANSI__
#define __STDINT_LIMITS
#define __STDC_LIMIT_MACROS
@@ -40,6 +41,7 @@
#include <system/audio_policy.h>
#include <hardware/audio_policy.h>
#include <audio_effects/audio_effects_conf.h>
+#include <media/AudioParameter.h>
namespace android {
@@ -49,7 +51,7 @@ static const char kCmdDeadlockedString[] = "AudioPolicyService command thread ma
static const int kDumpLockRetries = 50;
static const int kDumpLockSleepUs = 20000;
-static const nsecs_t kAudioCommandTimeout = 3000000000; // 3 seconds
+static const nsecs_t kAudioCommandTimeout = 3000000000LL; // 3 seconds
namespace {
extern struct audio_policy_service_ops aps_ops;
@@ -68,10 +70,11 @@ AudioPolicyService::AudioPolicyService()
Mutex::Autolock _l(mLock);
// start tone playback thread
- mTonePlaybackThread = new AudioCommandThread(String8(""));
+ mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this);
// start audio commands thread
- mAudioCommandThread = new AudioCommandThread(String8("ApmCommand"));
-
+ mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);
+ // start output activity command thread
+ mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);
/* instantiate the audio policy manager */
rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
if (rc)
@@ -222,15 +225,16 @@ audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- audio_output_flags_t flags)
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo)
{
if (mpAudioPolicy == NULL) {
return 0;
}
ALOGV("getOutput()");
Mutex::Autolock _l(mLock);
- return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, format, channelMask,
- flags);
+ return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate,
+ format, channelMask, flags, offloadInfo);
}
status_t AudioPolicyService::startOutput(audio_io_handle_t output,
@@ -253,6 +257,15 @@ status_t AudioPolicyService::stopOutput(audio_io_handle_t output,
return NO_INIT;
}
ALOGV("stopOutput()");
+ mOutputCommandThread->stopOutputCommand(output, stream, session);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::doStopOutput(audio_io_handle_t output,
+ audio_stream_type_t stream,
+ int session)
+{
+ ALOGV("doStopOutput from tid %d", gettid());
Mutex::Autolock _l(mLock);
return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session);
}
@@ -263,6 +276,12 @@ void AudioPolicyService::releaseOutput(audio_io_handle_t output)
return;
}
ALOGV("releaseOutput()");
+ mOutputCommandThread->releaseOutputCommand(output);
+}
+
+void AudioPolicyService::doReleaseOutput(audio_io_handle_t output)
+{
+ ALOGV("doReleaseOutput from tid %d", gettid());
Mutex::Autolock _l(mLock);
mpAudioPolicy->release_output(mpAudioPolicy, output);
}
@@ -277,9 +296,14 @@ audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource,
return 0;
}
// already checked by client, but double-check in case the client wrapper is bypassed
- if (uint32_t(inputSource) >= AUDIO_SOURCE_CNT) {
+ if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD) {
return 0;
}
+
+ if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
+ return 0;
+ }
+
Mutex::Autolock _l(mLock);
// the audio_in_acoustics_t parameter is ignored by get_input()
audio_io_handle_t input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate,
@@ -289,7 +313,10 @@ audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource,
return input;
}
// create audio pre processors according to input source
- ssize_t index = mInputSources.indexOfKey(inputSource);
+ audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ?
+ AUDIO_SOURCE_VOICE_RECOGNITION : inputSource;
+
+ ssize_t index = mInputSources.indexOfKey(aliasSource);
if (index < 0) {
return input;
}
@@ -638,8 +665,9 @@ status_t AudioPolicyService::onTransact(
// ----------- AudioPolicyService::AudioCommandThread implementation ----------
-AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name)
- : Thread(false), mName(name)
+AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name,
+ const wp<AudioPolicyService>& service)
+ : Thread(false), mName(name), mService(service)
{
mpToneGenerator = NULL;
}
@@ -647,7 +675,7 @@ AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name)
AudioPolicyService::AudioCommandThread::~AudioCommandThread()
{
- if (mName != "" && !mAudioCommands.isEmpty()) {
+ if (!mAudioCommands.isEmpty()) {
release_wake_lock(mName.string());
}
mAudioCommands.clear();
@@ -656,11 +684,7 @@ AudioPolicyService::AudioCommandThread::~AudioCommandThread()
void AudioPolicyService::AudioCommandThread::onFirstRef()
{
- if (mName != "") {
- run(mName.string(), ANDROID_PRIORITY_AUDIO);
- } else {
- run("AudioCommand", ANDROID_PRIORITY_AUDIO);
- }
+ run(mName.string(), ANDROID_PRIORITY_AUDIO);
}
bool AudioPolicyService::AudioCommandThread::threadLoop()
@@ -735,6 +759,32 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
}
delete data;
}break;
+ case STOP_OUTPUT: {
+ StopOutputData *data = (StopOutputData *)command->mParam;
+ ALOGV("AudioCommandThread() processing stop output %d",
+ data->mIO);
+ sp<AudioPolicyService> svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doStopOutput(data->mIO, data->mStream, data->mSession);
+ mLock.lock();
+ delete data;
+ }break;
+ case RELEASE_OUTPUT: {
+ ReleaseOutputData *data = (ReleaseOutputData *)command->mParam;
+ ALOGV("AudioCommandThread() processing release output %d",
+ data->mIO);
+ sp<AudioPolicyService> svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doReleaseOutput(data->mIO);
+ mLock.lock();
+ delete data;
+ }break;
default:
ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
}
@@ -746,7 +796,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
}
}
// release delayed commands wake lock
- if (mName != "" && mAudioCommands.isEmpty()) {
+ if (mAudioCommands.isEmpty()) {
release_wake_lock(mName.string());
}
ALOGV("AudioCommandThread() going to sleep");
@@ -890,17 +940,45 @@ status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume
return status;
}
+void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t output,
+ audio_stream_type_t stream,
+ int session)
+{
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = STOP_OUTPUT;
+ StopOutputData *data = new StopOutputData();
+ data->mIO = output;
+ data->mStream = stream;
+ data->mSession = session;
+ command->mParam = (void *)data;
+ Mutex::Autolock _l(mLock);
+ insertCommand_l(command);
+ ALOGV("AudioCommandThread() adding stop output %d", output);
+ mWaitWorkCV.signal();
+}
+
+void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handle_t output)
+{
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = RELEASE_OUTPUT;
+ ReleaseOutputData *data = new ReleaseOutputData();
+ data->mIO = output;
+ command->mParam = (void *)data;
+ Mutex::Autolock _l(mLock);
+ insertCommand_l(command);
+ ALOGV("AudioCommandThread() adding release output %d", output);
+ mWaitWorkCV.signal();
+}
+
// insertCommand_l() must be called with mLock held
void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
{
ssize_t i; // not size_t because i will count down to -1
Vector <AudioCommand *> removedCommands;
-
- nsecs_t time = 0;
command->mTime = systemTime() + milliseconds(delayMs);
// acquire wake lock to make sure delayed commands are processed
- if (mName != "" && mAudioCommands.isEmpty()) {
+ if (mAudioCommands.isEmpty()) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string());
}
@@ -942,7 +1020,10 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
} else {
data2->mKeyValuePairs = param2.toString();
}
- time = command2->mTime;
+ command->mTime = command2->mTime;
+ // force delayMs to non 0 so that code below does not request to wait for
+ // command status as the command is now delayed
+ delayMs = 1;
} break;
case SET_VOLUME: {
@@ -953,7 +1034,10 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
ALOGV("Filtering out volume command on output %d for stream %d",
data->mIO, data->mStream);
removedCommands.add(command2);
- time = command2->mTime;
+ command->mTime = command2->mTime;
+ // force delayMs to non 0 so that code below does not request to wait for
+ // command status as the command is now delayed
+ delayMs = 1;
} break;
case START_TONE:
case STOP_TONE:
@@ -975,16 +1059,12 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
}
removedCommands.clear();
- // wait for status only if delay is 0 and command time was not modified above
- if (delayMs == 0 && time == 0) {
+ // wait for status only if delay is 0
+ if (delayMs == 0) {
command->mWaitStatus = true;
} else {
command->mWaitStatus = false;
}
- // update command time if modified above
- if (time != 0) {
- command->mTime = time;
- }
// insert command at the right place according to its time stamp
ALOGV("inserting command: %d at index %d, num commands %d",
@@ -1055,6 +1135,21 @@ int AudioPolicyService::setVoiceVolume(float volume, int delayMs)
return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs);
}
+bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info)
+{
+ if (mpAudioPolicy == NULL) {
+ ALOGV("mpAudioPolicy == NULL");
+ return false;
+ }
+
+ if (mpAudioPolicy->is_offload_supported == NULL) {
+ ALOGV("HAL does not implement is_offload_supported");
+ return false;
+ }
+
+ return mpAudioPolicy->is_offload_supported(mpAudioPolicy, &info);
+}
+
// ----------------------------------------------------------------------------
// Audio pre-processing configuration
// ----------------------------------------------------------------------------
@@ -1387,7 +1482,8 @@ static audio_io_handle_t aps_open_output_on_module(void *service,
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_output_flags_t flags)
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
@@ -1395,7 +1491,7 @@ static audio_io_handle_t aps_open_output_on_module(void *service,
return 0;
}
return af->openOutput(module, pDevices, pSamplingRate, pFormat, pChannelMask,
- pLatencyMs, flags);
+ pLatencyMs, flags, offloadInfo);
}
static audio_io_handle_t aps_open_dup_output(void *service,
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 35cf368..ae053a9 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -19,6 +19,7 @@
#include <cutils/misc.h>
#include <cutils/config_utils.h>
+#include <cutils/compiler.h>
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/SortedVector.h>
@@ -44,7 +45,7 @@ class AudioPolicyService :
public:
// for BinderService
- static const char *getServiceName() { return "media.audio_policy"; }
+ static const char *getServiceName() ANDROID_API { return "media.audio_policy"; }
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -66,7 +67,8 @@ public:
audio_format_t format = AUDIO_FORMAT_DEFAULT,
audio_channel_mask_t channelMask = 0,
audio_output_flags_t flags =
- AUDIO_OUTPUT_FLAG_NONE);
+ AUDIO_OUTPUT_FLAG_NONE,
+ const audio_offload_info_t *offloadInfo = NULL);
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
int session = 0);
@@ -135,9 +137,15 @@ public:
virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream);
virtual status_t stopTone();
virtual status_t setVoiceVolume(float volume, int delayMs = 0);
+ virtual bool isOffloadSupported(const audio_offload_info_t &config);
+
+ status_t doStopOutput(audio_io_handle_t output,
+ audio_stream_type_t stream,
+ int session = 0);
+ void doReleaseOutput(audio_io_handle_t output);
private:
- AudioPolicyService();
+ AudioPolicyService() ANDROID_API;
virtual ~AudioPolicyService();
status_t dumpInternals(int fd);
@@ -158,10 +166,12 @@ private:
STOP_TONE,
SET_VOLUME,
SET_PARAMETERS,
- SET_VOICE_VOLUME
+ SET_VOICE_VOLUME,
+ STOP_OUTPUT,
+ RELEASE_OUTPUT
};
- AudioCommandThread (String8 name);
+ AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
virtual ~AudioCommandThread();
status_t dump(int fd);
@@ -179,6 +189,11 @@ private:
status_t parametersCommand(audio_io_handle_t ioHandle,
const char *keyValuePairs, int delayMs = 0);
status_t voiceVolumeCommand(float volume, int delayMs = 0);
+ void stopOutputCommand(audio_io_handle_t output,
+ audio_stream_type_t stream,
+ int session);
+ void releaseOutputCommand(audio_io_handle_t output);
+
void insertCommand_l(AudioCommand *command, int delayMs = 0);
private:
@@ -223,12 +238,25 @@ private:
float mVolume;
};
+ class StopOutputData {
+ public:
+ audio_io_handle_t mIO;
+ audio_stream_type_t mStream;
+ int mSession;
+ };
+
+ class ReleaseOutputData {
+ public:
+ audio_io_handle_t mIO;
+ };
+
Mutex mLock;
Condition mWaitWorkCV;
Vector <AudioCommand *> mAudioCommands; // list of pending commands
ToneGenerator *mpToneGenerator; // the tone generator
AudioCommand mLastCommand; // last processed command (used by dump)
String8 mName; // string used by wake lock fo delayed commands
+ wp<AudioPolicyService> mService;
};
class EffectDesc {
@@ -313,6 +341,7 @@ private:
// device connection state or routing
sp<AudioCommandThread> mAudioCommandThread; // audio commands thread
sp<AudioCommandThread> mTonePlaybackThread; // tone playback thread
+ sp<AudioCommandThread> mOutputCommandThread; // process stop and release output
struct audio_policy_device *mpAudioPolicyDev;
struct audio_policy *mpAudioPolicy;
KeyedVector< audio_source_t, InputSourceDesc* > mInputSources;
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index 2b8694f..33e64ce 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -19,13 +19,14 @@
#include <stdint.h>
#include <sys/types.h>
+#include <cutils/compiler.h>
#include <media/AudioBufferProvider.h>
namespace android {
// ----------------------------------------------------------------------------
-class AudioResampler {
+class ANDROID_API AudioResampler {
public:
// Determines quality of SRC.
// LOW_QUALITY: linear interpolator (1st order)
@@ -55,6 +56,14 @@ public:
// set the PTS of the next buffer output by the resampler
virtual void setPTS(int64_t pts);
+ // Resample int16_t samples from provider and accumulate into 'out'.
+ // A mono provider delivers a sequence of samples.
+ // A stereo provider delivers a sequence of interleaved pairs of samples.
+ // Multi-channel providers are not supported.
+ // In either case, 'out' holds interleaved pairs of fixed-point signed Q19.12.
+ // That is, for a mono provider, there is an implicit up-channeling.
+ // Since this method accumulates, the caller is responsible for clearing 'out' initially.
+ // FIXME assumes provider is always successful; it should return the actual frame count.
virtual void resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) = 0;
diff --git a/services/audioflinger/AudioWatchdog.cpp b/services/audioflinger/AudioWatchdog.cpp
index 8f328ee..93d185e 100644
--- a/services/audioflinger/AudioWatchdog.cpp
+++ b/services/audioflinger/AudioWatchdog.cpp
@@ -17,9 +17,12 @@
#define LOG_TAG "AudioWatchdog"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <utils/Log.h>
#include "AudioWatchdog.h"
+#ifdef AUDIO_WATCHDOG
+
namespace android {
void AudioWatchdogDump::dump(int fd)
@@ -132,3 +135,5 @@ void AudioWatchdog::setDump(AudioWatchdogDump *dump)
}
} // namespace android
+
+#endif // AUDIO_WATCHDOG
diff --git a/services/audioflinger/Configuration.h b/services/audioflinger/Configuration.h
new file mode 100644
index 0000000..bc2038a
--- /dev/null
+++ b/services/audioflinger/Configuration.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Put build-time configuration options here rather than Android.mk,
+// so that the instantiate for AudioFlinger service will pick up the same options.
+
+#ifndef ANDROID_AUDIOFLINGER_CONFIGURATION_H
+#define ANDROID_AUDIOFLINGER_CONFIGURATION_H
+
+// uncomment to enable detailed battery usage reporting (not debugged)
+//#define ADD_BATTERY_DATA
+
+// uncomment to enable the audio watchdog
+//#define AUDIO_WATCHDOG
+
+// uncomment to display CPU load adjusted for CPU frequency
+//#define CPU_FREQUENCY_STATISTICS
+
+// uncomment to enable fast mixer to take performance samples for later statistical analysis
+#define FAST_MIXER_STATISTICS
+
+// uncomment to allow fast tracks at non-native sample rate
+//#define FAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
+
+// uncomment for debugging timing problems related to StateQueue::push()
+//#define STATE_QUEUE_DUMP
+
+// uncomment to allow tee sink debugging to be enabled by property
+//#define TEE_SINK
+
+// uncomment to log CPU statistics every n wall clock seconds
+//#define DEBUG_CPU_USAGE 10
+
+#endif // ANDROID_AUDIOFLINGER_CONFIGURATION_H
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 942ea35..a8a5169 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -19,6 +19,7 @@
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <utils/Log.h>
#include <audio_effects/effect_visualizer.h>
#include <audio_utils/primitives.h>
@@ -94,16 +95,7 @@ AudioFlinger::EffectModule::~EffectModule()
{
ALOGV("Destructor %p", this);
if (mEffectInterface != NULL) {
- if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
- (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- audio_stream_t *stream = thread->stream();
- if (stream != NULL) {
- stream->remove_audio_effect(stream, mEffectInterface);
- }
- }
- }
+ remove_effect_from_hal_l();
// release effect engine
EffectRelease(mEffectInterface);
}
@@ -487,7 +479,7 @@ status_t AudioFlinger::EffectModule::stop_l()
if (mStatus != NO_ERROR) {
return mStatus;
}
- status_t cmdStatus;
+ status_t cmdStatus = NO_ERROR;
uint32_t size = sizeof(status_t);
status_t status = (*mEffectInterface)->command(mEffectInterface,
EFFECT_CMD_DISABLE,
@@ -495,12 +487,19 @@ status_t AudioFlinger::EffectModule::stop_l()
NULL,
&size,
&cmdStatus);
- if (status == 0) {
+ if (status == NO_ERROR) {
status = cmdStatus;
}
- if (status == 0 &&
- ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
- (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC)) {
+ if (status == NO_ERROR) {
+ status = remove_effect_from_hal_l();
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::remove_effect_from_hal_l()
+{
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
+ (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
audio_stream_t *stream = thread->stream();
@@ -509,7 +508,7 @@ status_t AudioFlinger::EffectModule::stop_l()
}
}
}
- return status;
+ return NO_ERROR;
}
status_t AudioFlinger::EffectModule::command(uint32_t cmdCode,
@@ -765,6 +764,46 @@ bool AudioFlinger::EffectModule::purgeHandles()
return enabled;
}
+status_t AudioFlinger::EffectModule::setOffloaded(bool offloaded, audio_io_handle_t io)
+{
+ Mutex::Autolock _l(mLock);
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ status_t status = NO_ERROR;
+ if ((mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0) {
+ status_t cmdStatus;
+ uint32_t size = sizeof(status_t);
+ effect_offload_param_t cmd;
+
+ cmd.isOffload = offloaded;
+ cmd.ioHandle = io;
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_OFFLOAD,
+ sizeof(effect_offload_param_t),
+ &cmd,
+ &size,
+ &cmdStatus);
+ if (status == NO_ERROR) {
+ status = cmdStatus;
+ }
+ mOffloaded = (status == NO_ERROR) ? offloaded : false;
+ } else {
+ if (offloaded) {
+ status = INVALID_OPERATION;
+ }
+ mOffloaded = false;
+ }
+ ALOGV("setOffloaded() offloaded %d io %d status %d", offloaded, io, status);
+ return status;
+}
+
+bool AudioFlinger::EffectModule::isOffloaded() const
+{
+ Mutex::Autolock _l(mLock);
+ return mOffloaded;
+}
+
void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
@@ -932,6 +971,23 @@ status_t AudioFlinger::EffectHandle::enable()
thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
}
mEnabled = false;
+ } else {
+ if (thread != 0) {
+ if (thread->type() == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ Mutex::Autolock _l(t->mLock);
+ t->broadcast_l();
+ }
+ if (!mEffect->isOffloadable()) {
+ if (thread->type() == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ t->invalidateTracks(AUDIO_STREAM_MUSIC);
+ }
+ if (mEffect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
+ thread->mAudioFlinger->onNonOffloadableGlobalEffectEnable();
+ }
+ }
+ }
}
return status;
}
@@ -960,6 +1016,11 @@ status_t AudioFlinger::EffectHandle::disable()
sp<ThreadBase> thread = mEffect->thread().promote();
if (thread != 0) {
thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+ if (thread->type() == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ Mutex::Autolock _l(t->mLock);
+ t->broadcast_l();
+ }
}
return status;
@@ -1217,9 +1278,7 @@ void AudioFlinger::EffectChain::clearInputBuffer()
// Must be called with EffectChain::mLock locked
void AudioFlinger::EffectChain::clearInputBuffer_l(sp<ThreadBase> thread)
{
- size_t numSamples = thread->frameCount() * thread->channelCount();
- memset(mInBuffer, 0, numSamples * sizeof(int16_t));
-
+ memset(mInBuffer, 0, thread->frameCount() * thread->frameSize());
}
// Must be called with EffectChain::mLock locked
@@ -1232,9 +1291,10 @@ void AudioFlinger::EffectChain::process_l()
}
bool isGlobalSession = (mSessionId == AUDIO_SESSION_OUTPUT_MIX) ||
(mSessionId == AUDIO_SESSION_OUTPUT_STAGE);
- // always process effects unless no more tracks are on the session and the effect tail
- // has been rendered
- bool doProcess = true;
+ // never process effects when:
+ // - on an OFFLOAD thread
+ // - no more tracks are on the session and the effect tail has been rendered
+ bool doProcess = (thread->type() != ThreadBase::OFFLOAD);
if (!isGlobalSession) {
bool tracksOnSession = (trackCnt() != 0);
@@ -1720,4 +1780,16 @@ void AudioFlinger::EffectChain::checkSuspendOnEffectEnabled(const sp<EffectModul
}
}
+bool AudioFlinger::EffectChain::isNonOffloadableEnabled()
+{
+ Mutex::Autolock _l(mLock);
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ if (mEffects[i]->isEnabled() && !mEffects[i]->isOffloadable()) {
+ return true;
+ }
+ }
+ return false;
+}
+
}; // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 91303ee..b717857 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -25,6 +25,10 @@
// state changes or resource modifications. Always respect the following order
// if multiple mutexes must be acquired to avoid cross deadlock:
// AudioFlinger -> ThreadBase -> EffectChain -> EffectModule
+// In addition, methods that lock the AudioPolicyService mutex (getOutputForEffect(),
+// startOutput()...) should never be called with AudioFlinger or Threadbase mutex locked
+// to avoid cross deadlock with other clients calling AudioPolicyService methods that in turn
+// call AudioFlinger thus locking the same mutexes in the reverse order.
// The EffectModule class is a wrapper object controlling the effect engine implementation
// in the effect library. It prevents concurrent calls to process() and command() functions
@@ -111,6 +115,10 @@ public:
bool purgeHandles();
void lock() { mLock.lock(); }
void unlock() { mLock.unlock(); }
+ bool isOffloadable() const
+ { return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; }
+ status_t setOffloaded(bool offloaded, audio_io_handle_t io);
+ bool isOffloaded() const;
void dump(int fd, const Vector<String16>& args);
@@ -126,6 +134,7 @@ protected:
status_t start_l();
status_t stop_l();
+ status_t remove_effect_from_hal_l();
mutable Mutex mLock; // mutex for process, commands and handles list protection
wp<ThreadBase> mThread; // parent thread
@@ -143,6 +152,7 @@ mutable Mutex mLock; // mutex for process, commands and handl
// sending disable command.
uint32_t mDisableWaitCnt; // current process() calls count during disable period.
bool mSuspended; // effect is suspended: temporarily disabled by framework
+ bool mOffloaded; // effect is currently offloaded to the audio DSP
};
// The EffectHandle class implements the IEffect interface. It provides resources
@@ -302,6 +312,10 @@ public:
void clearInputBuffer();
+ // At least one non offloadable effect in the chain is enabled
+ bool isNonOffloadableEnabled();
+
+
void dump(int fd, const Vector<String16>& args);
protected:
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 21df1d7..f27ea17 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -25,6 +25,7 @@
#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include "Configuration.h"
#include <sys/atomics.h>
#include <time.h>
#include <utils/Log.h>
@@ -44,6 +45,8 @@
#define MIN_WARMUP_CYCLES 2 // minimum number of loop cycles to wait for warmup
#define MAX_WARMUP_CYCLES 10 // maximum number of loop cycles to wait for warmup
+#define FCC_2 2 // fixed channel count assumption
+
namespace android {
// Fast mixer thread
@@ -82,7 +85,7 @@ bool FastMixer::threadLoop()
struct timespec oldLoad = {0, 0}; // previous value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
bool oldLoadValid = false; // whether oldLoad is valid
uint32_t bounds = 0;
- bool full = false; // whether we have collected at least kSamplingN samples
+ bool full = false; // whether we have collected at least mSamplingN samples
#ifdef CPU_FREQUENCY_STATISTICS
ThreadCpuUsage tcu; // for reading the current CPU clock frequency in kHz
#endif
@@ -93,6 +96,12 @@ bool FastMixer::threadLoop()
uint32_t warmupCycles = 0; // counter of number of loop cycles required to warmup
NBAIO_Sink* teeSink = NULL; // if non-NULL, then duplicate write() to this non-blocking sink
NBLog::Writer dummyLogWriter, *logWriter = &dummyLogWriter;
+ uint32_t totalNativeFramesWritten = 0; // copied to dumpState->mFramesWritten
+
+ // next 2 fields are valid only when timestampStatus == NO_ERROR
+ AudioTimestamp timestamp;
+ uint32_t nativeFramesWrittenButNotPresented = 0; // the = 0 is to silence the compiler
+ status_t timestampStatus = INVALID_OPERATION;
for (;;) {
@@ -142,7 +151,9 @@ bool FastMixer::threadLoop()
preIdle = *current;
current = &preIdle;
oldTsValid = false;
+#ifdef FAST_MIXER_STATISTICS
oldLoadValid = false;
+#endif
ignoreNextOverrun = true;
}
previous = current;
@@ -182,9 +193,12 @@ bool FastMixer::threadLoop()
warmupCycles = 0;
sleepNs = -1;
coldGen = current->mColdGen;
+#ifdef FAST_MIXER_STATISTICS
bounds = 0;
full = false;
+#endif
oldTsValid = !clock_gettime(CLOCK_MONOTONIC, &oldTs);
+ timestampStatus = INVALID_OPERATION;
} else {
sleepNs = FAST_HOT_IDLE_NS;
}
@@ -220,7 +234,7 @@ bool FastMixer::threadLoop()
} else {
format = outputSink->format();
sampleRate = Format_sampleRate(format);
- ALOG_ASSERT(Format_channelCount(format) == 2);
+ ALOG_ASSERT(Format_channelCount(format) == FCC_2);
}
dumpState->mSampleRate = sampleRate;
}
@@ -236,7 +250,7 @@ bool FastMixer::threadLoop()
// implementation; it would be better to have normal mixer allocate for us
// to avoid blocking here and to prevent possible priority inversion
mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks);
- mixBuffer = new short[frameCount * 2];
+ mixBuffer = new short[frameCount * FCC_2];
periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00
underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75
overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50
@@ -375,6 +389,31 @@ bool FastMixer::threadLoop()
i = __builtin_ctz(currentTrackMask);
currentTrackMask &= ~(1 << i);
const FastTrack* fastTrack = &current->mFastTracks[i];
+
+ // Refresh the per-track timestamp
+ if (timestampStatus == NO_ERROR) {
+ uint32_t trackFramesWrittenButNotPresented;
+ uint32_t trackSampleRate = fastTrack->mSampleRate;
+ // There is currently no sample rate conversion for fast tracks currently
+ if (trackSampleRate != 0 && trackSampleRate != sampleRate) {
+ trackFramesWrittenButNotPresented =
+ ((int64_t) nativeFramesWrittenButNotPresented * trackSampleRate) /
+ sampleRate;
+ } else {
+ trackFramesWrittenButNotPresented = nativeFramesWrittenButNotPresented;
+ }
+ uint32_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased();
+ // Can't provide an AudioTimestamp before first frame presented,
+ // or during the brief 32-bit wraparound window
+ if (trackFramesWritten >= trackFramesWrittenButNotPresented) {
+ AudioTimestamp perTrackTimestamp;
+ perTrackTimestamp.mPosition =
+ trackFramesWritten - trackFramesWrittenButNotPresented;
+ perTrackTimestamp.mTime = timestamp.mTime;
+ fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp);
+ }
+ }
+
int name = fastTrackNames[i];
ALOG_ASSERT(name >= 0);
if (fastTrack->mVolumeProvider != NULL) {
@@ -433,7 +472,7 @@ bool FastMixer::threadLoop()
//bool didFullWrite = false; // dumpsys could display a count of partial writes
if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mixBuffer != NULL)) {
if (mixBufferState == UNDEFINED) {
- memset(mixBuffer, 0, frameCount * 2 * sizeof(short));
+ memset(mixBuffer, 0, frameCount * FCC_2 * sizeof(short));
mixBufferState = ZEROED;
}
if (teeSink != NULL) {
@@ -448,7 +487,8 @@ bool FastMixer::threadLoop()
dumpState->mWriteSequence++;
if (framesWritten >= 0) {
ALOG_ASSERT((size_t) framesWritten <= frameCount);
- dumpState->mFramesWritten += framesWritten;
+ totalNativeFramesWritten += framesWritten;
+ dumpState->mFramesWritten = totalNativeFramesWritten;
//if ((size_t) framesWritten == frameCount) {
// didFullWrite = true;
//}
@@ -457,6 +497,18 @@ bool FastMixer::threadLoop()
}
attemptedWrite = true;
// FIXME count # of writes blocked excessively, CPU usage, etc. for dump
+
+ timestampStatus = outputSink->getTimestamp(timestamp);
+ if (timestampStatus == NO_ERROR) {
+ uint32_t totalNativeFramesPresented = timestamp.mPosition;
+ if (totalNativeFramesPresented <= totalNativeFramesWritten) {
+ nativeFramesWrittenButNotPresented =
+ totalNativeFramesWritten - totalNativeFramesPresented;
+ } else {
+ // HAL reported that more frames were presented than were written
+ timestampStatus = INVALID_OPERATION;
+ }
+ }
}
// To be exactly periodic, compute the next sleep time based on current time.
@@ -498,91 +550,91 @@ bool FastMixer::threadLoop()
}
}
sleepNs = -1;
- if (isWarm) {
- if (sec > 0 || nsec > underrunNs) {
- ATRACE_NAME("underrun");
- // FIXME only log occasionally
- ALOGV("underrun: time since last cycle %d.%03ld sec",
- (int) sec, nsec / 1000000L);
- dumpState->mUnderruns++;
- ignoreNextOverrun = true;
- } else if (nsec < overrunNs) {
- if (ignoreNextOverrun) {
- ignoreNextOverrun = false;
- } else {
+ if (isWarm) {
+ if (sec > 0 || nsec > underrunNs) {
+ ATRACE_NAME("underrun");
// FIXME only log occasionally
- ALOGV("overrun: time since last cycle %d.%03ld sec",
+ ALOGV("underrun: time since last cycle %d.%03ld sec",
(int) sec, nsec / 1000000L);
- dumpState->mOverruns++;
+ dumpState->mUnderruns++;
+ ignoreNextOverrun = true;
+ } else if (nsec < overrunNs) {
+ if (ignoreNextOverrun) {
+ ignoreNextOverrun = false;
+ } else {
+ // FIXME only log occasionally
+ ALOGV("overrun: time since last cycle %d.%03ld sec",
+ (int) sec, nsec / 1000000L);
+ dumpState->mOverruns++;
+ }
+ // This forces a minimum cycle time. It:
+ // - compensates for an audio HAL with jitter due to sample rate conversion
+ // - works with a variable buffer depth audio HAL that never pulls at a
+ // rate < than overrunNs per buffer.
+ // - recovers from overrun immediately after underrun
+ // It doesn't work with a non-blocking audio HAL.
+ sleepNs = forceNs - nsec;
+ } else {
+ ignoreNextOverrun = false;
}
- // This forces a minimum cycle time. It:
- // - compensates for an audio HAL with jitter due to sample rate conversion
- // - works with a variable buffer depth audio HAL that never pulls at a rate
- // < than overrunNs per buffer.
- // - recovers from overrun immediately after underrun
- // It doesn't work with a non-blocking audio HAL.
- sleepNs = forceNs - nsec;
- } else {
- ignoreNextOverrun = false;
}
- }
#ifdef FAST_MIXER_STATISTICS
- if (isWarm) {
- // advance the FIFO queue bounds
- size_t i = bounds & (FastMixerDumpState::kSamplingN - 1);
- bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF);
- if (full) {
- bounds += 0x10000;
- } else if (!(bounds & (FastMixerDumpState::kSamplingN - 1))) {
- full = true;
- }
- // compute the delta value of clock_gettime(CLOCK_MONOTONIC)
- uint32_t monotonicNs = nsec;
- if (sec > 0 && sec < 4) {
- monotonicNs += sec * 1000000000;
- }
- // compute the raw CPU load = delta value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
- uint32_t loadNs = 0;
- struct timespec newLoad;
- rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad);
- if (rc == 0) {
- if (oldLoadValid) {
- sec = newLoad.tv_sec - oldLoad.tv_sec;
- nsec = newLoad.tv_nsec - oldLoad.tv_nsec;
- if (nsec < 0) {
- --sec;
- nsec += 1000000000;
- }
- loadNs = nsec;
- if (sec > 0 && sec < 4) {
- loadNs += sec * 1000000000;
+ if (isWarm) {
+ // advance the FIFO queue bounds
+ size_t i = bounds & (dumpState->mSamplingN - 1);
+ bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF);
+ if (full) {
+ bounds += 0x10000;
+ } else if (!(bounds & (dumpState->mSamplingN - 1))) {
+ full = true;
+ }
+ // compute the delta value of clock_gettime(CLOCK_MONOTONIC)
+ uint32_t monotonicNs = nsec;
+ if (sec > 0 && sec < 4) {
+ monotonicNs += sec * 1000000000;
+ }
+ // compute raw CPU load = delta value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
+ uint32_t loadNs = 0;
+ struct timespec newLoad;
+ rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad);
+ if (rc == 0) {
+ if (oldLoadValid) {
+ sec = newLoad.tv_sec - oldLoad.tv_sec;
+ nsec = newLoad.tv_nsec - oldLoad.tv_nsec;
+ if (nsec < 0) {
+ --sec;
+ nsec += 1000000000;
+ }
+ loadNs = nsec;
+ if (sec > 0 && sec < 4) {
+ loadNs += sec * 1000000000;
+ }
+ } else {
+ // first time through the loop
+ oldLoadValid = true;
}
- } else {
- // first time through the loop
- oldLoadValid = true;
+ oldLoad = newLoad;
}
- oldLoad = newLoad;
- }
#ifdef CPU_FREQUENCY_STATISTICS
- // get the absolute value of CPU clock frequency in kHz
- int cpuNum = sched_getcpu();
- uint32_t kHz = tcu.getCpukHz(cpuNum);
- kHz = (kHz << 4) | (cpuNum & 0xF);
+ // get the absolute value of CPU clock frequency in kHz
+ int cpuNum = sched_getcpu();
+ uint32_t kHz = tcu.getCpukHz(cpuNum);
+ kHz = (kHz << 4) | (cpuNum & 0xF);
#endif
- // save values in FIFO queues for dumpsys
- // these stores #1, #2, #3 are not atomic with respect to each other,
- // or with respect to store #4 below
- dumpState->mMonotonicNs[i] = monotonicNs;
- dumpState->mLoadNs[i] = loadNs;
+ // save values in FIFO queues for dumpsys
+ // these stores #1, #2, #3 are not atomic with respect to each other,
+ // or with respect to store #4 below
+ dumpState->mMonotonicNs[i] = monotonicNs;
+ dumpState->mLoadNs[i] = loadNs;
#ifdef CPU_FREQUENCY_STATISTICS
- dumpState->mCpukHz[i] = kHz;
+ dumpState->mCpukHz[i] = kHz;
#endif
- // this store #4 is not atomic with respect to stores #1, #2, #3 above, but
- // the newest open and oldest closed halves are atomic with respect to each other
- dumpState->mBounds = bounds;
- ATRACE_INT("cycle_ms", monotonicNs / 1000000);
- ATRACE_INT("load_us", loadNs / 1000);
- }
+ // this store #4 is not atomic with respect to stores #1, #2, #3 above, but
+ // the newest open & oldest closed halves are atomic with respect to each other
+ dumpState->mBounds = bounds;
+ ATRACE_INT("cycle_ms", monotonicNs / 1000000);
+ ATRACE_INT("load_us", loadNs / 1000);
+ }
#endif
} else {
// first time through the loop
@@ -603,25 +655,43 @@ bool FastMixer::threadLoop()
// never return 'true'; Thread::_threadLoop() locks mutex which can result in priority inversion
}
-FastMixerDumpState::FastMixerDumpState() :
+FastMixerDumpState::FastMixerDumpState(
+#ifdef FAST_MIXER_STATISTICS
+ uint32_t samplingN
+#endif
+ ) :
mCommand(FastMixerState::INITIAL), mWriteSequence(0), mFramesWritten(0),
mNumTracks(0), mWriteErrors(0), mUnderruns(0), mOverruns(0),
mSampleRate(0), mFrameCount(0), /* mMeasuredWarmupTs({0, 0}), */ mWarmupCycles(0),
mTrackMask(0)
#ifdef FAST_MIXER_STATISTICS
- , mBounds(0)
+ , mSamplingN(0), mBounds(0)
#endif
{
mMeasuredWarmupTs.tv_sec = 0;
mMeasuredWarmupTs.tv_nsec = 0;
+#ifdef FAST_MIXER_STATISTICS
+ increaseSamplingN(samplingN);
+#endif
+}
+
+#ifdef FAST_MIXER_STATISTICS
+void FastMixerDumpState::increaseSamplingN(uint32_t samplingN)
+{
+ if (samplingN <= mSamplingN || samplingN > kSamplingN || roundup(samplingN) != samplingN) {
+ return;
+ }
+ uint32_t additional = samplingN - mSamplingN;
// sample arrays aren't accessed atomically with respect to the bounds,
// so clearing reduces chance for dumpsys to read random uninitialized samples
- memset(&mMonotonicNs, 0, sizeof(mMonotonicNs));
- memset(&mLoadNs, 0, sizeof(mLoadNs));
+ memset(&mMonotonicNs[mSamplingN], 0, sizeof(mMonotonicNs[0]) * additional);
+ memset(&mLoadNs[mSamplingN], 0, sizeof(mLoadNs[0]) * additional);
#ifdef CPU_FREQUENCY_STATISTICS
- memset(&mCpukHz, 0, sizeof(mCpukHz));
+ memset(&mCpukHz[mSamplingN], 0, sizeof(mCpukHz[0]) * additional);
#endif
+ mSamplingN = samplingN;
}
+#endif
FastMixerDumpState::~FastMixerDumpState()
{
@@ -641,7 +711,7 @@ static int compare_uint32_t(const void *pa, const void *pb)
}
}
-void FastMixerDumpState::dump(int fd)
+void FastMixerDumpState::dump(int fd) const
{
if (mCommand == FastMixerState::INITIAL) {
fdprintf(fd, "FastMixer not initialized\n");
@@ -692,9 +762,9 @@ void FastMixerDumpState::dump(int fd)
uint32_t newestOpen = bounds & 0xFFFF;
uint32_t oldestClosed = bounds >> 16;
uint32_t n = (newestOpen - oldestClosed) & 0xFFFF;
- if (n > kSamplingN) {
+ if (n > mSamplingN) {
ALOGE("too many samples %u", n);
- n = kSamplingN;
+ n = mSamplingN;
}
// statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency,
// and adjusted CPU load in MHz normalized for CPU clock frequency
@@ -710,7 +780,7 @@ void FastMixerDumpState::dump(int fd)
uint32_t *tail = n >= kTailDenominator ? new uint32_t[n] : NULL;
// loop over all the samples
for (uint32_t j = 0; j < n; ++j) {
- size_t i = oldestClosed++ & (kSamplingN - 1);
+ size_t i = oldestClosed++ & (mSamplingN - 1);
uint32_t wallNs = mMonotonicNs[i];
if (tail != NULL) {
tail[j] = wallNs;
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index 2ab1d04..6158925 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -85,10 +85,14 @@ struct FastTrackDump {
// Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks).
// It has a different lifetime than the FastMixer, and so it can't be a member of FastMixer.
struct FastMixerDumpState {
- FastMixerDumpState();
+ FastMixerDumpState(
+#ifdef FAST_MIXER_STATISTICS
+ uint32_t samplingN = kSamplingNforLowRamDevice
+#endif
+ );
/*virtual*/ ~FastMixerDumpState();
- void dump(int fd); // should only be called on a stable copy, not the original
+ void dump(int fd) const; // should only be called on a stable copy, not the original
FastMixerState::Command mCommand; // current command
uint32_t mWriteSequence; // incremented before and after each write()
@@ -106,8 +110,15 @@ struct FastMixerDumpState {
#ifdef FAST_MIXER_STATISTICS
// Recently collected samples of per-cycle monotonic time, thread CPU time, and CPU frequency.
- // kSamplingN is the size of the sampling frame, and must be a power of 2 <= 0x8000.
+ // kSamplingN is max size of sampling frame (statistics), and must be a power of 2 <= 0x8000.
+ // The sample arrays are virtually allocated based on this compile-time constant,
+ // but are only initialized and used based on the runtime parameter mSamplingN.
static const uint32_t kSamplingN = 0x8000;
+ // Compile-time constant for a "low RAM device", must be a power of 2 <= kSamplingN.
+ // This value was chosen such that each array uses 1 small page (4 Kbytes).
+ static const uint32_t kSamplingNforLowRamDevice = 0x400;
+ // Corresponding runtime maximum size of sample arrays, must be a power of 2 <= kSamplingN.
+ uint32_t mSamplingN;
// The bounds define the interval of valid samples, and are represented as follows:
// newest open (excluded) endpoint = lower 16 bits of bounds, modulo N
// oldest closed (included) endpoint = upper 16 bits of bounds, modulo N
@@ -119,6 +130,8 @@ struct FastMixerDumpState {
#ifdef CPU_FREQUENCY_STATISTICS
uint32_t mCpukHz[kSamplingN]; // absolute CPU clock frequency in kHz, bits 0-3 are CPU#
#endif
+ // Increase sampling window after construction, must be a power of 2 <= kSamplingN
+ void increaseSamplingN(uint32_t samplingN);
#endif
};
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
index c45c81b..737de97 100644
--- a/services/audioflinger/FastMixerState.cpp
+++ b/services/audioflinger/FastMixerState.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "Configuration.h"
#include "FastMixerState.h"
namespace android {
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index a749d7a..a2e2511 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -46,15 +46,21 @@ public:
void destroy();
int name() const { return mName; }
+ virtual uint32_t sampleRate() const;
+
audio_stream_type_t streamType() const {
return mStreamType;
}
+ bool isOffloaded() const { return (mFlags & IAudioFlinger::TRACK_OFFLOAD) != 0; }
+ status_t setParameters(const String8& keyValuePairs);
status_t attachAuxEffect(int EffectId);
void setAuxBuffer(int EffectId, int32_t *buffer);
int32_t *auxBuffer() const { return mAuxBuffer; }
void setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; }
int16_t *mainBuffer() const { return mMainBuffer; }
int auxEffectId() const { return mAuxEffectId; }
+ virtual status_t getTimestamp(AudioTimestamp& timestamp);
+ void signal();
// implement FastMixerState::VolumeProvider interface
virtual uint32_t getVolumeLR();
@@ -66,6 +72,7 @@ protected:
friend class PlaybackThread;
friend class MixerThread;
friend class DirectOutputThread;
+ friend class OffloadThread;
Track(const Track&);
Track& operator = (const Track&);
@@ -75,7 +82,9 @@ protected:
int64_t pts = kInvalidPTS);
// releaseBuffer() not overridden
+ // ExtendedAudioBufferProvider interface
virtual size_t framesReady() const;
+ virtual size_t framesReleased() const;
bool isPausing() const { return mState == PAUSING; }
bool isPaused() const { return mState == PAUSED; }
@@ -101,6 +110,7 @@ public:
bool isInvalid() const { return mIsInvalid; }
virtual bool isTimedTrack() const { return false; }
bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
+ int fastIndex() const { return mFastIndex; }
protected:
@@ -108,7 +118,10 @@ protected:
enum {FS_INVALID, FS_FILLING, FS_FILLED, FS_ACTIVE};
mutable uint8_t mFillingUpStatus;
int8_t mRetryCount;
- const sp<IMemory> mSharedBuffer;
+
+ // see comment at AudioFlinger::PlaybackThread::Track::~Track for why this can't be const
+ sp<IMemory> mSharedBuffer;
+
bool mResetDone;
const audio_stream_type_t mStreamType;
int mName; // track name on the normal mixer,
@@ -134,11 +147,12 @@ private:
// but the slot is only used if track is active
FastTrackUnderruns mObservedUnderruns; // Most recently observed value of
// mFastMixerDumpState.mTracks[mFastIndex].mUnderruns
- uint32_t mUnderrunCount; // Counter of total number of underruns, never reset
volatile float mCachedVolume; // combined master volume and stream type volume;
// 'volatile' means accessed without lock or
// barrier, but is read/written atomically
bool mIsInvalid; // non-resettable latch, set by invalidate()
+ AudioTrackServerProxy* mAudioTrackServerProxy;
+ bool mResumeToStopping; // track was paused in stopping state.
}; // end of Track
class TimedTrack : public Track {
@@ -255,10 +269,6 @@ public:
private:
- enum {
- NO_MORE_BUFFERS = 0x80000001, // same in AudioTrack.h, ok to be different value
- };
-
status_t obtainBuffer(AudioBufferProvider::Buffer* buffer,
uint32_t waitTimeMs);
void clearBufferQueue();
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 6c0d1d3..cd8f70c 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -36,6 +36,7 @@ public:
void destroy();
+ void invalidate();
// clear the buffer overflow flag
void clearOverflow() { mOverflow = false; }
// set the buffer overflow flag and return previous value
@@ -57,4 +58,5 @@ private:
// releaseBuffer() not overridden
bool mOverflow; // overflow on most recent attempt to fill client buffer
+ AudioRecordServerProxy* mAudioRecordServerProxy;
};
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index d15bd04..152455d 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -34,6 +34,22 @@ bool recordingAllowed() {
return ok;
}
+bool captureAudioOutputAllowed() {
+ if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
+ static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT");
+ // don't use PermissionCache; this is not a system permission
+ bool ok = checkCallingPermission(sCaptureAudioOutput);
+ if (!ok) ALOGE("Request requires android.permission.CAPTURE_AUDIO_OUTPUT");
+ return ok;
+}
+
+bool captureHotwordAllowed() {
+ static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD");
+ bool ok = checkCallingPermission(sCaptureHotwordAllowed);
+ if (!ok) ALOGE("android.permission.CAPTURE_AUDIO_HOTWORD");
+ return ok;
+}
+
bool settingsAllowed() {
if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS");
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
index 80cecba..531bc56 100644
--- a/services/audioflinger/ServiceUtilities.h
+++ b/services/audioflinger/ServiceUtilities.h
@@ -21,6 +21,8 @@ namespace android {
extern pid_t getpid_cached;
bool recordingAllowed();
+bool captureAudioOutputAllowed();
+bool captureHotwordAllowed();
bool settingsAllowed();
bool dumpAllowed();
diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp
index 3e891a5..c2d3bbd 100644
--- a/services/audioflinger/StateQueue.cpp
+++ b/services/audioflinger/StateQueue.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "StateQueue"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <time.h>
#include <cutils/atomic.h>
#include <utils/Log.h>
diff --git a/services/audioflinger/StateQueue.h b/services/audioflinger/StateQueue.h
index e33b3c6..9cde642 100644
--- a/services/audioflinger/StateQueue.h
+++ b/services/audioflinger/StateQueue.h
@@ -31,8 +31,14 @@
// and this may result in an audible artifact
// needs read-only access to a recent stable state,
// but not necessarily the most current one
+// only allocate and free memory when configuration changes
+// avoid conventional logging, as this is a form of I/O and could block
+// defer computation to other threads when feasible; for example
+// cycle times are collected by fast mixer thread but the floating-point
+// statistical calculations on these cycle times are computed by normal mixer
+// these requirements also apply to callouts such as AudioBufferProvider and VolumeProvider
// Normal mixer thread:
-// periodic with typical period ~40 ms
+// periodic with typical period ~20 ms
// SCHED_OTHER scheduling policy and nice priority == urgent audio
// ok to block, but prefer to avoid as much as possible
// needs read/write access to state
diff --git a/services/audioflinger/StateQueueInstantiations.cpp b/services/audioflinger/StateQueueInstantiations.cpp
index 077582f..0d5cd0c 100644
--- a/services/audioflinger/StateQueueInstantiations.cpp
+++ b/services/audioflinger/StateQueueInstantiations.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "Configuration.h"
#include "FastMixerState.h"
#include "StateQueue.h"
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 0b88c0e..47dcca6 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -20,11 +20,12 @@
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include "Configuration.h"
#include <math.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <cutils/properties.h>
-#include <cutils/compiler.h>
+#include <media/AudioParameter.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -53,14 +54,11 @@
#include "ServiceUtilities.h"
#include "SchedulingPolicyService.h"
-#undef ADD_BATTERY_DATA
-
#ifdef ADD_BATTERY_DATA
#include <media/IMediaPlayerService.h>
#include <media/IMediaDeathNotifier.h>
#endif
-// #define DEBUG_CPU_USAGE 10 // log statistics every n wall clock seconds
#ifdef DEBUG_CPU_USAGE
#include <cpustats/CentralTendencyStatistics.h>
#include <cpustats/ThreadCpuUsage.h>
@@ -111,6 +109,9 @@ static const uint32_t kMinNormalMixBufferSizeMs = 20;
// maximum normal mix buffer size
static const uint32_t kMaxNormalMixBufferSizeMs = 24;
+// Offloaded output thread standby delay: allows track transition without going to standby
+static const nsecs_t kOffloadStandbyDelayNs = seconds(1);
+
// Whether to use fast mixer
static const enum {
FastMixer_Never, // never initialize or use: for debugging only
@@ -139,7 +140,7 @@ static const int kPriorityFastMixer = 3;
// FIXME It would be better for client to tell AudioFlinger whether it wants double-buffering or
// N-buffering, so AudioFlinger could allocate the right amount of memory.
// See the client's minBufCount and mNotificationFramesAct calculations for details.
-static const int kFastTrackMultiplier = 2;
+static const int kFastTrackMultiplier = 1;
// ----------------------------------------------------------------------------
@@ -267,10 +268,9 @@ AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio
audio_devices_t outDevice, audio_devices_t inDevice, type_t type)
: Thread(false /*canCallJava*/),
mType(type),
- mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mNormalFrameCount(0),
- // mChannelMask
- mChannelCount(0),
- mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID),
+ mAudioFlinger(audioFlinger),
+ // mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, and mFormat are
+ // set by PlaybackThread::readOutputParameters() or RecordThread::readInputParameters()
mParamStatus(NO_ERROR),
mStandby(false), mOutDevice(outDevice), mInDevice(inDevice),
mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id),
@@ -281,6 +281,12 @@ AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio
AudioFlinger::ThreadBase::~ThreadBase()
{
+ // mConfigEvents should be empty, but just in case it isn't, free the memory it owns
+ for (size_t i = 0; i < mConfigEvents.size(); i++) {
+ delete mConfigEvents[i];
+ }
+ mConfigEvents.clear();
+
mParamCond.broadcast();
// do not lock the mutex in destructor
releaseWakeLock_l();
@@ -420,9 +426,7 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args)
result.append(buffer);
snprintf(buffer, SIZE, "HAL frame count: %d\n", mFrameCount);
result.append(buffer);
- snprintf(buffer, SIZE, "Normal frame count: %d\n", mNormalFrameCount);
- result.append(buffer);
- snprintf(buffer, SIZE, "Channel Count: %d\n", mChannelCount);
+ snprintf(buffer, SIZE, "Channel Count: %u\n", mChannelCount);
result.append(buffer);
snprintf(buffer, SIZE, "Channel Mask: 0x%08x\n", mChannelMask);
result.append(buffer);
@@ -472,13 +476,32 @@ void AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String16>&
}
}
-void AudioFlinger::ThreadBase::acquireWakeLock()
+void AudioFlinger::ThreadBase::acquireWakeLock(int uid)
{
Mutex::Autolock _l(mLock);
- acquireWakeLock_l();
+ acquireWakeLock_l(uid);
}
-void AudioFlinger::ThreadBase::acquireWakeLock_l()
+String16 AudioFlinger::ThreadBase::getWakeLockTag()
+{
+ switch (mType) {
+ case MIXER:
+ return String16("AudioMix");
+ case DIRECT:
+ return String16("AudioDirectOut");
+ case DUPLICATING:
+ return String16("AudioDup");
+ case RECORD:
+ return String16("AudioIn");
+ case OFFLOAD:
+ return String16("AudioOffload");
+ default:
+ ALOG_ASSERT(false);
+ return String16("AudioUnknown");
+ }
+}
+
+void AudioFlinger::ThreadBase::acquireWakeLock_l(int uid)
{
if (mPowerManager == 0) {
// use checkService() to avoid blocking if power service is not up yet
@@ -493,9 +516,19 @@ void AudioFlinger::ThreadBase::acquireWakeLock_l()
}
if (mPowerManager != 0) {
sp<IBinder> binder = new BBinder();
- status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
- binder,
- String16(mName));
+ status_t status;
+ if (uid >= 0) {
+ status = mPowerManager->acquireWakeLockWithUid(POWERMANAGER_PARTIAL_WAKE_LOCK,
+ binder,
+ getWakeLockTag(),
+ String16("media"),
+ uid);
+ } else {
+ status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
+ binder,
+ getWakeLockTag(),
+ String16("media"));
+ }
if (status == NO_ERROR) {
mWakeLockToken = binder;
}
@@ -697,14 +730,22 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
goto Exit;
}
- // Do not allow effects with session ID 0 on direct output or duplicating threads
- // TODO: add rule for hw accelerated effects on direct outputs with non PCM format
- if (sessionId == AUDIO_SESSION_OUTPUT_MIX && mType != MIXER) {
- ALOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
- desc->name, sessionId);
- lStatus = BAD_VALUE;
- goto Exit;
+ // Allow global effects only on offloaded and mixer threads
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
+ switch (mType) {
+ case MIXER:
+ case OFFLOAD:
+ break;
+ case DIRECT:
+ case DUPLICATING:
+ case RECORD:
+ default:
+ ALOGW("createEffect_l() Cannot add global effect %s on thread %s", desc->name, mName);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
}
+
// Only Pre processor effects are allowed on input threads and only on input threads
if ((mType == RECORD) != ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC)) {
ALOGW("createEffect_l() effect %s (flags %08x) created on wrong thread type %d",
@@ -747,6 +788,8 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
if (lStatus != NO_ERROR) {
goto Exit;
}
+ effect->setOffloaded(mType == OFFLOAD, mId);
+
lStatus = chain->addEffect_l(effect);
if (lStatus != NO_ERROR) {
goto Exit;
@@ -808,6 +851,10 @@ status_t AudioFlinger::ThreadBase::addEffect_l(const sp<EffectModule>& effect)
sp<EffectChain> chain = getEffectChain_l(sessionId);
bool chainCreated = false;
+ ALOGD_IF((mType == OFFLOAD) && !effect->isOffloadable(),
+ "addEffect_l() on offloaded thread %p: effect %s does not support offload flags %x",
+ this, effect->desc().name, effect->desc().flags);
+
if (chain == 0) {
// create a new chain for this session
ALOGV("addEffect_l() new effect chain for session %d", sessionId);
@@ -824,6 +871,8 @@ status_t AudioFlinger::ThreadBase::addEffect_l(const sp<EffectModule>& effect)
return BAD_VALUE;
}
+ effect->setOffloaded(mType == OFFLOAD, mId);
+
status_t status = chain->addEffect_l(effect);
if (status != NO_ERROR) {
if (chainCreated) {
@@ -926,16 +975,25 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
audio_devices_t device,
type_t type)
: ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type),
- mMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
+ mNormalFrameCount(0), mMixBuffer(NULL),
+ mAllocMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
// mStreamTypes[] initialized in constructor body
mOutput(output),
mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
mMixerStatus(MIXER_IDLE),
mMixerStatusIgnoringFastTracks(MIXER_IDLE),
standbyDelay(AudioFlinger::mStandbyTimeInNsecs),
+ mBytesRemaining(0),
+ mCurrentWriteLength(0),
+ mUseAsyncWrite(false),
+ mWriteAckSequence(0),
+ mDrainSequence(0),
+ mSignalPending(false),
mScreenState(AudioFlinger::mScreenState),
// index 0 is reserved for normal mixer's submix
- mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1)
+ mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1),
+ // mLatchD, mLatchQ,
+ mLatchDValid(false), mLatchQValid(false)
{
snprintf(mName, kNameLength, "AudioOut_%X", id);
mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
@@ -975,7 +1033,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
AudioFlinger::PlaybackThread::~PlaybackThread()
{
mAudioFlinger->unregisterWriter(mNBLogWriter);
- delete [] mMixBuffer;
+ delete [] mAllocMixBuffer;
}
void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
@@ -1043,6 +1101,8 @@ void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>&
snprintf(buffer, SIZE, "\nOutput thread %p internals\n", this);
result.append(buffer);
+ snprintf(buffer, SIZE, "Normal frame count: %d\n", mNormalFrameCount);
+ result.append(buffer);
snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n",
ns2ms(systemTime() - mLastWriteTime));
result.append(buffer);
@@ -1181,7 +1241,22 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
goto Exit;
}
}
+ } else if (mType == OFFLOAD) {
+ if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) {
+ ALOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelMask 0x%08x \""
+ "for output %p with format %d",
+ sampleRate, format, channelMask, mOutput, mFormat);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
} else {
+ if ((format & AUDIO_FORMAT_MAIN_MASK) != AUDIO_FORMAT_PCM) {
+ ALOGE("createTrack_l() Bad parameter: format %d \""
+ "for output %p with format %d",
+ format, mOutput, mFormat);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
// Resampler implementation limits input sampling rate to 2 x output sampling rate.
if (sampleRate > mSampleRate*2) {
ALOGE("Sample rate out of range: %u mSampleRate %u", sampleRate, mSampleRate);
@@ -1227,6 +1302,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
lStatus = NO_MEMORY;
goto Exit;
}
+
mTracks.add(track);
sp<EffectChain> chain = getEffectChain_l(sessionId);
@@ -1301,12 +1377,14 @@ void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, f
{
Mutex::Autolock _l(mLock);
mStreamTypes[stream].volume = value;
+ broadcast_l();
}
void AudioFlinger::PlaybackThread::setStreamMute(audio_stream_type_t stream, bool muted)
{
Mutex::Autolock _l(mLock);
mStreamTypes[stream].mute = muted;
+ broadcast_l();
}
float AudioFlinger::PlaybackThread::streamVolume(audio_stream_type_t stream) const
@@ -1326,7 +1404,31 @@ 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.
- track->mFillingUpStatus = Track::FS_FILLING;
+ if (!track->isOutputTrack()) {
+ TrackBase::track_state state = track->mState;
+ mLock.unlock();
+ status = AudioSystem::startOutput(mId, track->streamType(), track->sessionId());
+ mLock.lock();
+ // abort track was stopped/paused while we released the lock
+ if (state != track->mState) {
+ if (status == NO_ERROR) {
+ mLock.unlock();
+ AudioSystem::stopOutput(mId, track->streamType(), track->sessionId());
+ mLock.lock();
+ }
+ return INVALID_OPERATION;
+ }
+ // abort if start is rejected by audio policy manager
+ if (status != NO_ERROR) {
+ return PERMISSION_DENIED;
+ }
+#ifdef ADD_BATTERY_DATA
+ // to track the speaker usage
+ addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStart);
+#endif
+ }
+
+ track->mFillingUpStatus = track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
track->mResetDone = false;
track->mPresentationCompleteFrames = 0;
mActiveTracks.add(track);
@@ -1340,20 +1442,25 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
status = NO_ERROR;
}
- ALOGV("mWaitWorkCV.broadcast");
- mWaitWorkCV.broadcast();
+ ALOGV("signal playback thread");
+ broadcast_l();
return status;
}
-// destroyTrack_l() must be called with ThreadBase::mLock held
-void AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track)
+bool AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track)
{
- track->mState = TrackBase::TERMINATED;
+ track->terminate();
// active tracks are removed by threadLoop()
- if (mActiveTracks.indexOf(track) < 0) {
+ bool trackActive = (mActiveTracks.indexOf(track) >= 0);
+ track->mState = TrackBase::STOPPED;
+ if (!trackActive) {
removeTrack_l(track);
+ } else if (track->isFastTrack() || track->isOffloaded()) {
+ track->mState = TrackBase::STOPPING_1;
}
+
+ return trackActive;
}
void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track)
@@ -1377,18 +1484,25 @@ void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track)
}
}
-String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
+void AudioFlinger::PlaybackThread::broadcast_l()
{
- String8 out_s8 = String8("");
- char *s;
+ // Thread could be blocked waiting for async
+ // so signal it to handle state changes immediately
+ // If threadLoop is currently unlocked a signal of mWaitWorkCV will
+ // be lost so we also flag to prevent it blocking on mWaitWorkCV
+ mSignalPending = true;
+ mWaitWorkCV.broadcast();
+}
+String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
+{
Mutex::Autolock _l(mLock);
if (initCheck() != NO_ERROR) {
- return out_s8;
+ return String8();
}
- s = mOutput->stream->common.get_parameters(&mOutput->stream->common, keys.string());
- out_s8 = String8(s);
+ char *s = mOutput->stream->common.get_parameters(&mOutput->stream->common, keys.string());
+ const String8 out_s8(s);
free(s);
return out_s8;
}
@@ -1404,7 +1518,7 @@ void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
switch (event) {
case AudioSystem::OUTPUT_OPENED:
case AudioSystem::OUTPUT_CONFIG_CHANGED:
- desc.channels = mChannelMask;
+ desc.channelMask = mChannelMask;
desc.samplingRate = mSampleRate;
desc.format = mFormat;
desc.frameCount = mNormalFrameCount; // FIXME see
@@ -1422,12 +1536,80 @@ void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
mAudioFlinger->audioConfigChanged_l(event, mId, param2);
}
+void AudioFlinger::PlaybackThread::writeCallback()
+{
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->resetWriteBlocked();
+}
+
+void AudioFlinger::PlaybackThread::drainCallback()
+{
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->resetDraining();
+}
+
+void AudioFlinger::PlaybackThread::resetWriteBlocked(uint32_t sequence)
+{
+ Mutex::Autolock _l(mLock);
+ // reject out of sequence requests
+ if ((mWriteAckSequence & 1) && (sequence == mWriteAckSequence)) {
+ mWriteAckSequence &= ~1;
+ mWaitWorkCV.signal();
+ }
+}
+
+void AudioFlinger::PlaybackThread::resetDraining(uint32_t sequence)
+{
+ Mutex::Autolock _l(mLock);
+ // reject out of sequence requests
+ if ((mDrainSequence & 1) && (sequence == mDrainSequence)) {
+ mDrainSequence &= ~1;
+ mWaitWorkCV.signal();
+ }
+}
+
+// static
+int AudioFlinger::PlaybackThread::asyncCallback(stream_callback_event_t event,
+ void *param,
+ void *cookie)
+{
+ AudioFlinger::PlaybackThread *me = (AudioFlinger::PlaybackThread *)cookie;
+ ALOGV("asyncCallback() event %d", event);
+ switch (event) {
+ case STREAM_CBK_EVENT_WRITE_READY:
+ me->writeCallback();
+ break;
+ case STREAM_CBK_EVENT_DRAIN_READY:
+ me->drainCallback();
+ break;
+ default:
+ ALOGW("asyncCallback() unknown event %d", event);
+ break;
+ }
+ return 0;
+}
+
void AudioFlinger::PlaybackThread::readOutputParameters()
{
+ // unfortunately we have no way of recovering from errors here, hence the LOG_FATAL
mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common);
mChannelMask = mOutput->stream->common.get_channels(&mOutput->stream->common);
- mChannelCount = (uint16_t)popcount(mChannelMask);
+ if (!audio_is_output_channel(mChannelMask)) {
+ LOG_FATAL("HAL channel mask %#x not valid for output", mChannelMask);
+ }
+ if ((mType == MIXER || mType == DUPLICATING) && mChannelMask != AUDIO_CHANNEL_OUT_STEREO) {
+ LOG_FATAL("HAL channel mask %#x not supported for mixed output; "
+ "must be AUDIO_CHANNEL_OUT_STEREO", mChannelMask);
+ }
+ mChannelCount = popcount(mChannelMask);
mFormat = mOutput->stream->common.get_format(&mOutput->stream->common);
+ if (!audio_is_valid_format(mFormat)) {
+ LOG_FATAL("HAL format %d not valid for output", mFormat);
+ }
+ if ((mType == MIXER || mType == DUPLICATING) && mFormat != AUDIO_FORMAT_PCM_16_BIT) {
+ LOG_FATAL("HAL format %d not supported for mixed output; must be AUDIO_FORMAT_PCM_16_BIT",
+ mFormat);
+ }
mFrameSize = audio_stream_frame_size(&mOutput->stream->common);
mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize;
if (mFrameCount & 15) {
@@ -1435,6 +1617,15 @@ void AudioFlinger::PlaybackThread::readOutputParameters()
mFrameCount);
}
+ if ((mOutput->flags & AUDIO_OUTPUT_FLAG_NON_BLOCKING) &&
+ (mOutput->stream->set_callback != NULL)) {
+ if (mOutput->stream->set_callback(mOutput->stream,
+ AudioFlinger::PlaybackThread::asyncCallback, this) == 0) {
+ mUseAsyncWrite = true;
+ mCallbackThread = new AudioFlinger::AsyncCallbackThread(this);
+ }
+ }
+
// Calculate size of normal mix buffer relative to the HAL output buffer size
double multiplier = 1.0;
if (mType == MIXER && (kUseFastMixer == FastMixer_Static ||
@@ -1477,9 +1668,11 @@ void AudioFlinger::PlaybackThread::readOutputParameters()
ALOGI("HAL output buffer size %u frames, normal mix buffer size %u frames", mFrameCount,
mNormalFrameCount);
- delete[] mMixBuffer;
- mMixBuffer = new int16_t[mNormalFrameCount * mChannelCount];
- memset(mMixBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t));
+ delete[] mAllocMixBuffer;
+ size_t align = (mFrameSize < sizeof(int16_t)) ? sizeof(int16_t) : mFrameSize;
+ mAllocMixBuffer = new int8_t[mNormalFrameCount * mFrameSize + align - 1];
+ mMixBuffer = (int16_t *) ((((size_t)mAllocMixBuffer + align - 1) / align) * align);
+ memset(mMixBuffer, 0, mNormalFrameCount * mFrameSize);
// force reconfiguration of effect chains and engines to take new buffer size and audio
// parameters into account
@@ -1613,16 +1806,21 @@ void AudioFlinger::PlaybackThread::threadLoop_removeTracks(
const Vector< sp<Track> >& tracksToRemove)
{
size_t count = tracksToRemove.size();
- if (CC_UNLIKELY(count)) {
+ if (count) {
for (size_t i = 0 ; i < count ; i++) {
const sp<Track>& track = tracksToRemove.itemAt(i);
- if ((track->sharedBuffer() != 0) &&
- (track->mState == TrackBase::ACTIVE || track->mState == TrackBase::RESUMING)) {
+ if (!track->isOutputTrack()) {
AudioSystem::stopOutput(mId, track->streamType(), track->sessionId());
+#ifdef ADD_BATTERY_DATA
+ // to track the speaker usage
+ addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
+#endif
+ if (track->isTerminated()) {
+ AudioSystem::releaseOutput(mId);
+ }
}
}
}
-
}
void AudioFlinger::PlaybackThread::checkSilentMode_l()
@@ -1643,17 +1841,18 @@ void AudioFlinger::PlaybackThread::checkSilentMode_l()
}
// shared by MIXER and DIRECT, overridden by DUPLICATING
-void AudioFlinger::PlaybackThread::threadLoop_write()
+ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
{
// FIXME rewrite to reduce number of system calls
mLastWriteTime = systemTime();
mInWrite = true;
- int bytesWritten;
+ ssize_t bytesWritten;
// If an NBAIO sink is present, use it to write the normal mixer's submix
if (mNormalSink != 0) {
#define mBitShift 2 // FIXME
- size_t count = mixBufferSize >> mBitShift;
+ size_t count = mBytesRemaining >> mBitShift;
+ size_t offset = (mCurrentWriteLength - mBytesRemaining) >> 1;
ATRACE_BEGIN("write");
// update the setpoint when AudioFlinger::mScreenState changes
uint32_t screenState = AudioFlinger::mScreenState;
@@ -1665,24 +1864,70 @@ void AudioFlinger::PlaybackThread::threadLoop_write()
(pipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
}
}
- ssize_t framesWritten = mNormalSink->write(mMixBuffer, count);
+ ssize_t framesWritten = mNormalSink->write(mMixBuffer + offset, count);
ATRACE_END();
if (framesWritten > 0) {
bytesWritten = framesWritten << mBitShift;
} else {
bytesWritten = framesWritten;
}
+ status_t status = mNormalSink->getTimestamp(mLatchD.mTimestamp);
+ if (status == NO_ERROR) {
+ size_t totalFramesWritten = mNormalSink->framesWritten();
+ if (totalFramesWritten >= mLatchD.mTimestamp.mPosition) {
+ mLatchD.mUnpresentedFrames = totalFramesWritten - mLatchD.mTimestamp.mPosition;
+ mLatchDValid = true;
+ }
+ }
// otherwise use the HAL / AudioStreamOut directly
} else {
- // Direct output thread.
- bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
+ // Direct output and offload threads
+ size_t offset = (mCurrentWriteLength - mBytesRemaining) / sizeof(int16_t);
+ if (mUseAsyncWrite) {
+ ALOGW_IF(mWriteAckSequence & 1, "threadLoop_write(): out of sequence write request");
+ mWriteAckSequence += 2;
+ mWriteAckSequence |= 1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ }
+ // FIXME We should have an implementation of timestamps for direct output threads.
+ // They are used e.g for multichannel PCM playback over HDMI.
+ bytesWritten = mOutput->stream->write(mOutput->stream,
+ mMixBuffer + offset, mBytesRemaining);
+ if (mUseAsyncWrite &&
+ ((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) {
+ // do not wait for async callback in case of error of full write
+ mWriteAckSequence &= ~1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ }
}
- if (bytesWritten > 0) {
- mBytesWritten += mixBufferSize;
- }
mNumWrites++;
mInWrite = false;
+
+ return bytesWritten;
+}
+
+void AudioFlinger::PlaybackThread::threadLoop_drain()
+{
+ if (mOutput->stream->drain) {
+ ALOGV("draining %s", (mMixerStatus == MIXER_DRAIN_TRACK) ? "early" : "full");
+ if (mUseAsyncWrite) {
+ ALOGW_IF(mDrainSequence & 1, "threadLoop_drain(): out of sequence drain request");
+ mDrainSequence |= 1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setDraining(mDrainSequence);
+ }
+ mOutput->stream->drain(mOutput->stream,
+ (mMixerStatus == MIXER_DRAIN_TRACK) ? AUDIO_DRAIN_EARLY_NOTIFY
+ : AUDIO_DRAIN_ALL);
+ }
+}
+
+void AudioFlinger::PlaybackThread::threadLoop_exit()
+{
+ // Default implementation has nothing to do
}
/*
@@ -1713,7 +1958,7 @@ void AudioFlinger::PlaybackThread::cacheParameters_l()
void AudioFlinger::PlaybackThread::invalidateTracks(audio_stream_type_t streamType)
{
- ALOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d",
+ ALOGV("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d",
this, streamType, mTracks.size());
Mutex::Autolock _l(mLock);
@@ -1899,6 +2144,8 @@ bool AudioFlinger::PlaybackThread::threadLoop()
// and then that string will be logged at the next convenient opportunity.
const char *logString = NULL;
+ checkSilentMode_l();
+
while (!exitPending())
{
cpuStats.sample(myName);
@@ -1917,16 +2164,38 @@ bool AudioFlinger::PlaybackThread::threadLoop()
logString = NULL;
}
+ if (mLatchDValid) {
+ mLatchQ = mLatchD;
+ mLatchDValid = false;
+ mLatchQValid = true;
+ }
+
if (checkForNewParameters_l()) {
cacheParameters_l();
}
saveOutputTracks();
+ if (mSignalPending) {
+ // A signal was raised while we were unlocked
+ mSignalPending = false;
+ } else if (waitingAsyncCallback_l()) {
+ if (exitPending()) {
+ break;
+ }
+ releaseWakeLock_l();
+ ALOGV("wait async completion");
+ mWaitWorkCV.wait(mLock);
+ ALOGV("async completion/wake");
+ acquireWakeLock_l();
+ standbyTime = systemTime() + standbyDelay;
+ sleepTime = 0;
- // put audio hardware into standby after short delay
- if (CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
- isSuspended())) {
- if (!mStandby) {
+ continue;
+ }
+ if ((!mActiveTracks.size() && systemTime() > standbyTime) ||
+ isSuspended()) {
+ // put audio hardware into standby after short delay
+ if (shouldStandby_l()) {
threadLoop_standby();
@@ -1953,7 +2222,7 @@ bool AudioFlinger::PlaybackThread::threadLoop()
mMixerStatus = MIXER_IDLE;
mMixerStatusIgnoringFastTracks = MIXER_IDLE;
mBytesWritten = 0;
-
+ mBytesRemaining = 0;
checkSilentMode_l();
standbyTime = systemTime() + standbyDelay;
@@ -1965,7 +2234,6 @@ bool AudioFlinger::PlaybackThread::threadLoop()
continue;
}
}
-
// mMixerStatusIgnoringFastTracks is also updated internally
mMixerStatus = prepareTracks_l(&tracksToRemove);
@@ -1975,19 +2243,40 @@ bool AudioFlinger::PlaybackThread::threadLoop()
lockEffectChains_l(effectChains);
}
- if (CC_LIKELY(mMixerStatus == MIXER_TRACKS_READY)) {
- threadLoop_mix();
- } else {
- threadLoop_sleepTime();
- }
+ if (mBytesRemaining == 0) {
+ mCurrentWriteLength = 0;
+ if (mMixerStatus == MIXER_TRACKS_READY) {
+ // threadLoop_mix() sets mCurrentWriteLength
+ threadLoop_mix();
+ } else if ((mMixerStatus != MIXER_DRAIN_TRACK)
+ && (mMixerStatus != MIXER_DRAIN_ALL)) {
+ // threadLoop_sleepTime sets sleepTime to 0 if data
+ // must be written to HAL
+ threadLoop_sleepTime();
+ if (sleepTime == 0) {
+ mCurrentWriteLength = mixBufferSize;
+ }
+ }
+ mBytesRemaining = mCurrentWriteLength;
+ if (isSuspended()) {
+ sleepTime = suspendSleepTimeUs();
+ // simulate write to HAL when suspended
+ mBytesWritten += mixBufferSize;
+ mBytesRemaining = 0;
+ }
- if (isSuspended()) {
- sleepTime = suspendSleepTimeUs();
- mBytesWritten += mixBufferSize;
+ // only process effects if we're going to write
+ if (sleepTime == 0 && mType != OFFLOAD) {
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ effectChains[i]->process_l();
+ }
+ }
}
-
- // only process effects if we're going to write
- if (sleepTime == 0) {
+ // Process effect chains for offloaded thread even if no audio
+ // was read from audio track: process only updates effect state
+ // and thus does have to be synchronized with audio writes but may have
+ // to be called while waiting for async write callback
+ if (mType == OFFLOAD) {
for (size_t i = 0; i < effectChains.size(); i ++) {
effectChains[i]->process_l();
}
@@ -1996,29 +2285,40 @@ bool AudioFlinger::PlaybackThread::threadLoop()
// enable changes in effect chain
unlockEffectChains(effectChains);
- // sleepTime == 0 means we must write to audio hardware
- if (sleepTime == 0) {
-
- threadLoop_write();
-
+ if (!waitingAsyncCallback()) {
+ // sleepTime == 0 means we must write to audio hardware
+ if (sleepTime == 0) {
+ if (mBytesRemaining) {
+ ssize_t ret = threadLoop_write();
+ if (ret < 0) {
+ mBytesRemaining = 0;
+ } else {
+ mBytesWritten += ret;
+ mBytesRemaining -= ret;
+ }
+ } else if ((mMixerStatus == MIXER_DRAIN_TRACK) ||
+ (mMixerStatus == MIXER_DRAIN_ALL)) {
+ threadLoop_drain();
+ }
if (mType == MIXER) {
- // write blocked detection
- nsecs_t now = systemTime();
- nsecs_t delta = now - mLastWriteTime;
- if (!mStandby && delta > maxPeriod) {
- mNumDelayedWrites++;
- if ((now - lastWarning) > kWarningThrottleNs) {
- ATRACE_NAME("underrun");
- ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
- ns2ms(delta), mNumDelayedWrites, this);
- lastWarning = now;
+ // write blocked detection
+ nsecs_t now = systemTime();
+ nsecs_t delta = now - mLastWriteTime;
+ if (!mStandby && delta > maxPeriod) {
+ mNumDelayedWrites++;
+ if ((now - lastWarning) > kWarningThrottleNs) {
+ ATRACE_NAME("underrun");
+ ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
+ ns2ms(delta), mNumDelayedWrites, this);
+ lastWarning = now;
+ }
}
- }
}
- mStandby = false;
- } else {
- usleep(sleepTime);
+ mStandby = false;
+ } else {
+ usleep(sleepTime);
+ }
}
// Finally let go of removed track(s), without the lock held
@@ -2040,8 +2340,10 @@ if (mType == MIXER) {
// is now local to this block, but will keep it for now (at least until merge done).
}
+ threadLoop_exit();
+
// for DuplicatingThread, standby mode is handled by the outputTracks, otherwise ...
- if (mType == MIXER || mType == DIRECT) {
+ if (mType == MIXER || mType == DIRECT || mType == OFFLOAD) {
// put output stream into standby mode
if (!mStandby) {
mOutput->stream->common.standby(&mOutput->stream->common);
@@ -2054,7 +2356,45 @@ if (mType == MIXER) {
return false;
}
+// removeTracks_l() must be called with ThreadBase::mLock held
+void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp<Track> >& tracksToRemove)
+{
+ size_t count = tracksToRemove.size();
+ if (count) {
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<Track>& track = tracksToRemove.itemAt(i);
+ mActiveTracks.remove(track);
+ ALOGV("removeTracks_l removing track on session %d", track->sessionId());
+ sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+ if (chain != 0) {
+ ALOGV("stopping track on chain %p for session Id: %d", chain.get(),
+ track->sessionId());
+ chain->decActiveTrackCnt();
+ }
+ if (track->isTerminated()) {
+ removeTrack_l(track);
+ }
+ }
+ }
+
+}
+status_t AudioFlinger::PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp)
+{
+ if (mNormalSink != 0) {
+ return mNormalSink->getTimestamp(timestamp);
+ }
+ if (mType == OFFLOAD && mOutput->stream->get_presentation_position) {
+ uint64_t position64;
+ int ret = mOutput->stream->get_presentation_position(
+ mOutput->stream, &position64, &timestamp.mTime);
+ if (ret == 0) {
+ timestamp.mPosition = (uint32_t)position64;
+ return NO_ERROR;
+ }
+ }
+ return INVALID_OPERATION;
+}
// ----------------------------------------------------------------------------
AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
@@ -2068,7 +2408,7 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud
// mNormalSink below
{
ALOGV("MixerThread() id=%d device=%#x type=%d", id, device, type);
- ALOGV("mSampleRate=%u, mChannelMask=%#x, mChannelCount=%d, mFormat=%d, mFrameSize=%u, "
+ ALOGV("mSampleRate=%u, mChannelMask=%#x, mChannelCount=%u, mFormat=%d, mFrameSize=%u, "
"mFrameCount=%d, mNormalFrameCount=%d",
mSampleRate, mChannelMask, mChannelCount, mFormat, mFrameSize, mFrameCount,
mNormalFrameCount);
@@ -2258,7 +2598,7 @@ void AudioFlinger::MixerThread::threadLoop_removeTracks(const Vector< sp<Track>
PlaybackThread::threadLoop_removeTracks(tracksToRemove);
}
-void AudioFlinger::MixerThread::threadLoop_write()
+ssize_t AudioFlinger::MixerThread::threadLoop_write()
{
// FIXME we should only do one push per cycle; confirm this is true
// Start the fast mixer if it's not already running
@@ -2279,6 +2619,8 @@ void AudioFlinger::MixerThread::threadLoop_write()
#endif
}
state->mCommand = FastMixerState::MIX_WRITE;
+ mFastMixerDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ?
+ FastMixerDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN);
sq->end();
sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
if (kUseFastMixer == FastMixer_Dynamic) {
@@ -2288,7 +2630,7 @@ void AudioFlinger::MixerThread::threadLoop_write()
sq->end(false /*didModify*/);
}
}
- PlaybackThread::threadLoop_write();
+ return PlaybackThread::threadLoop_write();
}
void AudioFlinger::MixerThread::threadLoop_standby()
@@ -2320,11 +2662,41 @@ void AudioFlinger::MixerThread::threadLoop_standby()
PlaybackThread::threadLoop_standby();
}
+// Empty implementation for standard mixer
+// Overridden for offloaded playback
+void AudioFlinger::PlaybackThread::flushOutput_l()
+{
+}
+
+bool AudioFlinger::PlaybackThread::waitingAsyncCallback_l()
+{
+ return false;
+}
+
+bool AudioFlinger::PlaybackThread::shouldStandby_l()
+{
+ return !mStandby;
+}
+
+bool AudioFlinger::PlaybackThread::waitingAsyncCallback()
+{
+ Mutex::Autolock _l(mLock);
+ return waitingAsyncCallback_l();
+}
+
// shared by MIXER and DIRECT, overridden by DUPLICATING
void AudioFlinger::PlaybackThread::threadLoop_standby()
{
ALOGV("Audio hardware entering standby, mixer %p, suspend count %d", this, mSuspended);
mOutput->stream->common.standby(&mOutput->stream->common);
+ if (mUseAsyncWrite != 0) {
+ // discard any pending drain or write ack by incrementing sequence
+ mWriteAckSequence = (mWriteAckSequence + 2) & ~1;
+ mDrainSequence = (mDrainSequence + 2) & ~1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ mCallbackThread->setDraining(mDrainSequence);
+ }
}
void AudioFlinger::MixerThread::threadLoop_mix()
@@ -2345,6 +2717,7 @@ void AudioFlinger::MixerThread::threadLoop_mix()
// mix buffers...
mAudioMixer->process(pts);
+ mCurrentWriteLength = mixBufferSize;
// increase sleep time progressively when application underrun condition clears.
// Only increase sleep time if the mixer is ready for two consecutive times to avoid
// that a steady state of alternating ready/not ready conditions keeps the sleep time
@@ -2426,7 +2799,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
}
for (size_t i=0 ; i<count ; i++) {
- sp<Track> t = mActiveTracks[i].promote();
+ const sp<Track> t = mActiveTracks[i].promote();
if (t == 0) {
continue;
}
@@ -2462,8 +2835,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
track->mObservedUnderruns = underruns;
// don't count underruns that occur while stopping or pausing
// or stopped which can occur when flush() is called while active
- if (!(track->isStopping() || track->isPausing() || track->isStopped())) {
- track->mUnderrunCount += recentUnderruns;
+ if (!(track->isStopping() || track->isPausing() || track->isStopped()) &&
+ recentUnderruns > 0) {
+ // FIXME fast mixer will pull & mix partial buffers, but we count as a full underrun
+ track->mAudioTrackServerProxy->tallyUnderrunFrames(recentUnderruns * mFrameCount);
}
// This is similar to the state machine for normal tracks,
@@ -2472,7 +2847,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
switch (track->mState) {
case TrackBase::STOPPING_1:
// track stays active in STOPPING_1 state until first underrun
- if (recentUnderruns > 0) {
+ if (recentUnderruns > 0 || track->isTerminated()) {
track->mState = TrackBase::STOPPING_2;
}
break;
@@ -2506,7 +2881,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
}
// indicate to client process that the track was disabled because of underrun;
// it will then automatically call start() when data is available
- android_atomic_or(CBLK_DISABLED, &track->mCblk->flags);
+ android_atomic_or(CBLK_DISABLED, &track->mCblk->mFlags);
// remove from active list, but state remains ACTIVE [confusing but true]
isActive = false;
break;
@@ -2514,7 +2889,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
// fall through
case TrackBase::STOPPING_2:
case TrackBase::PAUSED:
- case TrackBase::TERMINATED:
case TrackBase::STOPPED:
case TrackBase::FLUSHED: // flush() while active
// Check for presentation complete if track is inactive
@@ -2595,28 +2969,39 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
// app does not call stop() and relies on underrun to stop:
// hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed
// during last round
+ size_t desiredFrames;
+ uint32_t sr = track->sampleRate();
+ if (sr == mSampleRate) {
+ desiredFrames = mNormalFrameCount;
+ } else {
+ // +1 for rounding and +1 for additional sample needed for interpolation
+ desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1;
+ // add frames already consumed but not yet released by the resampler
+ // because cblk->framesReady() will include these frames
+ desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
+ // the minimum track buffer size is normally twice the number of frames necessary
+ // to fill one buffer and the resampler should not leave more than one buffer worth
+ // of unreleased frames after each pass, but just in case...
+ ALOG_ASSERT(desiredFrames <= cblk->frameCount_);
+ }
uint32_t minFrames = 1;
if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
- if (t->sampleRate() == mSampleRate) {
- minFrames = mNormalFrameCount;
- } else {
- // +1 for rounding and +1 for additional sample needed for interpolation
- minFrames = (mNormalFrameCount * t->sampleRate()) / mSampleRate + 1 + 1;
- // add frames already consumed but not yet released by the resampler
- // because cblk->framesReady() will include these frames
- minFrames += mAudioMixer->getUnreleasedFrames(track->name());
- // the minimum track buffer size is normally twice the number of frames necessary
- // to fill one buffer and the resampler should not leave more than one buffer worth
- // of unreleased frames after each pass, but just in case...
- ALOG_ASSERT(minFrames <= cblk->frameCount_);
- }
+ minFrames = desiredFrames;
}
- if ((track->framesReady() >= minFrames) && track->isReady() &&
+ // It's not safe to call framesReady() for a static buffer track, so assume it's ready
+ size_t framesReady;
+ if (track->sharedBuffer() == 0) {
+ framesReady = track->framesReady();
+ } else if (track->isStopped()) {
+ framesReady = 0;
+ } else {
+ framesReady = 1;
+ }
+ if ((framesReady >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
{
- ALOGVV("track %d u=%08x, s=%08x [OK] on thread %p", name, cblk->user, cblk->server,
- this);
+ ALOGVV("track %d s=%08x [OK] on thread %p", name, cblk->mServer, this);
mixedTracks++;
@@ -2645,7 +3030,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
param = AudioMixer::RAMP_VOLUME;
}
mAudioMixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
- } else if (cblk->server != 0) {
+ // FIXME should not make a decision based on mServer
+ } else if (cblk->mServer != 0) {
// If the track is stopped before the first frame was mixed,
// do not apply ramp
param = AudioMixer::RAMP_VOLUME;
@@ -2663,7 +3049,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
// read original volumes with volume control
float typeVolume = mStreamTypes[track->streamType()].volume;
float v = masterVolume * typeVolume;
- ServerProxy *proxy = track->mServerProxy;
+ AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
uint32_t vlr = proxy->getVolumeLR();
vl = vlr & 0xFFFF;
vr = vlr >> 16;
@@ -2690,6 +3076,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
}
va = (uint32_t)(v * sendLevel);
}
+
// Delegate volume control to effect in track effect chain if needed
if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
// Do not ramp volume if volume is controlled by effect
@@ -2736,7 +3123,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
AudioMixer::CHANNEL_MASK, (void *)track->channelMask());
// limit track sample rate to 2 x output sample rate, which changes at re-configuration
uint32_t maxSampleRate = mSampleRate * 2;
- uint32_t reqSampleRate = track->mServerProxy->getSampleRate();
+ uint32_t reqSampleRate = track->mAudioTrackServerProxy->getSampleRate();
if (reqSampleRate == 0) {
reqSampleRate = mSampleRate;
} else if (reqSampleRate > maxSampleRate) {
@@ -2767,6 +3154,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
mixerStatus = MIXER_TRACKS_READY;
}
} else {
+ if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) {
+ track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
+ }
// clear effect chain input buffer if an active track underruns to avoid sending
// previous audio buffer again to effects
chain = getEffectChain_l(track->sessionId());
@@ -2774,8 +3164,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
chain->clearInputBuffer();
}
- ALOGVV("track %d u=%08x, s=%08x [NOT READY] on thread %p", name, cblk->user,
- cblk->server, this);
+ ALOGVV("track %d s=%08x [NOT READY] on thread %p", name, cblk->mServer, this);
if ((track->sharedBuffer() != 0) || track->isTerminated() ||
track->isStopped() || track->isPaused()) {
// We have consumed all the buffers of this track.
@@ -2791,7 +3180,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
tracksToRemove->add(track);
}
} else {
- track->mUnderrunCount++;
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
if (--(track->mRetryCount) <= 0) {
@@ -2799,7 +3187,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
tracksToRemove->add(track);
// indicate to client process that the track was disabled because of underrun;
// it will then automatically call start() when data is available
- android_atomic_or(CBLK_DISABLED, &cblk->flags);
+ android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
// If one track is not ready, mark the mixer also not ready if:
// - the mixer was ready during previous round OR
// - no other track is ready
@@ -2861,30 +3249,13 @@ track_is_ready: ;
}
// remove all the tracks that need to be...
- count = tracksToRemove->size();
- if (CC_UNLIKELY(count)) {
- for (size_t i=0 ; i<count ; i++) {
- const sp<Track>& track = tracksToRemove->itemAt(i);
- mActiveTracks.remove(track);
- if (track->mainBuffer() != mMixBuffer) {
- chain = getEffectChain_l(track->sessionId());
- if (chain != 0) {
- ALOGV("stopping track on chain %p for session Id: %d", chain.get(),
- track->sessionId());
- chain->decActiveTrackCnt();
- }
- }
- if (track->isTerminated()) {
- removeTrack_l(track);
- }
- }
- }
+ removeTracks_l(*tracksToRemove);
// mix buffer must be cleared if all tracks are connected to an
// effect chain as in this case the mixer will not write to
// mix buffer and track effects will accumulate into it
- if ((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||
- (mixedTracks == 0 && fastTracks > 0)) {
+ if ((mBytesRemaining == 0) && ((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||
+ (mixedTracks == 0 && fastTracks > 0))) {
// FIXME as a performance optimization, should remember previous zero status
memset(mMixBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t));
}
@@ -2948,7 +3319,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
}
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
- if (value != AUDIO_CHANNEL_OUT_STEREO) {
+ if ((audio_channel_mask_t) value != AUDIO_CHANNEL_OUT_STEREO) {
status = BAD_VALUE;
} else {
reconfig = true;
@@ -3009,10 +3380,8 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
keyValuePair.string());
}
if (status == NO_ERROR && reconfig) {
- delete mAudioMixer;
- // for safety in case readOutputParameters() accesses mAudioMixer (it doesn't)
- mAudioMixer = NULL;
readOutputParameters();
+ delete mAudioMixer;
mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
for (size_t i = 0; i < mTracks.size() ; i++) {
int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId);
@@ -3061,7 +3430,7 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& ar
write(fd, result.string(), result.size());
// Make a non-atomic copy of fast mixer dump state so it won't change underneath us
- FastMixerDumpState copy = mFastMixerDumpState;
+ const FastMixerDumpState copy(mFastMixerDumpState);
copy.dump(fd);
#ifdef STATE_QUEUE_DUMP
@@ -3116,10 +3485,63 @@ AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& aud
{
}
+AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamOut* output, audio_io_handle_t id, uint32_t device,
+ ThreadBase::type_t type)
+ : PlaybackThread(audioFlinger, output, id, device, type)
+ // mLeftVolFloat, mRightVolFloat
+{
+}
+
AudioFlinger::DirectOutputThread::~DirectOutputThread()
{
}
+void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTrack)
+{
+ audio_track_cblk_t* cblk = track->cblk();
+ float left, right;
+
+ if (mMasterMute || mStreamTypes[track->streamType()].mute) {
+ left = right = 0;
+ } else {
+ float typeVolume = mStreamTypes[track->streamType()].volume;
+ float v = mMasterVolume * typeVolume;
+ AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
+ uint32_t vlr = proxy->getVolumeLR();
+ float v_clamped = v * (vlr & 0xFFFF);
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ left = v_clamped/MAX_GAIN;
+ v_clamped = v * (vlr >> 16);
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ right = v_clamped/MAX_GAIN;
+ }
+
+ if (lastTrack) {
+ if (left != mLeftVolFloat || right != mRightVolFloat) {
+ mLeftVolFloat = left;
+ mRightVolFloat = right;
+
+ // Convert volumes from float to 8.24
+ uint32_t vl = (uint32_t)(left * (1 << 24));
+ uint32_t vr = (uint32_t)(right * (1 << 24));
+
+ // Delegate volume control to effect in track effect chain if needed
+ // only one effect chain can be present on DirectOutputThread, so if
+ // there is one, the track is connected to it
+ if (!mEffectChains.isEmpty()) {
+ mEffectChains[0]->setVolume_l(&vl, &vr);
+ left = (float)vl / (1 << 24);
+ right = (float)vr / (1 << 24);
+ }
+ if (mOutput->stream->set_volume) {
+ mOutput->stream->set_volume(mOutput->stream, left, right);
+ }
+ }
+ }
+}
+
+
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prepareTracks_l(
Vector< sp<Track> > *tracksToRemove
)
@@ -3146,66 +3568,29 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
} else {
minFrames = 1;
}
+ // Only consider last track started for volume and mixer state control.
+ // This is the last entry in mActiveTracks unless a track underruns.
+ // As we only care about the transition phase between two tracks on a
+ // direct output, it is not a problem to ignore the underrun case.
+ bool last = (i == (count - 1));
+
if ((track->framesReady() >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
{
- ALOGVV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
+ ALOGVV("track %d s=%08x [OK]", track->name(), cblk->mServer);
if (track->mFillingUpStatus == Track::FS_FILLED) {
track->mFillingUpStatus = Track::FS_ACTIVE;
- mLeftVolFloat = mRightVolFloat = 0;
+ // make sure processVolume_l() will apply new volume even if 0
+ mLeftVolFloat = mRightVolFloat = -1.0;
if (track->mState == TrackBase::RESUMING) {
track->mState = TrackBase::ACTIVE;
}
}
// compute volume for this track
- float left, right;
- if (mMasterMute || track->isPausing() || mStreamTypes[track->streamType()].mute) {
- left = right = 0;
- if (track->isPausing()) {
- track->setPaused();
- }
- } else {
- float typeVolume = mStreamTypes[track->streamType()].volume;
- float v = mMasterVolume * typeVolume;
- uint32_t vlr = track->mServerProxy->getVolumeLR();
- float v_clamped = v * (vlr & 0xFFFF);
- if (v_clamped > MAX_GAIN) {
- v_clamped = MAX_GAIN;
- }
- left = v_clamped/MAX_GAIN;
- v_clamped = v * (vlr >> 16);
- if (v_clamped > MAX_GAIN) {
- v_clamped = MAX_GAIN;
- }
- right = v_clamped/MAX_GAIN;
- }
- // Only consider last track started for volume and mixer state control.
- // This is the last entry in mActiveTracks unless a track underruns.
- // As we only care about the transition phase between two tracks on a
- // direct output, it is not a problem to ignore the underrun case.
- if (i == (count - 1)) {
- if (left != mLeftVolFloat || right != mRightVolFloat) {
- mLeftVolFloat = left;
- mRightVolFloat = right;
-
- // Convert volumes from float to 8.24
- uint32_t vl = (uint32_t)(left * (1 << 24));
- uint32_t vr = (uint32_t)(right * (1 << 24));
-
- // Delegate volume control to effect in track effect chain if needed
- // only one effect chain can be present on DirectOutputThread, so if
- // there is one, the track is connected to it
- if (!mEffectChains.isEmpty()) {
- // Do not ramp volume if volume is controlled by effect
- mEffectChains[0]->setVolume_l(&vl, &vr);
- left = (float)vl / (1 << 24);
- right = (float)vr / (1 << 24);
- }
- mOutput->stream->set_volume(mOutput->stream, left, right);
- }
-
+ processVolume_l(track, last);
+ if (last) {
// reset retry count
track->mRetryCount = kMaxTrackRetriesDirect;
mActiveTrack = t;
@@ -3218,7 +3603,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
mEffectChains[0]->clearInputBuffer();
}
- ALOGVV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
+ ALOGVV("track %d s=%08x [NOT READY]", track->name(), cblk->mServer);
if ((track->sharedBuffer() != 0) || track->isTerminated() ||
track->isStopped() || track->isPaused()) {
// We have consumed all the buffers of this track.
@@ -3239,7 +3624,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
if (--(track->mRetryCount) <= 0) {
ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
tracksToRemove->add(track);
- } else if (i == (count -1)){
+ } else if (last) {
mixerStatus = MIXER_TRACKS_ENABLED;
}
}
@@ -3247,35 +3632,21 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
}
// remove all the tracks that need to be...
- count = tracksToRemove->size();
- if (CC_UNLIKELY(count)) {
- for (size_t i = 0 ; i < count ; i++) {
- const sp<Track>& track = tracksToRemove->itemAt(i);
- mActiveTracks.remove(track);
- if (!mEffectChains.isEmpty()) {
- ALOGV("stopping track on chain %p for session Id: %d", mEffectChains[0].get(),
- track->sessionId());
- mEffectChains[0]->decActiveTrackCnt();
- }
- if (track->isTerminated()) {
- removeTrack_l(track);
- }
- }
- }
+ removeTracks_l(*tracksToRemove);
return mixerStatus;
}
void AudioFlinger::DirectOutputThread::threadLoop_mix()
{
- AudioBufferProvider::Buffer buffer;
size_t frameCount = mFrameCount;
int8_t *curBuf = (int8_t *)mMixBuffer;
// output audio to hardware
while (frameCount) {
+ AudioBufferProvider::Buffer buffer;
buffer.frameCount = frameCount;
mActiveTrack->getNextBuffer(&buffer);
- if (CC_UNLIKELY(buffer.raw == NULL)) {
+ if (buffer.raw == NULL) {
memset(curBuf, 0, frameCount * mFrameSize);
break;
}
@@ -3284,10 +3655,10 @@ void AudioFlinger::DirectOutputThread::threadLoop_mix()
curBuf += buffer.frameCount * mFrameSize;
mActiveTrack->releaseBuffer(&buffer);
}
+ mCurrentWriteLength = curBuf - (int8_t *)mMixBuffer;
sleepTime = 0;
standbyTime = systemTime() + standbyDelay;
mActiveTrack.clear();
-
}
void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
@@ -3403,7 +3774,358 @@ void AudioFlinger::DirectOutputThread::cacheParameters_l()
// use shorter standby delay as on normal output to release
// hardware resources as soon as possible
- standbyDelay = microseconds(activeSleepTime*2);
+ if (audio_is_linear_pcm(mFormat)) {
+ standbyDelay = microseconds(activeSleepTime*2);
+ } else {
+ standbyDelay = kOffloadStandbyDelayNs;
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::AsyncCallbackThread::AsyncCallbackThread(
+ const wp<AudioFlinger::PlaybackThread>& playbackThread)
+ : Thread(false /*canCallJava*/),
+ mPlaybackThread(playbackThread),
+ mWriteAckSequence(0),
+ mDrainSequence(0)
+{
+}
+
+AudioFlinger::AsyncCallbackThread::~AsyncCallbackThread()
+{
+}
+
+void AudioFlinger::AsyncCallbackThread::onFirstRef()
+{
+ run("Offload Cbk", ANDROID_PRIORITY_URGENT_AUDIO);
+}
+
+bool AudioFlinger::AsyncCallbackThread::threadLoop()
+{
+ while (!exitPending()) {
+ uint32_t writeAckSequence;
+ uint32_t drainSequence;
+
+ {
+ Mutex::Autolock _l(mLock);
+ mWaitWorkCV.wait(mLock);
+ if (exitPending()) {
+ break;
+ }
+ ALOGV("AsyncCallbackThread mWriteAckSequence %d mDrainSequence %d",
+ mWriteAckSequence, mDrainSequence);
+ writeAckSequence = mWriteAckSequence;
+ mWriteAckSequence &= ~1;
+ drainSequence = mDrainSequence;
+ mDrainSequence &= ~1;
+ }
+ {
+ sp<AudioFlinger::PlaybackThread> playbackThread = mPlaybackThread.promote();
+ if (playbackThread != 0) {
+ if (writeAckSequence & 1) {
+ playbackThread->resetWriteBlocked(writeAckSequence >> 1);
+ }
+ if (drainSequence & 1) {
+ playbackThread->resetDraining(drainSequence >> 1);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void AudioFlinger::AsyncCallbackThread::exit()
+{
+ ALOGV("AsyncCallbackThread::exit");
+ Mutex::Autolock _l(mLock);
+ requestExit();
+ mWaitWorkCV.broadcast();
+}
+
+void AudioFlinger::AsyncCallbackThread::setWriteBlocked(uint32_t sequence)
+{
+ Mutex::Autolock _l(mLock);
+ // bit 0 is cleared
+ mWriteAckSequence = sequence << 1;
+}
+
+void AudioFlinger::AsyncCallbackThread::resetWriteBlocked()
+{
+ Mutex::Autolock _l(mLock);
+ // ignore unexpected callbacks
+ if (mWriteAckSequence & 2) {
+ mWriteAckSequence |= 1;
+ mWaitWorkCV.signal();
+ }
+}
+
+void AudioFlinger::AsyncCallbackThread::setDraining(uint32_t sequence)
+{
+ Mutex::Autolock _l(mLock);
+ // bit 0 is cleared
+ mDrainSequence = sequence << 1;
+}
+
+void AudioFlinger::AsyncCallbackThread::resetDraining()
+{
+ Mutex::Autolock _l(mLock);
+ // ignore unexpected callbacks
+ if (mDrainSequence & 2) {
+ mDrainSequence |= 1;
+ mWaitWorkCV.signal();
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+AudioFlinger::OffloadThread::OffloadThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamOut* output, audio_io_handle_t id, uint32_t device)
+ : DirectOutputThread(audioFlinger, output, id, device, OFFLOAD),
+ mHwPaused(false),
+ mFlushPending(false),
+ mPausedBytesRemaining(0)
+{
+}
+
+AudioFlinger::OffloadThread::~OffloadThread()
+{
+ mPreviousTrack.clear();
+}
+
+void AudioFlinger::OffloadThread::threadLoop_exit()
+{
+ if (mFlushPending || mHwPaused) {
+ // If a flush is pending or track was paused, just discard buffered data
+ flushHw_l();
+ } else {
+ mMixerStatus = MIXER_DRAIN_ALL;
+ threadLoop_drain();
+ }
+ mCallbackThread->exit();
+ PlaybackThread::threadLoop_exit();
+}
+
+AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTracks_l(
+ Vector< sp<Track> > *tracksToRemove
+)
+{
+ size_t count = mActiveTracks.size();
+
+ mixer_state mixerStatus = MIXER_IDLE;
+ bool doHwPause = false;
+ bool doHwResume = false;
+
+ ALOGV("OffloadThread::prepareTracks_l active tracks %d", count);
+
+ // find out which tracks need to be processed
+ for (size_t i = 0; i < count; i++) {
+ sp<Track> t = mActiveTracks[i].promote();
+ // The track died recently
+ if (t == 0) {
+ continue;
+ }
+ Track* const track = t.get();
+ audio_track_cblk_t* cblk = track->cblk();
+ if (mPreviousTrack != NULL) {
+ if (t != mPreviousTrack) {
+ // Flush any data still being written from last track
+ mBytesRemaining = 0;
+ if (mPausedBytesRemaining) {
+ // Last track was paused so we also need to flush saved
+ // mixbuffer state and invalidate track so that it will
+ // re-submit that unwritten data when it is next resumed
+ mPausedBytesRemaining = 0;
+ // Invalidate is a bit drastic - would be more efficient
+ // to have a flag to tell client that some of the
+ // previously written data was lost
+ mPreviousTrack->invalidate();
+ }
+ }
+ }
+ mPreviousTrack = t;
+ bool last = (i == (count - 1));
+ if (track->isPausing()) {
+ track->setPaused();
+ if (last) {
+ if (!mHwPaused) {
+ doHwPause = true;
+ mHwPaused = true;
+ }
+ // If we were part way through writing the mixbuffer to
+ // the HAL we must save this until we resume
+ // BUG - this will be wrong if a different track is made active,
+ // in that case we want to discard the pending data in the
+ // mixbuffer and tell the client to present it again when the
+ // track is resumed
+ mPausedWriteLength = mCurrentWriteLength;
+ mPausedBytesRemaining = mBytesRemaining;
+ mBytesRemaining = 0; // stop writing
+ }
+ tracksToRemove->add(track);
+ } else if (track->framesReady() && track->isReady() &&
+ !track->isPaused() && !track->isTerminated() && !track->isStopping_2()) {
+ ALOGVV("OffloadThread: track %d s=%08x [OK]", track->name(), cblk->mServer);
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ // make sure processVolume_l() will apply new volume even if 0
+ mLeftVolFloat = mRightVolFloat = -1.0;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ if (last) {
+ if (mPausedBytesRemaining) {
+ // Need to continue write that was interrupted
+ mCurrentWriteLength = mPausedWriteLength;
+ mBytesRemaining = mPausedBytesRemaining;
+ mPausedBytesRemaining = 0;
+ }
+ if (mHwPaused) {
+ doHwResume = true;
+ mHwPaused = false;
+ // threadLoop_mix() will handle the case that we need to
+ // resume an interrupted write
+ }
+ // enable write to audio HAL
+ sleepTime = 0;
+ }
+ }
+ }
+
+ if (last) {
+ // reset retry count
+ track->mRetryCount = kMaxTrackRetriesOffload;
+ mActiveTrack = t;
+ mixerStatus = MIXER_TRACKS_READY;
+ }
+ } else {
+ ALOGVV("OffloadThread: track %d s=%08x [NOT READY]", track->name(), cblk->mServer);
+ if (track->isStopping_1()) {
+ // Hardware buffer can hold a large amount of audio so we must
+ // wait for all current track's data to drain before we say
+ // that the track is stopped.
+ if (mBytesRemaining == 0) {
+ // Only start draining when all data in mixbuffer
+ // has been written
+ ALOGV("OffloadThread: underrun and STOPPING_1 -> draining, STOPPING_2");
+ track->mState = TrackBase::STOPPING_2; // so presentation completes after drain
+ if (last) {
+ sleepTime = 0;
+ standbyTime = systemTime() + standbyDelay;
+ mixerStatus = MIXER_DRAIN_TRACK;
+ mDrainSequence += 2;
+ if (mHwPaused) {
+ // It is possible to move from PAUSED to STOPPING_1 without
+ // a resume so we must ensure hardware is running
+ mOutput->stream->resume(mOutput->stream);
+ mHwPaused = false;
+ }
+ }
+ }
+ } else if (track->isStopping_2()) {
+ // Drain has completed, signal presentation complete
+ if (!(mDrainSequence & 1) || !last) {
+ track->mState = TrackBase::STOPPED;
+ size_t audioHALFrames =
+ (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
+ size_t framesWritten =
+ mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
+ track->presentationComplete(framesWritten, audioHALFrames);
+ track->reset();
+ tracksToRemove->add(track);
+ }
+ } else {
+ // No buffers for this track. Give it a few chances to
+ // fill a buffer, then remove it from active list.
+ if (--(track->mRetryCount) <= 0) {
+ ALOGV("OffloadThread: BUFFER TIMEOUT: remove(%d) from active list",
+ track->name());
+ tracksToRemove->add(track);
+ } else if (last){
+ mixerStatus = MIXER_TRACKS_ENABLED;
+ }
+ }
+ }
+ // compute volume for this track
+ processVolume_l(track, last);
+ }
+
+ // make sure the pause/flush/resume sequence is executed in the right order.
+ // If a flush is pending and a track is active but the HW is not paused, force a HW pause
+ // before flush and then resume HW. This can happen in case of pause/flush/resume
+ // if resume is received before pause is executed.
+ if (doHwPause || (mFlushPending && !mHwPaused && (count != 0))) {
+ mOutput->stream->pause(mOutput->stream);
+ if (!doHwPause) {
+ doHwResume = true;
+ }
+ }
+ if (mFlushPending) {
+ flushHw_l();
+ mFlushPending = false;
+ }
+ if (doHwResume) {
+ mOutput->stream->resume(mOutput->stream);
+ }
+
+ // remove all the tracks that need to be...
+ removeTracks_l(*tracksToRemove);
+
+ return mixerStatus;
+}
+
+void AudioFlinger::OffloadThread::flushOutput_l()
+{
+ mFlushPending = true;
+}
+
+// must be called with thread mutex locked
+bool AudioFlinger::OffloadThread::waitingAsyncCallback_l()
+{
+ ALOGVV("waitingAsyncCallback_l mWriteAckSequence %d mDrainSequence %d",
+ mWriteAckSequence, mDrainSequence);
+ if (mUseAsyncWrite && ((mWriteAckSequence & 1) || (mDrainSequence & 1))) {
+ return true;
+ }
+ return false;
+}
+
+// must be called with thread mutex locked
+bool AudioFlinger::OffloadThread::shouldStandby_l()
+{
+ bool TrackPaused = false;
+
+ // do not put the HAL in standby when paused. AwesomePlayer clear the offloaded AudioTrack
+ // after a timeout and we will enter standby then.
+ if (mTracks.size() > 0) {
+ TrackPaused = mTracks[mTracks.size() - 1]->isPaused();
+ }
+
+ return !mStandby && !TrackPaused;
+}
+
+
+bool AudioFlinger::OffloadThread::waitingAsyncCallback()
+{
+ Mutex::Autolock _l(mLock);
+ return waitingAsyncCallback_l();
+}
+
+void AudioFlinger::OffloadThread::flushHw_l()
+{
+ mOutput->stream->flush(mOutput->stream);
+ // Flush anything still waiting in the mixbuffer
+ mCurrentWriteLength = 0;
+ mBytesRemaining = 0;
+ mPausedWriteLength = 0;
+ mPausedBytesRemaining = 0;
+ if (mUseAsyncWrite) {
+ // discard any pending drain or write ack by incrementing sequence
+ mWriteAckSequence = (mWriteAckSequence + 2) & ~1;
+ mDrainSequence = (mDrainSequence + 2) & ~1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ mCallbackThread->setDraining(mDrainSequence);
+ }
}
// ----------------------------------------------------------------------------
@@ -3434,6 +4156,7 @@ void AudioFlinger::DuplicatingThread::threadLoop_mix()
}
sleepTime = 0;
writeFrames = mNormalFrameCount;
+ mCurrentWriteLength = mixBufferSize;
standbyTime = systemTime() + standbyDelay;
}
@@ -3457,12 +4180,12 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime()
}
}
-void AudioFlinger::DuplicatingThread::threadLoop_write()
+ssize_t AudioFlinger::DuplicatingThread::threadLoop_write()
{
for (size_t i = 0; i < outputTracks.size(); i++) {
outputTracks[i]->write(mMixBuffer, writeFrames);
}
- mBytesWritten += mixBufferSize;
+ return (ssize_t)mixBufferSize;
}
void AudioFlinger::DuplicatingThread::threadLoop_standby()
@@ -3583,7 +4306,7 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
) :
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD),
mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
- // mRsmpInIndex and mInputBytes set by readInputParameters()
+ // mRsmpInIndex and mBufferSize set by readInputParameters()
mReqChannelCount(popcount(channelMask)),
mReqSampleRate(sampleRate)
// mBytesRead is only meaningful while active, and so is cleared in start()
@@ -3595,7 +4318,7 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
snprintf(mName, kNameLength, "AudioIn_%X", id);
readInputParameters();
-
+ mClientUid = IPCThreadState::self()->getCallingUid();
}
@@ -3627,7 +4350,7 @@ bool AudioFlinger::RecordThread::threadLoop()
nsecs_t lastWarning = 0;
inputStandBy();
- acquireWakeLock();
+ acquireWakeLock(mClientUid);
// used to verify we've read at least once before evaluating how many bytes were read
bool readOnce = false;
@@ -3652,11 +4375,14 @@ bool AudioFlinger::RecordThread::threadLoop()
// go to sleep
mWaitWorkCV.wait(mLock);
ALOGV("RecordThread: loop starting");
- acquireWakeLock_l();
+ acquireWakeLock_l(mClientUid);
continue;
}
if (mActiveTrack != 0) {
- if (mActiveTrack->mState == TrackBase::PAUSING) {
+ if (mActiveTrack->isTerminated()) {
+ removeTrack_l(mActiveTrack);
+ mActiveTrack.clear();
+ } else if (mActiveTrack->mState == TrackBase::PAUSING) {
standby();
mActiveTrack.clear();
mStartStopCond.broadcast();
@@ -3675,11 +4401,9 @@ bool AudioFlinger::RecordThread::threadLoop()
mStartStopCond.broadcast();
}
mStandby = false;
- } else if (mActiveTrack->mState == TrackBase::TERMINATED) {
- removeTrack_l(mActiveTrack);
- mActiveTrack.clear();
}
}
+
lockEffectChains_l(effectChains);
}
@@ -3695,7 +4419,8 @@ bool AudioFlinger::RecordThread::threadLoop()
}
buffer.frameCount = mFrameCount;
- if (CC_LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) {
+ status_t status = mActiveTrack->getNextBuffer(&buffer);
+ if (status == NO_ERROR) {
readOnce = true;
size_t framesOut = buffer.frameCount;
if (mResampler == NULL) {
@@ -3710,8 +4435,7 @@ bool AudioFlinger::RecordThread::threadLoop()
framesIn = framesOut;
mRsmpInIndex += framesIn;
framesOut -= framesIn;
- if (mChannelCount == mReqChannelCount ||
- mFormat != AUDIO_FORMAT_PCM_16_BIT) {
+ if (mChannelCount == mReqChannelCount) {
memcpy(dst, src, framesIn * mFrameSize);
} else {
if (mChannelCount == 1) {
@@ -3725,9 +4449,7 @@ bool AudioFlinger::RecordThread::threadLoop()
}
if (framesOut && mFrameCount == mRsmpInIndex) {
void *readInto;
- if (framesOut == mFrameCount &&
- (mChannelCount == mReqChannelCount ||
- mFormat != AUDIO_FORMAT_PCM_16_BIT)) {
+ if (framesOut == mFrameCount && mChannelCount == mReqChannelCount) {
readInto = buffer.raw;
framesOut = 0;
} else {
@@ -3735,7 +4457,7 @@ bool AudioFlinger::RecordThread::threadLoop()
mRsmpInIndex = 0;
}
mBytesRead = mInput->stream->read(mInput->stream, readInto,
- mInputBytes);
+ mBufferSize);
if (mBytesRead <= 0) {
if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE))
{
@@ -3760,7 +4482,8 @@ bool AudioFlinger::RecordThread::threadLoop()
} else {
// resampling
- memset(mRsmpOutBuffer, 0, framesOut * 2 * sizeof(int32_t));
+ // resampler accumulates, but we only have one source track
+ memset(mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
// alter output frame count as if we were expecting stereo samples
if (mChannelCount == 1 && mReqChannelCount == 1) {
framesOut >>= 1;
@@ -3770,6 +4493,7 @@ bool AudioFlinger::RecordThread::threadLoop()
// ditherAndClamp() works as long as all buffers returned by
// mActiveTrack->getNextBuffer() are 32 bit aligned which should be always true.
if (mChannelCount == 2 && mReqChannelCount == 1) {
+ // temporarily type pun mRsmpOutBuffer from Q19.12 to int16_t
ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
// the resampler always outputs stereo samples:
// do post stereo to mono conversion
@@ -3778,6 +4502,7 @@ bool AudioFlinger::RecordThread::threadLoop()
} else {
ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut);
}
+ // now done with mRsmpOutBuffer
}
if (mFramestoDrop == 0) {
@@ -3826,6 +4551,10 @@ bool AudioFlinger::RecordThread::threadLoop()
{
Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<RecordTrack> track = mTracks[i];
+ track->invalidate();
+ }
mActiveTrack.clear();
mStartStopCond.broadcast();
}
@@ -3856,7 +4585,7 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
audio_channel_mask_t channelMask,
size_t frameCount,
int sessionId,
- IAudioFlinger::track_flags_t flags,
+ IAudioFlinger::track_flags_t *flags,
pid_t tid,
status_t *status)
{
@@ -3865,9 +4594,59 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
lStatus = initCheck();
if (lStatus != NO_ERROR) {
- ALOGE("Audio driver not initialized.");
+ ALOGE("createRecordTrack_l() audio driver not initialized");
goto Exit;
}
+ // client expresses a preference for FAST, but we get the final say
+ if (*flags & IAudioFlinger::TRACK_FAST) {
+ if (
+ // use case: callback handler and frame count is default or at least as large as HAL
+ (
+ (tid != -1) &&
+ ((frameCount == 0) ||
+ (frameCount >= (mFrameCount * kFastTrackMultiplier)))
+ ) &&
+ // FIXME when record supports non-PCM data, also check for audio_is_linear_pcm(format)
+ // mono or stereo
+ ( (channelMask == AUDIO_CHANNEL_OUT_MONO) ||
+ (channelMask == AUDIO_CHANNEL_OUT_STEREO) ) &&
+ // hardware sample rate
+ (sampleRate == mSampleRate) &&
+ // record thread has an associated fast recorder
+ hasFastRecorder()
+ // FIXME test that RecordThread for this fast track has a capable output HAL
+ // FIXME add a permission test also?
+ ) {
+ // if frameCount not specified, then it defaults to fast recorder (HAL) frame count
+ if (frameCount == 0) {
+ frameCount = mFrameCount * kFastTrackMultiplier;
+ }
+ ALOGV("AUDIO_INPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d",
+ frameCount, mFrameCount);
+ } else {
+ ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%d "
+ "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u "
+ "hasFastRecorder=%d tid=%d",
+ frameCount, mFrameCount, format,
+ audio_is_linear_pcm(format),
+ channelMask, sampleRate, mSampleRate, hasFastRecorder(), tid);
+ *flags &= ~IAudioFlinger::TRACK_FAST;
+ // For compatibility with AudioRecord calculation, buffer depth is forced
+ // to be at least 2 x the record thread frame count and cover audio hardware latency.
+ // This is probably too conservative, but legacy application code may depend on it.
+ // If you change this calculation, also review the start threshold which is related.
+ uint32_t latencyMs = 50; // FIXME mInput->stream->get_latency(mInput->stream);
+ size_t mNormalFrameCount = 2048; // FIXME
+ uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
+ if (minBufCount < 2) {
+ minBufCount = 2;
+ }
+ size_t minFrameCount = mNormalFrameCount * minBufCount;
+ if (frameCount < minFrameCount) {
+ frameCount = minFrameCount;
+ }
+ }
+ }
// FIXME use flags and tid similar to createTrack_l()
@@ -3878,7 +4657,9 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
format, channelMask, frameCount, sessionId);
if (track->getCblk() == 0) {
+ ALOGE("createRecordTrack_l() no control block");
lStatus = NO_MEMORY;
+ track.clear();
goto Exit;
}
mTracks.add(track);
@@ -3888,6 +4669,13 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
mAudioFlinger->btNrecIsOff();
setEffectSuspended_l(FX_IID_AEC, suspend, sessionId);
setEffectSuspended_l(FX_IID_NS, suspend, sessionId);
+
+ if ((*flags & IAudioFlinger::TRACK_FAST) && (tid != -1)) {
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ // we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
+ // so ask activity manager to do this on our behalf
+ sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp);
+ }
}
lStatus = NO_ERROR;
@@ -3969,6 +4757,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac
ALOGV("Record started OK");
return status;
}
+
startError:
AudioSystem::stopInput(mId);
clearSyncStartEvent();
@@ -4003,8 +4792,9 @@ void AudioFlinger::RecordThread::handleSyncStartEvent(const sp<SyncEvent>& event
}
}
-bool AudioFlinger::RecordThread::stop_l(RecordThread::RecordTrack* recordTrack) {
+bool AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) {
ALOGV("RecordThread::stop");
+ AutoMutex _l(mLock);
if (recordTrack != mActiveTrack.get() || recordTrack->mState == TrackBase::PAUSING) {
return false;
}
@@ -4055,7 +4845,8 @@ status_t AudioFlinger::RecordThread::setSyncEvent(const sp<SyncEvent>& event)
// destroyTrack_l() must be called with ThreadBase::mLock held
void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track)
{
- track->mState = TrackBase::TERMINATED;
+ track->terminate();
+ track->mState = TrackBase::STOPPED;
// active tracks are removed by threadLoop()
if (mActiveTrack != track) {
removeTrack_l(track);
@@ -4087,7 +4878,7 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& a
if (mActiveTrack != 0) {
snprintf(buffer, SIZE, "In index: %d\n", mRsmpInIndex);
result.append(buffer);
- snprintf(buffer, SIZE, "In size: %d\n", mInputBytes);
+ snprintf(buffer, SIZE, "Buffer size: %u bytes\n", mBufferSize);
result.append(buffer);
snprintf(buffer, SIZE, "Resampling: %d\n", (mResampler != NULL));
result.append(buffer);
@@ -4140,7 +4931,7 @@ status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer*
int channelCount;
if (framesReady == 0) {
- mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mInputBytes);
+ mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mBufferSize);
if (mBytesRead <= 0) {
if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE)) {
ALOGE("RecordThread::getNextBuffer() Error reading audio input");
@@ -4196,8 +4987,12 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
- reqFormat = (audio_format_t) value;
- reconfig = true;
+ if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
+ status = BAD_VALUE;
+ } else {
+ reqFormat = (audio_format_t) value;
+ reconfig = true;
+ }
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
reqChannelCount = popcount(value);
@@ -4289,16 +5084,13 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
String8 AudioFlinger::RecordThread::getParameters(const String8& keys)
{
- char *s;
- String8 out_s8 = String8();
-
Mutex::Autolock _l(mLock);
if (initCheck() != NO_ERROR) {
- return out_s8;
+ return String8();
}
- s = mInput->stream->common.get_parameters(&mInput->stream->common, keys.string());
- out_s8 = String8(s);
+ char *s = mInput->stream->common.get_parameters(&mInput->stream->common, keys.string());
+ const String8 out_s8(s);
free(s);
return out_s8;
}
@@ -4310,7 +5102,7 @@ void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
switch (event) {
case AudioSystem::INPUT_OPENED:
case AudioSystem::INPUT_CONFIG_CHANGED:
- desc.channels = mChannelMask;
+ desc.channelMask = mChannelMask;
desc.samplingRate = mSampleRate;
desc.format = mFormat;
desc.frameCount = mFrameCount;
@@ -4336,12 +5128,14 @@ void AudioFlinger::RecordThread::readInputParameters()
mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
- mChannelCount = (uint16_t)popcount(mChannelMask);
+ mChannelCount = popcount(mChannelMask);
mFormat = mInput->stream->common.get_format(&mInput->stream->common);
+ if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
+ ALOGE("HAL format %d not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
+ }
mFrameSize = audio_stream_frame_size(&mInput->stream->common);
- mInputBytes = mInput->stream->common.get_buffer_size(&mInput->stream->common);
- mFrameCount = mInputBytes / mFrameSize;
- mNormalFrameCount = mFrameCount; // not used by record, but used by input effects
+ mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common);
+ mFrameCount = mBufferSize / mFrameSize;
mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
if (mSampleRate != mReqSampleRate && mChannelCount <= FCC_2 && mReqChannelCount <= FCC_2)
@@ -4357,7 +5151,7 @@ void AudioFlinger::RecordThread::readInputParameters()
mResampler = AudioResampler::create(16, channelCount, mReqSampleRate);
mResampler->setSampleRate(mSampleRate);
mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN);
- mRsmpOutBuffer = new int32_t[mFrameCount * 2];
+ mRsmpOutBuffer = new int32_t[mFrameCount * FCC_2];
// optmization: if mono to mono, alter input frame count as if we were inputing
// stereo samples
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 7de6872..802b784 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -28,7 +28,8 @@ public:
MIXER, // Thread class is MixerThread
DIRECT, // Thread class is DirectOutputThread
DUPLICATING, // Thread class is DuplicatingThread
- RECORD // Thread class is RecordThread
+ RECORD, // Thread class is RecordThread
+ OFFLOAD // Thread class is OffloadThread
};
ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
@@ -125,10 +126,9 @@ public:
audio_channel_mask_t channelMask() const { return mChannelMask; }
audio_format_t format() const { return mFormat; }
// Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects,
- // and returns the normal mix buffer's frame count.
- size_t frameCount() const { return mNormalFrameCount; }
- // Return's the HAL's frame count i.e. fast mixer buffer size.
- size_t frameCountHAL() const { return mFrameCount; }
+ // and returns the [normal mix] buffer's frame count.
+ virtual size_t frameCount() const = 0;
+ size_t frameSize() const { return mFrameSize; }
// Should be "virtual status_t requestExitAndWait()" and override same
// method in Thread, but Thread::requestExitAndWait() is not yet virtual.
@@ -184,6 +184,8 @@ public:
void lockEffectChains_l(Vector< sp<EffectChain> >& effectChains);
// unlock effect chains after process
void unlockEffectChains(const Vector< sp<EffectChain> >& effectChains);
+ // get a copy of mEffectChains vector
+ Vector< sp<EffectChain> > getEffectChains_l() const { return mEffectChains; };
// set audio mode to all effect chains
void setMode(audio_mode_t mode);
// get effect module with corresponding ID on specified audio session
@@ -235,8 +237,8 @@ protected:
effect_uuid_t mType; // effect type UUID
};
- void acquireWakeLock();
- void acquireWakeLock_l();
+ void acquireWakeLock(int uid = -1);
+ void acquireWakeLock_l(int uid = -1);
void releaseWakeLock();
void releaseWakeLock_l();
void setEffectSuspended_l(const effect_uuid_t *type,
@@ -249,6 +251,8 @@ protected:
// check if some effects must be suspended when an effect chain is added
void checkSuspendOnAddEffectChain_l(const sp<EffectChain>& chain);
+ String16 getWakeLockTag();
+
virtual void preExit() { }
friend class AudioFlinger; // for mEffectChains
@@ -259,11 +263,13 @@ protected:
Condition mWaitWorkCV;
const sp<AudioFlinger> mAudioFlinger;
+
+ // updated by PlaybackThread::readOutputParameters() or
+ // RecordThread::readInputParameters()
uint32_t mSampleRate;
size_t mFrameCount; // output HAL, direct output, record
- size_t mNormalFrameCount; // normal mixer and effects
audio_channel_mask_t mChannelMask;
- uint16_t mChannelCount;
+ uint32_t mChannelCount;
size_t mFrameSize;
audio_format_t mFormat;
@@ -290,6 +296,7 @@ protected:
Vector<String8> mNewParameters;
status_t mParamStatus;
+ // vector owns each ConfigEvent *, so must delete after removing
Vector<ConfigEvent *> mConfigEvents;
// These fields are written and read by thread itself without lock or barrier,
@@ -328,11 +335,19 @@ public:
enum mixer_state {
MIXER_IDLE, // no active tracks
MIXER_TRACKS_ENABLED, // at least one active track, but no track has any data ready
- MIXER_TRACKS_READY // at least one active track, and at least one track has data
+ MIXER_TRACKS_READY, // at least one active track, and at least one track has data
+ MIXER_DRAIN_TRACK, // drain currently playing track
+ MIXER_DRAIN_ALL, // fully drain the hardware
// standby mode does not have an enum value
// suspend by audio policy manager is orthogonal to mixer state
};
+ // retry count before removing active track in case of underrun on offloaded thread:
+ // we need to make sure that AudioTrack client has enough time to send large buffers
+//FIXME may be more appropriate if expressed in time units. Need to revise how underrun is handled
+ // for offloaded tracks
+ static const int8_t kMaxTrackRetriesOffload = 20;
+
PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
audio_io_handle_t id, audio_devices_t device, type_t type);
virtual ~PlaybackThread();
@@ -350,8 +365,10 @@ protected:
// Code snippets that were lifted up out of threadLoop()
virtual void threadLoop_mix() = 0;
virtual void threadLoop_sleepTime() = 0;
- virtual void threadLoop_write();
+ virtual ssize_t threadLoop_write();
+ virtual void threadLoop_drain();
virtual void threadLoop_standby();
+ virtual void threadLoop_exit();
virtual void threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove);
// prepareTracks_l reads and writes mActiveTracks, and returns
@@ -359,6 +376,19 @@ protected:
// is responsible for clearing or destroying this Vector later on, when it
// is safe to do so. That will drop the final ref count and destroy the tracks.
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove) = 0;
+ void removeTracks_l(const Vector< sp<Track> >& tracksToRemove);
+
+ void writeCallback();
+ void resetWriteBlocked(uint32_t sequence);
+ void drainCallback();
+ void resetDraining(uint32_t sequence);
+
+ static int asyncCallback(stream_callback_event_t event, void *param, void *cookie);
+
+ virtual bool waitingAsyncCallback();
+ virtual bool waitingAsyncCallback_l();
+ virtual bool shouldStandby_l();
+
// ThreadBase virtuals
virtual void preExit();
@@ -429,11 +459,23 @@ public:
virtual status_t setSyncEvent(const sp<SyncEvent>& event);
virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const;
+
+ // called with AudioFlinger lock held
void invalidateTracks(audio_stream_type_t streamType);
+ virtual size_t frameCount() const { return mNormalFrameCount; }
+
+ // 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);
protected:
- int16_t* mMixBuffer;
+ // updated by readOutputParameters()
+ size_t mNormalFrameCount; // normal mixer and effects
+
+ int16_t* mMixBuffer; // frame size aligned mix buffer
+ int8_t* mAllocMixBuffer; // mixer buffer allocation address
// suspend count, > 0 means suspended. While suspended, the thread continues to pull from
// tracks and mix, but doesn't write to HAL. A2DP and SCO HAL implementations can't handle
@@ -486,8 +528,9 @@ private:
PlaybackThread& operator = (const PlaybackThread&);
status_t addTrack_l(const sp<Track>& track);
- void destroyTrack_l(const sp<Track>& track);
+ bool destroyTrack_l(const sp<Track>& track);
void removeTrack_l(const sp<Track>& track);
+ void broadcast_l();
void readOutputParameters();
@@ -535,6 +578,27 @@ private:
// DUPLICATING only
uint32_t writeFrames;
+ size_t mBytesRemaining;
+ size_t mCurrentWriteLength;
+ bool mUseAsyncWrite;
+ // mWriteAckSequence contains current write sequence on bits 31-1. The write sequence is
+ // incremented each time a write(), a flush() or a standby() occurs.
+ // Bit 0 is set when a write blocks and indicates a callback is expected.
+ // Bit 0 is reset by the async callback thread calling resetWriteBlocked(). Out of sequence
+ // callbacks are ignored.
+ uint32_t mWriteAckSequence;
+ // mDrainSequence contains current drain sequence on bits 31-1. The drain sequence is
+ // incremented each time a drain is requested or a flush() or standby() occurs.
+ // Bit 0 is set when the drain() command is called at the HAL and indicates a callback is
+ // expected.
+ // Bit 0 is reset by the async callback thread calling resetDraining(). Out of sequence
+ // callbacks are ignored.
+ uint32_t mDrainSequence;
+ // A condition that must be evaluated by prepareTrack_l() has changed and we must not wait
+ // for async write callback in the thread loop before evaluating it
+ bool mSignalPending;
+ sp<AsyncCallbackThread> mCallbackThread;
+
private:
// The HAL output sink is treated as non-blocking, but current implementation is blocking
sp<NBAIO_Sink> mOutputSink;
@@ -558,7 +622,18 @@ public:
protected:
// accessed by both binder threads and within threadLoop(), lock on mutex needed
unsigned mFastTrackAvailMask; // bit i set if fast track [i] is available
+ virtual void flushOutput_l();
+private:
+ // timestamp latch:
+ // D input is written by threadLoop_write while mutex is unlocked, and read while locked
+ // Q output is written while locked, and read while locked
+ struct {
+ AudioTimestamp mTimestamp;
+ uint32_t mUnpresentedFrames;
+ } mLatchD, mLatchQ;
+ bool mLatchDValid; // true means mLatchD is valid, and clock it into latch at next opportunity
+ bool mLatchQValid; // true means mLatchQ is valid
};
class MixerThread : public PlaybackThread {
@@ -584,7 +659,7 @@ protected:
virtual void cacheParameters_l();
// threadLoop snippets
- virtual void threadLoop_write();
+ virtual ssize_t threadLoop_write();
virtual void threadLoop_standby();
virtual void threadLoop_mix();
virtual void threadLoop_sleepTime();
@@ -641,17 +716,81 @@ protected:
virtual void threadLoop_mix();
virtual void threadLoop_sleepTime();
-private:
// volumes last sent to audio HAL with stream->set_volume()
float mLeftVolFloat;
float mRightVolFloat;
+ DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+ audio_io_handle_t id, uint32_t device, ThreadBase::type_t type);
+ void processVolume_l(Track *track, bool lastTrack);
+
// prepareTracks_l() tells threadLoop_mix() the name of the single active track
sp<Track> mActiveTrack;
public:
virtual bool hasFastMixer() const { return false; }
};
+class OffloadThread : public DirectOutputThread {
+public:
+
+ OffloadThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+ audio_io_handle_t id, uint32_t device);
+ virtual ~OffloadThread();
+
+protected:
+ // threadLoop snippets
+ virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
+ virtual void threadLoop_exit();
+ virtual void flushOutput_l();
+
+ virtual bool waitingAsyncCallback();
+ virtual bool waitingAsyncCallback_l();
+ virtual bool shouldStandby_l();
+
+private:
+ void flushHw_l();
+
+private:
+ bool mHwPaused;
+ bool mFlushPending;
+ size_t mPausedWriteLength; // length in bytes of write interrupted by pause
+ size_t mPausedBytesRemaining; // bytes still waiting in mixbuffer after resume
+ sp<Track> mPreviousTrack; // used to detect track switch
+};
+
+class AsyncCallbackThread : public Thread {
+public:
+
+ AsyncCallbackThread(const wp<PlaybackThread>& playbackThread);
+
+ virtual ~AsyncCallbackThread();
+
+ // Thread virtuals
+ virtual bool threadLoop();
+
+ // RefBase
+ virtual void onFirstRef();
+
+ void exit();
+ void setWriteBlocked(uint32_t sequence);
+ void resetWriteBlocked();
+ void setDraining(uint32_t sequence);
+ void resetDraining();
+
+private:
+ const wp<PlaybackThread> mPlaybackThread;
+ // mWriteAckSequence corresponds to the last write sequence passed by the offload thread via
+ // setWriteBlocked(). The sequence is shifted one bit to the left and the lsb is used
+ // to indicate that the callback has been received via resetWriteBlocked()
+ uint32_t mWriteAckSequence;
+ // mDrainSequence corresponds to the last drain sequence passed by the offload thread via
+ // setDraining(). The sequence is shifted one bit to the left and the lsb is used
+ // to indicate that the callback has been received via resetDraining()
+ uint32_t mDrainSequence;
+ Condition mWaitWorkCV;
+ Mutex mLock;
+};
+
class DuplicatingThread : public MixerThread {
public:
DuplicatingThread(const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread,
@@ -671,7 +810,7 @@ protected:
// threadLoop snippets
virtual void threadLoop_mix();
virtual void threadLoop_sleepTime();
- virtual void threadLoop_write();
+ virtual ssize_t threadLoop_write();
virtual void threadLoop_standby();
virtual void cacheParameters_l();
@@ -734,7 +873,7 @@ public:
audio_channel_mask_t channelMask,
size_t frameCount,
int sessionId,
- IAudioFlinger::track_flags_t flags,
+ IAudioFlinger::track_flags_t *flags,
pid_t tid,
status_t *status);
@@ -744,7 +883,7 @@ public:
// ask the thread to stop the specified track, and
// return true if the caller should then do it's part of the stopping process
- bool stop_l(RecordTrack* recordTrack);
+ bool stop(RecordTrack* recordTrack);
void dump(int fd, const Vector<String16>& args);
AudioStreamIn* clearInput();
@@ -775,6 +914,9 @@ public:
static void syncStartEventCallback(const wp<SyncEvent>& event);
void handleSyncStartEvent(const sp<SyncEvent>& event);
+ virtual size_t frameCount() const { return mFrameCount; }
+ bool hasFastRecorder() const { return false; }
+
private:
void clearSyncStartEvent();
@@ -790,11 +932,14 @@ private:
// is used together with mStartStopCond to indicate start()/stop() progress
sp<RecordTrack> mActiveTrack;
Condition mStartStopCond;
+
+ // updated by RecordThread::readInputParameters()
AudioResampler *mResampler;
+ // interleaved stereo pairs of fixed-point signed Q19.12
int32_t *mRsmpOutBuffer;
- int16_t *mRsmpInBuffer;
+ int16_t *mRsmpInBuffer; // [mFrameCount * mChannelCount]
size_t mRsmpInIndex;
- size_t mInputBytes;
+ size_t mBufferSize; // stream buffer size for read()
const uint32_t mReqChannelCount;
const uint32_t mReqSampleRate;
ssize_t mBytesRead;
@@ -808,4 +953,5 @@ private:
// For dumpsys
const sp<NBAIO_Sink> mTeeSink;
+ int mClientUid;
};
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index fac7071..523e4b2 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -25,10 +25,10 @@ class TrackBase : public ExtendedAudioBufferProvider, public RefBase {
public:
enum track_state {
IDLE,
- TERMINATED,
FLUSHED,
STOPPED,
- // next 2 states are currently used for fast tracks only
+ // next 2 states are currently used for fast tracks
+ // and offloaded tracks only
STOPPING_1, // waiting for first underrun
STOPPING_2, // waiting for presentation complete
RESUMING,
@@ -74,7 +74,7 @@ protected:
audio_channel_mask_t channelMask() const { return mChannelMask; }
- uint32_t sampleRate() const; // FIXME inline after cblk sr moved
+ virtual uint32_t sampleRate() const { return mSampleRate; }
// Return a pointer to the start of a contiguous slice of the track buffer.
// Parameter 'offset' is the requested start position, expressed in
@@ -89,7 +89,7 @@ protected:
return (mState == STOPPED || mState == FLUSHED);
}
- // for fast tracks only
+ // for fast tracks and offloaded tracks only
bool isStopping() const {
return mState == STOPPING_1 || mState == STOPPING_2;
}
@@ -101,11 +101,12 @@ protected:
}
bool isTerminated() const {
- return mState == TERMINATED;
+ return mTerminated;
}
- bool step(); // mStepCount is an implicit input
- void reset();
+ void terminate() {
+ mTerminated = true;
+ }
bool isOut() const { return mIsOut; }
// true for Track and TimedTrack, false for RecordTrack,
@@ -117,24 +118,19 @@ protected:
audio_track_cblk_t* mCblk;
void* mBuffer; // start of track buffer, typically in shared memory
// except for OutputTrack when it is in local memory
- void* mBufferEnd; // &mBuffer[mFrameCount * frameSize], where frameSize
- // is based on mChannelCount and 16-bit samples
- uint32_t mStepCount; // saves AudioBufferProvider::Buffer::frameCount as of
- // time of releaseBuffer() for later use by step()
// we don't really need a lock for these
track_state mState;
const uint32_t mSampleRate; // initial sample rate only; for tracks which
// support dynamic rates, the current value is in control block
const audio_format_t mFormat;
const audio_channel_mask_t mChannelMask;
- const uint8_t mChannelCount;
+ const uint32_t mChannelCount;
const size_t mFrameSize; // AudioFlinger's view of frame size in shared memory,
// where for AudioTrack (but not AudioRecord),
// 8-bit PCM samples are stored as 16-bit
const size_t mFrameCount;// size of track buffer given at createTrack() or
// openRecord(), and then adjusted as needed
- bool mStepServerFailed;
const int mSessionId;
Vector < sp<SyncEvent> >mSyncEvents;
const bool mIsOut;
@@ -142,4 +138,5 @@ protected:
const int mId;
sp<NBAIO_Sink> mTeeSink;
sp<NBAIO_Source> mTeeSource;
+ bool mTerminated;
};
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 5ac3129..9c6e724 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -19,8 +19,8 @@
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <math.h>
-#include <cutils/compiler.h>
#include <utils/Log.h>
#include <private/media/AudioTrackShared.h>
@@ -74,8 +74,6 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
mClient(client),
mCblk(NULL),
// mBuffer
- // mBufferEnd
- mStepCount(0),
mState(IDLE),
mSampleRate(sampleRate),
mFormat(format),
@@ -84,11 +82,11 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
mFrameSize(audio_is_linear_pcm(format) ?
mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)),
mFrameCount(frameCount),
- mStepServerFailed(false),
mSessionId(sessionId),
mIsOut(isOut),
mServerProxy(NULL),
- mId(android_atomic_inc(&nextTrackId))
+ mId(android_atomic_inc(&nextTrackId)),
+ mTerminated(false)
{
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -98,7 +96,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
// ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
size_t size = sizeof(audio_track_cblk_t);
- size_t bufferSize = frameCount * mFrameSize;
+ size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
if (sharedBuffer == 0) {
size += bufferSize;
}
@@ -124,22 +122,15 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
new(mCblk) audio_track_cblk_t();
// clear all buffers
mCblk->frameCount_ = frameCount;
-// uncomment the following lines to quickly test 32-bit wraparound
-// mCblk->user = 0xffff0000;
-// mCblk->server = 0xffff0000;
-// mCblk->userBase = 0xffff0000;
-// mCblk->serverBase = 0xffff0000;
if (sharedBuffer == 0) {
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
memset(mBuffer, 0, bufferSize);
- // Force underrun condition to avoid false underrun callback until first data is
- // written to buffer (other flags are cleared)
- mCblk->flags = CBLK_UNDERRUN;
} else {
mBuffer = sharedBuffer->pointer();
+#if 0
+ mCblk->mFlags = CBLK_FORCEREADY; // FIXME hack, need to fix the track ready logic
+#endif
}
- mBufferEnd = (uint8_t *)mBuffer + bufferSize;
- mServerProxy = new ServerProxy(mCblk, mBuffer, frameCount, mFrameSize, isOut);
#ifdef TEE_SINK
if (mTeeSinkTrackEnabled) {
@@ -199,51 +190,12 @@ void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buf
}
#endif
- buffer->raw = NULL;
- mStepCount = buffer->frameCount;
- // FIXME See note at getNextBuffer()
- (void) step(); // ignore return value of step()
+ ServerProxy::Buffer buf;
+ buf.mFrameCount = buffer->frameCount;
+ buf.mRaw = buffer->raw;
buffer->frameCount = 0;
-}
-
-bool AudioFlinger::ThreadBase::TrackBase::step() {
- bool result = mServerProxy->step(mStepCount);
- if (!result) {
- ALOGV("stepServer failed acquiring cblk mutex");
- mStepServerFailed = true;
- }
- return result;
-}
-
-void AudioFlinger::ThreadBase::TrackBase::reset() {
- audio_track_cblk_t* cblk = this->cblk();
-
- cblk->user = 0;
- cblk->server = 0;
- cblk->userBase = 0;
- cblk->serverBase = 0;
- mStepServerFailed = false;
- ALOGV("TrackBase::reset");
-}
-
-uint32_t AudioFlinger::ThreadBase::TrackBase::sampleRate() const {
- return mServerProxy->getSampleRate();
-}
-
-void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
- audio_track_cblk_t* cblk = this->cblk();
- int8_t *bufferStart = (int8_t *)mBuffer + (offset-cblk->serverBase) * mFrameSize;
- int8_t *bufferEnd = bufferStart + frames * mFrameSize;
-
- // Check validity of returned pointer in case the track control block would have been corrupted.
- ALOG_ASSERT(!(bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd),
- "TrackBase::getBuffer buffer out of range:\n"
- " start: %p, end %p , mBuffer %p mBufferEnd %p\n"
- " server %u, serverBase %u, user %u, userBase %u, frameSize %u",
- bufferStart, bufferEnd, mBuffer, mBufferEnd,
- cblk->server, cblk->serverBase, cblk->user, cblk->userBase, mFrameSize);
-
- return bufferStart;
+ buffer->raw = NULL;
+ mServerProxy->releaseBuffer(&buf);
}
status_t AudioFlinger::ThreadBase::TrackBase::setSyncEvent(const sp<SyncEvent>& event)
@@ -327,6 +279,21 @@ status_t AudioFlinger::TrackHandle::setMediaTimeTransform(
xform, static_cast<TimedAudioTrack::TargetTimeline>(target));
}
+status_t AudioFlinger::TrackHandle::setParameters(const String8& keyValuePairs) {
+ return mTrack->setParameters(keyValuePairs);
+}
+
+status_t AudioFlinger::TrackHandle::getTimestamp(AudioTimestamp& timestamp)
+{
+ return mTrack->getTimestamp(timestamp);
+}
+
+
+void AudioFlinger::TrackHandle::signal()
+{
+ return mTrack->signal();
+}
+
status_t AudioFlinger::TrackHandle::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -360,20 +327,29 @@ AudioFlinger::PlaybackThread::Track::Track(
mPresentationCompleteFrames(0),
mFlags(flags),
mFastIndex(-1),
- mUnderrunCount(0),
mCachedVolume(1.0),
- mIsInvalid(false)
+ mIsInvalid(false),
+ mAudioTrackServerProxy(NULL),
+ mResumeToStopping(false)
{
if (mCblk != NULL) {
+ if (sharedBuffer == 0) {
+ mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
+ mFrameSize);
+ } else {
+ mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,
+ mFrameSize);
+ }
+ mServerProxy = mAudioTrackServerProxy;
// to avoid leaking a track name, do not allocate one unless there is an mCblk
mName = thread->getTrackName_l(channelMask, sessionId);
- mCblk->mName = mName;
if (mName < 0) {
ALOGE("no more track names available");
return;
}
// only allocate a fast track index if we were able to allocate a normal track name
if (flags & IAudioFlinger::TRACK_FAST) {
+ mAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads();
ALOG_ASSERT(thread->mFastTrackAvailMask != 0);
int i = __builtin_ctz(thread->mFastTrackAvailMask);
ALOG_ASSERT(0 < i && i < (int)FastMixerState::kMaxFastTracks);
@@ -382,7 +358,6 @@ AudioFlinger::PlaybackThread::Track::Track(
// this means we are potentially denying other more important fast tracks from
// being created. It would be better to allocate the index dynamically.
mFastIndex = i;
- mCblk->mName = i;
// Read the initial underruns because this field is never cleared by the fast mixer
mObservedUnderruns = thread->getFastTrackUnderruns(i);
thread->mFastTrackAvailMask &= ~(1 << i);
@@ -395,6 +370,16 @@ AudioFlinger::PlaybackThread::Track::Track(
AudioFlinger::PlaybackThread::Track::~Track()
{
ALOGV("PlaybackThread::Track destructor");
+
+ // The destructor would clear mSharedBuffer,
+ // but it will not push the decremented reference count,
+ // leaving the client's IMemory dangling indefinitely.
+ // This prevents that leak.
+ if (mSharedBuffer != 0) {
+ mSharedBuffer.clear();
+ // flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+ }
}
void AudioFlinger::PlaybackThread::Track::destroy()
@@ -411,33 +396,25 @@ void AudioFlinger::PlaybackThread::Track::destroy()
{ // scope for mLock
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
- if (!isOutputTrack()) {
- if (mState == ACTIVE || mState == RESUMING) {
- AudioSystem::stopOutput(thread->id(), mStreamType, mSessionId);
-
-#ifdef ADD_BATTERY_DATA
- // to track the speaker usage
- addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
-#endif
- }
- AudioSystem::releaseOutput(thread->id());
- }
Mutex::Autolock _l(thread->mLock);
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- playbackThread->destroyTrack_l(this);
+ bool wasActive = playbackThread->destroyTrack_l(this);
+ if (!isOutputTrack() && !wasActive) {
+ AudioSystem::releaseOutput(thread->id());
+ }
}
}
}
/*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
{
- result.append(" Name Client Type Fmt Chn mask Session StpCnt fCount S F SRate "
- "L dB R dB Server User Main buf Aux Buf Flags Underruns\n");
+ result.append(" Name Client Type Fmt Chn mask Session fCount S F SRate "
+ "L dB R dB Server Main buf Aux Buf Flags UndFrmCnt\n");
}
void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
{
- uint32_t vlr = mServerProxy->getVolumeLR();
+ uint32_t vlr = mAudioTrackServerProxy->getVolumeLR();
if (isFastTrack()) {
sprintf(buffer, " F %2d", mFastIndex);
} else {
@@ -445,40 +422,41 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
}
track_state state = mState;
char stateChar;
- switch (state) {
- case IDLE:
- stateChar = 'I';
- break;
- case TERMINATED:
+ if (isTerminated()) {
stateChar = 'T';
- break;
- case STOPPING_1:
- stateChar = 's';
- break;
- case STOPPING_2:
- stateChar = '5';
- break;
- case STOPPED:
- stateChar = 'S';
- break;
- case RESUMING:
- stateChar = 'R';
- break;
- case ACTIVE:
- stateChar = 'A';
- break;
- case PAUSING:
- stateChar = 'p';
- break;
- case PAUSED:
- stateChar = 'P';
- break;
- case FLUSHED:
- stateChar = 'F';
- break;
- default:
- stateChar = '?';
- break;
+ } else {
+ switch (state) {
+ case IDLE:
+ stateChar = 'I';
+ break;
+ case STOPPING_1:
+ stateChar = 's';
+ break;
+ case STOPPING_2:
+ stateChar = '5';
+ break;
+ case STOPPED:
+ stateChar = 'S';
+ break;
+ case RESUMING:
+ stateChar = 'R';
+ break;
+ case ACTIVE:
+ stateChar = 'A';
+ break;
+ case PAUSING:
+ stateChar = 'p';
+ break;
+ case PAUSED:
+ stateChar = 'P';
+ break;
+ case FLUSHED:
+ stateChar = 'F';
+ break;
+ default:
+ stateChar = '?';
+ break;
+ }
}
char nowInUnderrun;
switch (mObservedUnderruns.mBitFields.mMostRecent) {
@@ -495,77 +473,50 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
nowInUnderrun = '?';
break;
}
- snprintf(&buffer[7], size-7, " %6d %4u %3u 0x%08x %7u %6u %6u %1c %1d %5u %5.2g %5.2g "
- "0x%08x 0x%08x 0x%08x 0x%08x %#5x %9u%c\n",
+ snprintf(&buffer[7], size-7, " %6u %4u %08X %08X %7u %6u %1c %1d %5u %5.2g %5.2g "
+ "%08X %08X %08X 0x%03X %9u%c\n",
(mClient == 0) ? getpid_cached : mClient->pid(),
mStreamType,
mFormat,
mChannelMask,
mSessionId,
- mStepCount,
mFrameCount,
stateChar,
mFillingUpStatus,
- mServerProxy->getSampleRate(),
+ mAudioTrackServerProxy->getSampleRate(),
20.0 * log10((vlr & 0xFFFF) / 4096.0),
20.0 * log10((vlr >> 16) / 4096.0),
- mCblk->server,
- mCblk->user,
+ mCblk->mServer,
(int)mMainBuffer,
(int)mAuxBuffer,
- mCblk->flags,
- mUnderrunCount,
+ mCblk->mFlags,
+ mAudioTrackServerProxy->getUnderrunFrames(),
nowInUnderrun);
}
+uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const {
+ return mAudioTrackServerProxy->getSampleRate();
+}
+
// AudioBufferProvider interface
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
AudioBufferProvider::Buffer* buffer, int64_t pts)
{
- audio_track_cblk_t* cblk = this->cblk();
- uint32_t framesReady;
- uint32_t framesReq = buffer->frameCount;
-
- // Check if last stepServer failed, try to step now
- if (mStepServerFailed) {
- // FIXME When called by fast mixer, this takes a mutex with tryLock().
- // Since the fast mixer is higher priority than client callback thread,
- // it does not result in priority inversion for client.
- // But a non-blocking solution would be preferable to avoid
- // fast mixer being unable to tryLock(), and
- // to avoid the extra context switches if the client wakes up,
- // discovers the mutex is locked, then has to wait for fast mixer to unlock.
- if (!step()) goto getNextBuffer_exit;
- ALOGV("stepServer recovered");
- mStepServerFailed = false;
+ ServerProxy::Buffer buf;
+ size_t desiredFrames = buffer->frameCount;
+ buf.mFrameCount = desiredFrames;
+ status_t status = mServerProxy->obtainBuffer(&buf);
+ buffer->frameCount = buf.mFrameCount;
+ buffer->raw = buf.mRaw;
+ if (buf.mFrameCount == 0) {
+ mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
}
+ return status;
+}
- // FIXME Same as above
- framesReady = mServerProxy->framesReady();
-
- if (CC_LIKELY(framesReady)) {
- uint32_t s = cblk->server;
- uint32_t bufferEnd = cblk->serverBase + mFrameCount;
-
- bufferEnd = (cblk->loopEnd < bufferEnd) ? cblk->loopEnd : bufferEnd;
- if (framesReq > framesReady) {
- framesReq = framesReady;
- }
- if (framesReq > bufferEnd - s) {
- framesReq = bufferEnd - s;
- }
+// releaseBuffer() is not overridden
- buffer->raw = getBuffer(s, framesReq);
- buffer->frameCount = framesReq;
- return NO_ERROR;
- }
-
-getNextBuffer_exit:
- buffer->raw = NULL;
- buffer->frameCount = 0;
- ALOGV("getNextBuffer() no more data for track %d on thread %p", mName, mThread.unsafe_get());
- return NOT_ENOUGH_DATA;
-}
+// ExtendedAudioBufferProvider interface
// Note that framesReady() takes a mutex on the control block using tryLock().
// This could result in priority inversion if framesReady() is called by the normal mixer,
@@ -576,7 +527,12 @@ getNextBuffer_exit:
// the tryLock() could block for up to 1 ms, and a sequence of these could delay fast mixer.
// FIXME Replace AudioTrackShared control block implementation by a non-blocking FIFO queue.
size_t AudioFlinger::PlaybackThread::Track::framesReady() const {
- return mServerProxy->framesReady();
+ return mAudioTrackServerProxy->framesReady();
+}
+
+size_t AudioFlinger::PlaybackThread::Track::framesReleased() const
+{
+ return mAudioTrackServerProxy->framesReleased();
}
// Don't call for fast tracks; the framesReady() could result in priority inversion
@@ -586,9 +542,9 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const {
}
if (framesReady() >= mFrameCount ||
- (mCblk->flags & CBLK_FORCEREADY)) {
+ (mCblk->mFlags & CBLK_FORCEREADY)) {
mFillingUpStatus = FS_FILLED;
- android_atomic_and(~CBLK_FORCEREADY, &mCblk->flags);
+ android_atomic_and(~CBLK_FORCEREADY, &mCblk->mFlags);
return true;
}
return false;
@@ -603,36 +559,47 @@ status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t ev
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
- Mutex::Autolock _l(thread->mLock);
+ if (isOffloaded()) {
+ Mutex::Autolock _laf(thread->mAudioFlinger->mLock);
+ Mutex::Autolock _lth(thread->mLock);
+ sp<EffectChain> ec = thread->getEffectChain_l(mSessionId);
+ if (thread->mAudioFlinger->isNonOffloadableGlobalEffectEnabled_l() ||
+ (ec != 0 && ec->isNonOffloadableEnabled())) {
+ invalidate();
+ return PERMISSION_DENIED;
+ }
+ }
+ Mutex::Autolock _lth(thread->mLock);
track_state state = mState;
// here the track could be either new, or restarted
// in both cases "unstop" the track
+
if (state == PAUSED) {
- mState = TrackBase::RESUMING;
- ALOGV("PAUSED => RESUMING (%d) on thread %p", mName, this);
+ if (mResumeToStopping) {
+ // happened we need to resume to STOPPING_1
+ mState = TrackBase::STOPPING_1;
+ ALOGV("PAUSED => STOPPING_1 (%d) on thread %p", mName, this);
+ } else {
+ mState = TrackBase::RESUMING;
+ ALOGV("PAUSED => RESUMING (%d) on thread %p", mName, this);
+ }
} else {
mState = TrackBase::ACTIVE;
ALOGV("? => ACTIVE (%d) on thread %p", mName, this);
}
- if (!isOutputTrack() && state != ACTIVE && state != RESUMING) {
- thread->mLock.unlock();
- status = AudioSystem::startOutput(thread->id(), mStreamType, mSessionId);
- thread->mLock.lock();
-
-#ifdef ADD_BATTERY_DATA
- // to track the speaker usage
- if (status == NO_ERROR) {
- addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStart);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ status = playbackThread->addTrack_l(this);
+ if (status == INVALID_OPERATION || status == PERMISSION_DENIED) {
+ triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
+ // restore previous state if start was rejected by policy manager
+ if (status == PERMISSION_DENIED) {
+ mState = state;
}
-#endif
}
- if (status == NO_ERROR) {
- PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- playbackThread->addTrack_l(this);
- } else {
- mState = state;
- triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
+ // track was already in the active list, not a problem
+ if (status == ALREADY_EXISTS) {
+ status = NO_ERROR;
}
} else {
status = BAD_VALUE;
@@ -653,26 +620,18 @@ void AudioFlinger::PlaybackThread::Track::stop()
if (playbackThread->mActiveTracks.indexOf(this) < 0) {
reset();
mState = STOPPED;
- } else if (!isFastTrack()) {
+ } else if (!isFastTrack() && !isOffloaded()) {
mState = STOPPED;
} else {
- // prepareTracks_l() will set state to STOPPING_2 after next underrun,
- // and then to STOPPED and reset() when presentation is complete
+ // For fast tracks prepareTracks_l() will set state to STOPPING_2
+ // presentation is complete
+ // For an offloaded track this starts a drain and state will
+ // move to STOPPING_2 when drain completes and then STOPPED
mState = STOPPING_1;
}
ALOGV("not stopping/stopped => stopping/stopped (%d) on thread %p", mName,
playbackThread);
}
- if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) {
- thread->mLock.unlock();
- AudioSystem::stopOutput(thread->id(), mStreamType, mSessionId);
- thread->mLock.lock();
-
-#ifdef ADD_BATTERY_DATA
- // to track the speaker usage
- addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
-#endif
- }
}
}
@@ -682,19 +641,27 @@ void AudioFlinger::PlaybackThread::Track::pause()
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
- if (mState == ACTIVE || mState == RESUMING) {
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ switch (mState) {
+ case STOPPING_1:
+ case STOPPING_2:
+ if (!isOffloaded()) {
+ /* nothing to do if track is not offloaded */
+ break;
+ }
+
+ // Offloaded track was draining, we need to carry on draining when resumed
+ mResumeToStopping = true;
+ // fall through...
+ case ACTIVE:
+ case RESUMING:
mState = PAUSING;
ALOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
- if (!isOutputTrack()) {
- thread->mLock.unlock();
- AudioSystem::stopOutput(thread->id(), mStreamType, mSessionId);
- thread->mLock.lock();
-
-#ifdef ADD_BATTERY_DATA
- // to track the speaker usage
- addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
-#endif
- }
+ playbackThread->broadcast_l();
+ break;
+
+ default:
+ break;
}
}
}
@@ -705,21 +672,52 @@ void AudioFlinger::PlaybackThread::Track::flush()
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
- if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED && mState != PAUSED &&
- mState != PAUSING && mState != IDLE && mState != FLUSHED) {
- return;
- }
- // No point remaining in PAUSED state after a flush => go to
- // FLUSHED state
- mState = FLUSHED;
- // do not reset the track if it is still in the process of being stopped or paused.
- // this will be done by prepareTracks_l() when the track is stopped.
- // prepareTracks_l() will see mState == FLUSHED, then
- // remove from active track list, reset(), and trigger presentation complete
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- if (playbackThread->mActiveTracks.indexOf(this) < 0) {
+
+ if (isOffloaded()) {
+ // If offloaded we allow flush during any state except terminated
+ // and keep the track active to avoid problems if user is seeking
+ // rapidly and underlying hardware has a significant delay handling
+ // a pause
+ if (isTerminated()) {
+ return;
+ }
+
+ ALOGV("flush: offload flush");
reset();
+
+ if (mState == STOPPING_1 || mState == STOPPING_2) {
+ ALOGV("flushed in STOPPING_1 or 2 state, change state to ACTIVE");
+ mState = ACTIVE;
+ }
+
+ if (mState == ACTIVE) {
+ ALOGV("flush called in active state, resetting buffer time out retry count");
+ mRetryCount = PlaybackThread::kMaxTrackRetriesOffload;
+ }
+
+ mResumeToStopping = false;
+ } else {
+ if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED &&
+ mState != PAUSED && mState != PAUSING && mState != IDLE && mState != FLUSHED) {
+ return;
+ }
+ // No point remaining in PAUSED state after a flush => go to
+ // FLUSHED state
+ mState = FLUSHED;
+ // do not reset the track if it is still in the process of being stopped or paused.
+ // this will be done by prepareTracks_l() when the track is stopped.
+ // prepareTracks_l() will see mState == FLUSHED, then
+ // remove from active track list, reset(), and trigger presentation complete
+ if (playbackThread->mActiveTracks.indexOf(this) < 0) {
+ reset();
+ }
}
+ // Prevent flush being lost if the track is flushed and then resumed
+ // before mixer thread can run. This is important when offloading
+ // because the hardware buffer could hold a large amount of audio
+ playbackThread->flushOutput_l();
+ playbackThread->broadcast_l();
}
}
@@ -728,11 +726,9 @@ void AudioFlinger::PlaybackThread::Track::reset()
// Do not reset twice to avoid discarding data written just after a flush and before
// the audioflinger thread detects the track is stopped.
if (!mResetDone) {
- TrackBase::reset();
// Force underrun condition to avoid false underrun callback until first data is
// written to buffer
- android_atomic_and(~CBLK_FORCEREADY, &mCblk->flags);
- android_atomic_or(CBLK_UNDERRUN, &mCblk->flags);
+ android_atomic_and(~CBLK_FORCEREADY, &mCblk->mFlags);
mFillingUpStatus = FS_FILLING;
mResetDone = true;
if (mState == FLUSHED) {
@@ -741,6 +737,51 @@ void AudioFlinger::PlaybackThread::Track::reset()
}
}
+status_t AudioFlinger::PlaybackThread::Track::setParameters(const String8& keyValuePairs)
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ ALOGE("thread is dead");
+ return FAILED_TRANSACTION;
+ } else if ((thread->type() == ThreadBase::DIRECT) ||
+ (thread->type() == ThreadBase::OFFLOAD)) {
+ return thread->setParameters(keyValuePairs);
+ } else {
+ return PERMISSION_DENIED;
+ }
+}
+
+status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
+{
+ // Client should implement this using SSQ; the unpresented frame count in latch is irrelevant
+ if (isFastTrack()) {
+ return INVALID_OPERATION;
+ }
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ return INVALID_OPERATION;
+ }
+ Mutex::Autolock _l(thread->mLock);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if (!isOffloaded()) {
+ if (!playbackThread->mLatchQValid) {
+ return INVALID_OPERATION;
+ }
+ uint32_t unpresentedFrames =
+ ((int64_t) playbackThread->mLatchQ.mUnpresentedFrames * mSampleRate) /
+ playbackThread->mSampleRate;
+ uint32_t framesWritten = mAudioTrackServerProxy->framesReleased();
+ if (framesWritten < unpresentedFrames) {
+ return INVALID_OPERATION;
+ }
+ timestamp.mPosition = framesWritten - unpresentedFrames;
+ timestamp.mTime = playbackThread->mLatchQ.mTimestamp.mTime;
+ return NO_ERROR;
+ }
+
+ return playbackThread->getTimestamp_l(timestamp);
+}
+
status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId)
{
status_t status = DEAD_OBJECT;
@@ -766,7 +807,11 @@ status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId)
return INVALID_OPERATION;
}
srcThread->removeEffect_l(effect);
- playbackThread->addEffect_l(effect);
+ status = playbackThread->addEffect_l(effect);
+ if (status != NO_ERROR) {
+ srcThread->addEffect_l(effect);
+ return INVALID_OPERATION;
+ }
// removeEffect_l() has stopped the effect if it was active so it must be restarted
if (effect->state() == EffectModule::ACTIVE ||
effect->state() == EffectModule::STOPPING) {
@@ -802,15 +847,23 @@ bool AudioFlinger::PlaybackThread::Track::presentationComplete(size_t framesWrit
// a track is considered presented when the total number of frames written to audio HAL
// corresponds to the number of frames written when presentationComplete() is called for the
// first time (mPresentationCompleteFrames == 0) plus the buffer filling status at that time.
+ // For an offloaded track the HAL+h/w delay is variable so a HAL drain() is used
+ // to detect when all frames have been played. In this case framesWritten isn't
+ // useful because it doesn't always reflect whether there is data in the h/w
+ // buffers, particularly if a track has been paused and resumed during draining
+ ALOGV("presentationComplete() mPresentationCompleteFrames %d framesWritten %d",
+ mPresentationCompleteFrames, framesWritten);
if (mPresentationCompleteFrames == 0) {
mPresentationCompleteFrames = framesWritten + audioHalFrames;
ALOGV("presentationComplete() reset: mPresentationCompleteFrames %d audioHalFrames %d",
mPresentationCompleteFrames, audioHalFrames);
}
- if (framesWritten >= mPresentationCompleteFrames) {
+
+ if (framesWritten >= mPresentationCompleteFrames || isOffloaded()) {
ALOGV("presentationComplete() session %d complete: framesWritten %d",
mSessionId, framesWritten);
triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
+ mAudioTrackServerProxy->setStreamEndDone();
return true;
}
return false;
@@ -833,7 +886,7 @@ uint32_t AudioFlinger::PlaybackThread::Track::getVolumeLR()
{
// called by FastMixer, so not allowed to take any locks, block, or do I/O including logs
ALOG_ASSERT(isFastTrack() && (mCblk != NULL));
- uint32_t vlr = mServerProxy->getVolumeLR();
+ uint32_t vlr = mAudioTrackServerProxy->getVolumeLR();
uint32_t vl = vlr & 0xFFFF;
uint32_t vr = vlr >> 16;
// track volumes come from shared memory, so can't be trusted and must be clamped
@@ -856,7 +909,7 @@ uint32_t AudioFlinger::PlaybackThread::Track::getVolumeLR()
status_t AudioFlinger::PlaybackThread::Track::setSyncEvent(const sp<SyncEvent>& event)
{
- if (mState == TERMINATED || mState == PAUSED ||
+ if (isTerminated() || mState == PAUSED ||
((framesReady() == 0) && ((mSharedBuffer != 0) ||
(mState == STOPPED)))) {
ALOGW("Track::setSyncEvent() in invalid state %d on session %d %s mode, framesReady %d ",
@@ -870,12 +923,25 @@ status_t AudioFlinger::PlaybackThread::Track::setSyncEvent(const sp<SyncEvent>&
void AudioFlinger::PlaybackThread::Track::invalidate()
{
- // FIXME should use proxy
- android_atomic_or(CBLK_INVALID, &mCblk->flags);
- mCblk->cv.signal();
+ // FIXME should use proxy, and needs work
+ audio_track_cblk_t* cblk = mCblk;
+ android_atomic_or(CBLK_INVALID, &cblk->mFlags);
+ android_atomic_release_store(0x40000000, &cblk->mFutex);
+ // client is not in server, so FUTEX_WAKE is needed instead of FUTEX_WAKE_PRIVATE
+ (void) __futex_syscall3(&cblk->mFutex, FUTEX_WAKE, INT_MAX);
mIsInvalid = true;
}
+void AudioFlinger::PlaybackThread::Track::signal()
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ Mutex::Autolock _l(t->mLock);
+ t->broadcast_l();
+ }
+}
+
// ----------------------------------------------------------------------------
sp<AudioFlinger::PlaybackThread::TimedTrack>
@@ -1185,10 +1251,12 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer(
}
}
+ uint32_t sr = sampleRate();
+
// adjust the head buffer's PTS to reflect the portion of the head buffer
// that has already been consumed
int64_t effectivePTS = headLocalPTS +
- ((head.position() / mFrameSize) * mLocalTimeFreq / sampleRate());
+ ((head.position() / mFrameSize) * mLocalTimeFreq / sr);
// Calculate the delta in samples between the head of the input buffer
// queue and the start of the next output buffer that will be written.
@@ -1220,7 +1288,7 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer(
// the current output position is within this threshold, then we will
// concatenate the next input samples to the previous output
const int64_t kSampleContinuityThreshold =
- (static_cast<int64_t>(sampleRate()) << 32) / 250;
+ (static_cast<int64_t>(sr) << 32) / 250;
// if this is the first buffer of audio that we're emitting from this track
// then it should be almost exactly on time.
@@ -1409,15 +1477,17 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
mOutBuffer.frameCount = 0;
playbackThread->mTracks.add(this);
ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, "
- "mCblk->frameCount_ %u, mChannelMask 0x%08x mBufferEnd %p",
+ "mCblk->frameCount_ %u, mChannelMask 0x%08x",
mCblk, mBuffer,
- mCblk->frameCount_, mChannelMask, mBufferEnd);
+ mCblk->frameCount_, mChannelMask);
// since client and server are in the same process,
// the buffer has the same virtual address on both sides
mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize);
mClientProxy->setVolumeLR((uint32_t(uint16_t(0x1000)) << 16) | uint16_t(0x1000));
mClientProxy->setSendLevel(0.0);
mClientProxy->setSampleRate(sampleRate);
+ mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize,
+ true /*clientInServer*/);
} else {
ALOGW("Error creating output track on thread %p", playbackThread);
}
@@ -1477,7 +1547,7 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
memset(pInBuffer->raw, 0, startFrames * channelCount * sizeof(int16_t));
mBufferQueue.add(pInBuffer);
} else {
- ALOGW ("OutputTrack::write() %p no more buffers in queue", this);
+ ALOGW("OutputTrack::write() %p no more buffers in queue", this);
}
}
}
@@ -1498,9 +1568,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
if (mOutBuffer.frameCount == 0) {
mOutBuffer.frameCount = pInBuffer->frameCount;
nsecs_t startTime = systemTime();
- if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)NO_MORE_BUFFERS) {
- ALOGV ("OutputTrack::write() %p thread %p no more output buffers", this,
- mThread.unsafe_get());
+ status_t status = obtainBuffer(&mOutBuffer, waitTimeLeftMs);
+ if (status != NO_ERROR) {
+ ALOGV("OutputTrack::write() %p thread %p no more output buffers; status %d", this,
+ mThread.unsafe_get(), status);
outputBufferFull = true;
break;
}
@@ -1515,7 +1586,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount :
pInBuffer->frameCount;
memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t));
- mClientProxy->stepUser(outFrames);
+ Proxy::Buffer buf;
+ buf.mFrameCount = outFrames;
+ buf.mRaw = NULL;
+ mClientProxy->releaseBuffer(&buf);
pInBuffer->frameCount -= outFrames;
pInBuffer->i16 += outFrames * channelCount;
mOutBuffer.frameCount -= outFrames;
@@ -1559,8 +1633,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
// If no more buffers are pending, fill output track buffer to make sure it is started
// by output mixer.
if (frames == 0 && mBufferQueue.size() == 0) {
- if (mCblk->user < mFrameCount) {
- frames = mFrameCount - mCblk->user;
+ // FIXME borken, replace by getting framesReady() from proxy
+ size_t user = 0; // was mCblk->user
+ if (user < mFrameCount) {
+ frames = mFrameCount - user;
pInBuffer = new Buffer;
pInBuffer->mBuffer = new int16_t[frames * channelCount];
pInBuffer->frameCount = frames;
@@ -1578,46 +1654,17 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(
AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs)
{
- audio_track_cblk_t* cblk = mCblk;
- uint32_t framesReq = buffer->frameCount;
-
- ALOGVV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server);
- buffer->frameCount = 0;
-
- size_t framesAvail;
- {
- Mutex::Autolock _l(cblk->lock);
-
- // read the server count again
- while (!(framesAvail = mClientProxy->framesAvailable_l())) {
- if (CC_UNLIKELY(!mActive)) {
- ALOGV("Not active and NO_MORE_BUFFERS");
- return NO_MORE_BUFFERS;
- }
- status_t result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
- if (result != NO_ERROR) {
- return NO_MORE_BUFFERS;
- }
- }
- }
-
- if (framesReq > framesAvail) {
- framesReq = framesAvail;
- }
-
- uint32_t u = cblk->user;
- uint32_t bufferEnd = cblk->userBase + mFrameCount;
-
- if (framesReq > bufferEnd - u) {
- framesReq = bufferEnd - u;
- }
-
- buffer->frameCount = framesReq;
- buffer->raw = mClientProxy->buffer(u);
- return NO_ERROR;
+ ClientProxy::Buffer buf;
+ buf.mFrameCount = buffer->frameCount;
+ struct timespec timeout;
+ timeout.tv_sec = waitTimeMs / 1000;
+ timeout.tv_nsec = (int) (waitTimeMs % 1000) * 1000000;
+ status_t status = mClientProxy->obtainBuffer(&buf, &timeout);
+ buffer->frameCount = buf.mFrameCount;
+ buffer->raw = buf.mRaw;
+ return status;
}
-
void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue()
{
size_t size = mBufferQueue.size();
@@ -1687,7 +1734,12 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, false /*isOut*/),
mOverflow(false)
{
- ALOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer);
+ ALOGV("RecordTrack constructor");
+ if (mCblk != NULL) {
+ mAudioRecordServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
+ mFrameSize);
+ mServerProxy = mAudioRecordServerProxy;
+ }
}
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
@@ -1699,42 +1751,16 @@ AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer,
int64_t pts)
{
- audio_track_cblk_t* cblk = this->cblk();
- uint32_t framesAvail;
- uint32_t framesReq = buffer->frameCount;
-
- // Check if last stepServer failed, try to step now
- if (mStepServerFailed) {
- if (!step()) {
- goto getNextBuffer_exit;
- }
- ALOGV("stepServer recovered");
- mStepServerFailed = false;
+ ServerProxy::Buffer buf;
+ buf.mFrameCount = buffer->frameCount;
+ status_t status = mServerProxy->obtainBuffer(&buf);
+ buffer->frameCount = buf.mFrameCount;
+ buffer->raw = buf.mRaw;
+ if (buf.mFrameCount == 0) {
+ // FIXME also wake futex so that overrun is noticed more quickly
+ (void) android_atomic_or(CBLK_OVERRUN, &mCblk->mFlags);
}
-
- // FIXME lock is not actually held, so overrun is possible
- framesAvail = mServerProxy->framesAvailableIn_l();
-
- if (CC_LIKELY(framesAvail)) {
- uint32_t s = cblk->server;
- uint32_t bufferEnd = cblk->serverBase + mFrameCount;
-
- if (framesReq > framesAvail) {
- framesReq = framesAvail;
- }
- if (framesReq > bufferEnd - s) {
- framesReq = bufferEnd - s;
- }
-
- buffer->raw = getBuffer(s, framesReq);
- buffer->frameCount = framesReq;
- return NO_ERROR;
- }
-
-getNextBuffer_exit:
- buffer->raw = NULL;
- buffer->frameCount = 0;
- return NOT_ENOUGH_DATA;
+ return status;
}
status_t AudioFlinger::RecordThread::RecordTrack::start(AudioSystem::sync_event_t event,
@@ -1754,16 +1780,7 @@ void AudioFlinger::RecordThread::RecordTrack::stop()
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
RecordThread *recordThread = (RecordThread *)thread.get();
- recordThread->mLock.lock();
- bool doStop = recordThread->stop_l(this);
- if (doStop) {
- TrackBase::reset();
- // Force overrun condition to avoid false overrun callback until first data is
- // read from buffer
- android_atomic_or(CBLK_UNDERRUN, &mCblk->flags);
- }
- recordThread->mLock.unlock();
- if (doStop) {
+ if (recordThread->stop(this)) {
AudioSystem::stopInput(recordThread->id());
}
}
@@ -1787,23 +1804,31 @@ void AudioFlinger::RecordThread::RecordTrack::destroy()
}
}
+void AudioFlinger::RecordThread::RecordTrack::invalidate()
+{
+ // FIXME should use proxy, and needs work
+ audio_track_cblk_t* cblk = mCblk;
+ android_atomic_or(CBLK_INVALID, &cblk->mFlags);
+ android_atomic_release_store(0x40000000, &cblk->mFutex);
+ // client is not in server, so FUTEX_WAKE is needed instead of FUTEX_WAKE_PRIVATE
+ (void) __futex_syscall3(&cblk->mFutex, FUTEX_WAKE, INT_MAX);
+}
+
/*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
{
- result.append(" Clien Fmt Chn mask Session Step S Serv User FrameCount\n");
+ result.append("Client Fmt Chn mask Session S Server fCount\n");
}
void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
{
- snprintf(buffer, size, " %05d %03u 0x%08x %05d %04u %01d %08x %08x %05d\n",
+ snprintf(buffer, size, "%6u %3u %08X %7u %1d %08X %6u\n",
(mClient == 0) ? getpid_cached : mClient->pid(),
mFormat,
mChannelMask,
mSessionId,
- mStepCount,
mState,
- mCblk->server,
- mCblk->user,
+ mCblk->mServer,
mFrameCount);
}