summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorGlenn Kasten <gkasten@google.com>2012-12-18 15:57:32 -0800
committerGlenn Kasten <gkasten@google.com>2013-06-12 14:33:10 -0700
commit9f80dd223d83d9bb9077fb6baee056cee4eaf7e5 (patch)
tree79f19f80ebb7978e9bab16e1d9b835849c735592 /media
parent9fef8d453b15a91a2b748faac2bfaff713bcf1e1 (diff)
downloadframeworks_av-9f80dd223d83d9bb9077fb6baee056cee4eaf7e5.zip
frameworks_av-9f80dd223d83d9bb9077fb6baee056cee4eaf7e5.tar.gz
frameworks_av-9f80dd223d83d9bb9077fb6baee056cee4eaf7e5.tar.bz2
New control block for AudioTrack and AudioRecord
Main differences between old and new control block: - removes the mutex, which was a potential source of priority inversion - circular indices into shared buffer, which is now always a power-of-2 size Change-Id: I4e9b7fa99858b488ac98a441fa70e31dbba1b865
Diffstat (limited to 'media')
-rw-r--r--media/libmedia/AudioRecord.cpp832
-rw-r--r--media/libmedia/AudioTrack.cpp1219
-rw-r--r--media/libmedia/AudioTrackShared.cpp716
-rw-r--r--media/libmedia/ToneGenerator.cpp4
4 files changed, 1726 insertions, 1045 deletions
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index a2b8ae2..9faa497 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -19,18 +19,13 @@
#define LOG_TAG "AudioRecord"
#include <sys/resource.h>
-#include <sys/types.h>
-
#include <binder/IPCThreadState.h>
-#include <cutils/atomic.h>
-#include <cutils/compiler.h>
#include <media/AudioRecord.h>
-#include <media/AudioSystem.h>
-#include <system/audio.h>
#include <utils/Log.h>
-
#include <private/media/AudioTrackShared.h>
+#define WAIT_PERIOD_MS 10
+
namespace android {
// ---------------------------------------------------------------------------
@@ -41,7 +36,9 @@ status_t AudioRecord::getMinFrameCount(
audio_format_t format,
audio_channel_mask_t channelMask)
{
- if (frameCount == NULL) return BAD_VALUE;
+ if (frameCount == NULL) {
+ return BAD_VALUE;
+ }
// default to 0 in case of error
*frameCount = 0;
@@ -75,8 +72,7 @@ status_t AudioRecord::getMinFrameCount(
AudioRecord::AudioRecord()
: mStatus(NO_INIT), mSessionId(0),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
- mProxy(NULL)
+ mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
{
}
@@ -89,14 +85,15 @@ AudioRecord::AudioRecord(
callback_t cbf,
void* user,
int notificationFrames,
- int sessionId)
+ int sessionId,
+ transfer_type transferType)
: mStatus(NO_INIT), mSessionId(0),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
mProxy(NULL)
{
- mStatus = set(inputSource, sampleRate, format, channelMask,
- frameCount, cbf, user, notificationFrames, false /*threadCanCallJava*/, sessionId);
+ mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
+ notificationFrames, false /*threadCanCallJava*/, sessionId, transferType);
}
AudioRecord::~AudioRecord()
@@ -111,11 +108,13 @@ AudioRecord::~AudioRecord()
mAudioRecordThread->requestExitAndWait();
mAudioRecordThread.clear();
}
- mAudioRecord.clear();
+ if (mAudioRecord != 0) {
+ mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
+ mAudioRecord.clear();
+ }
IPCThreadState::self()->flushCommands();
AudioSystem::releaseAudioSessionId(mSessionId);
}
- delete mProxy;
}
status_t AudioRecord::set(
@@ -128,8 +127,32 @@ status_t AudioRecord::set(
void* user,
int notificationFrames,
bool threadCanCallJava,
- int sessionId)
+ int sessionId,
+ transfer_type transferType)
{
+ switch (transferType) {
+ case TRANSFER_DEFAULT:
+ if (cbf == NULL || threadCanCallJava) {
+ transferType = TRANSFER_SYNC;
+ } else {
+ transferType = TRANSFER_CALLBACK;
+ }
+ break;
+ case TRANSFER_CALLBACK:
+ if (cbf == NULL) {
+ ALOGE("Transfer type TRANSFER_CALLBACK but cbf == NULL");
+ return BAD_VALUE;
+ }
+ break;
+ case TRANSFER_OBTAIN:
+ case TRANSFER_SYNC:
+ break;
+ default:
+ ALOGE("Invalid transfer type %d", transferType);
+ return BAD_VALUE;
+ }
+ mTransfer = transferType;
+
// FIXME "int" here is legacy and will be replaced by size_t later
if (frameCountInt < 0) {
ALOGE("Invalid frame count %d", frameCountInt);
@@ -143,6 +166,7 @@ status_t AudioRecord::set(
AutoMutex lock(mLock);
if (mAudioRecord != 0) {
+ ALOGE("Track already in use");
return INVALID_OPERATION;
}
@@ -159,14 +183,16 @@ status_t AudioRecord::set(
if (format == AUDIO_FORMAT_DEFAULT) {
format = AUDIO_FORMAT_PCM_16_BIT;
}
+
// validate parameters
if (!audio_is_valid_format(format)) {
- ALOGE("Invalid format");
+ ALOGE("Invalid format %d", format);
return BAD_VALUE;
}
mFormat = format;
if (!audio_is_input_channel(channelMask)) {
+ ALOGE("Invalid channel mask %#x", channelMask);
return BAD_VALUE;
}
mChannelMask = channelMask;
@@ -200,6 +226,7 @@ status_t AudioRecord::set(
size_t minFrameCount = 0;
status_t status = getMinFrameCount(&minFrameCount, sampleRate, format, channelMask);
if (status != NO_ERROR) {
+ ALOGE("getMinFrameCount() failed; status %d", status);
return status;
}
ALOGV("AudioRecord::set() minFrameCount = %d", minFrameCount);
@@ -207,6 +234,7 @@ status_t AudioRecord::set(
if (frameCount == 0) {
frameCount = minFrameCount;
} else if (frameCount < minFrameCount) {
+ ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount);
return BAD_VALUE;
}
@@ -215,7 +243,7 @@ status_t AudioRecord::set(
}
// create the IAudioRecord
- status = openRecord_l(sampleRate, format, frameCount, input);
+ status = openRecord_l(sampleRate, format, frameCount, input, 0 /*epoch*/);
if (status != NO_ERROR) {
return status;
}
@@ -233,7 +261,7 @@ status_t AudioRecord::set(
mActive = false;
mCbf = cbf;
mNotificationFrames = notificationFrames;
- mRemainingFrames = notificationFrames;
+ mRefreshRemaining = true;
mUserData = user;
// TODO: add audio hardware input latency here
mLatency = (1000*mFrameCount) / sampleRate;
@@ -244,117 +272,78 @@ status_t AudioRecord::set(
mInputSource = inputSource;
mInput = input;
AudioSystem::acquireAudioSessionId(mSessionId);
+ mSequence = 1;
+ mObservedSequence = mSequence;
+ mInOverrun = false;
return NO_ERROR;
}
-status_t AudioRecord::initCheck() const
-{
- return mStatus;
-}
-
-// -------------------------------------------------------------------------
-
-uint32_t AudioRecord::latency() const
-{
- return mLatency;
-}
-
-audio_format_t AudioRecord::format() const
-{
- return mFormat;
-}
-
-uint32_t AudioRecord::channelCount() const
-{
- return mChannelCount;
-}
-
-size_t AudioRecord::frameCount() const
-{
- return mFrameCount;
-}
-
-audio_source_t AudioRecord::inputSource() const
-{
- return mInputSource;
-}
-
// -------------------------------------------------------------------------
status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession)
{
- status_t ret = NO_ERROR;
- sp<AudioRecordThread> t = mAudioRecordThread;
-
ALOGV("start, sync event %d trigger session %d", event, triggerSession);
AutoMutex lock(mLock);
- // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
- // while we are accessing the cblk
- sp<IAudioRecord> audioRecord = mAudioRecord;
- sp<IMemory> iMem = mCblkMemory;
- audio_track_cblk_t* cblk = mCblk;
+ if (mActive) {
+ return NO_ERROR;
+ }
- if (!mActive) {
- mActive = true;
+ // reset current position as seen by client to 0
+ mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
- cblk->lock.lock();
- if (!(cblk->flags & CBLK_INVALID)) {
- cblk->lock.unlock();
- ALOGV("mAudioRecord->start()");
- ret = mAudioRecord->start(event, triggerSession);
- cblk->lock.lock();
- if (ret == DEAD_OBJECT) {
- android_atomic_or(CBLK_INVALID, &cblk->flags);
- }
- }
- if (cblk->flags & CBLK_INVALID) {
- audio_track_cblk_t* temp = cblk;
- ret = restoreRecord_l(temp);
- cblk = temp;
+ mNewPosition = mProxy->getPosition() + mUpdatePeriod;
+ int32_t flags = android_atomic_acquire_load(&mCblk->flags);
+
+ status_t status = NO_ERROR;
+ if (!(flags & CBLK_INVALID)) {
+ ALOGV("mAudioRecord->start()");
+ status = mAudioRecord->start(event, triggerSession);
+ if (status == DEAD_OBJECT) {
+ flags |= CBLK_INVALID;
}
- cblk->lock.unlock();
- if (ret == NO_ERROR) {
- mNewPosition = cblk->user + mUpdatePeriod;
- cblk->bufferTimeoutMs = (event == AudioSystem::SYNC_EVENT_NONE) ? MAX_RUN_TIMEOUT_MS :
- AudioSystem::kSyncRecordStartTimeOutMs;
- cblk->waitTimeMs = 0;
- if (t != 0) {
- t->resume();
- } else {
- mPreviousPriority = getpriority(PRIO_PROCESS, 0);
- get_sched_policy(0, &mPreviousSchedulingGroup);
- androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
- }
+ }
+ if (flags & CBLK_INVALID) {
+ status = restoreRecord_l("start");
+ }
+
+ if (status != NO_ERROR) {
+ ALOGE("start() status %d", status);
+ } else {
+ mActive = true;
+ sp<AudioRecordThread> t = mAudioRecordThread;
+ if (t != 0) {
+ t->resume();
} else {
- mActive = false;
+ mPreviousPriority = getpriority(PRIO_PROCESS, 0);
+ get_sched_policy(0, &mPreviousSchedulingGroup);
+ androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
}
}
- return ret;
+ return status;
}
void AudioRecord::stop()
{
- sp<AudioRecordThread> t = mAudioRecordThread;
-
- ALOGV("stop");
-
AutoMutex lock(mLock);
- if (mActive) {
- mActive = false;
- mCblk->cv.signal();
- mAudioRecord->stop();
- // the record head position will reset to 0, so if a marker is set, we need
- // to activate it again
- mMarkerReached = false;
- if (t != 0) {
- t->pause();
- } else {
- setpriority(PRIO_PROCESS, 0, mPreviousPriority);
- set_sched_policy(0, mPreviousSchedulingGroup);
- }
+ if (!mActive) {
+ return;
+ }
+
+ mActive = false;
+ mProxy->interrupt();
+ mAudioRecord->stop();
+ // the record head position will reset to 0, so if a marker is set, we need
+ // to activate it again
+ mMarkerReached = false;
+ sp<AudioRecordThread> t = mAudioRecordThread;
+ if (t != 0) {
+ t->pause();
+ } else {
+ setpriority(PRIO_PROCESS, 0, mPreviousPriority);
+ set_sched_policy(0, mPreviousSchedulingGroup);
}
}
@@ -364,14 +353,11 @@ bool AudioRecord::stopped() const
return !mActive;
}
-uint32_t AudioRecord::getSampleRate() const
-{
- return mSampleRate;
-}
-
status_t AudioRecord::setMarkerPosition(uint32_t marker)
{
- if (mCbf == NULL) return INVALID_OPERATION;
+ if (mCbf == NULL) {
+ return INVALID_OPERATION;
+ }
AutoMutex lock(mLock);
mMarkerPosition = marker;
@@ -382,7 +368,9 @@ status_t AudioRecord::setMarkerPosition(uint32_t marker)
status_t AudioRecord::getMarkerPosition(uint32_t *marker) const
{
- if (marker == NULL) return BAD_VALUE;
+ if (marker == NULL) {
+ return BAD_VALUE;
+ }
AutoMutex lock(mLock);
*marker = mMarkerPosition;
@@ -392,13 +380,12 @@ status_t AudioRecord::getMarkerPosition(uint32_t *marker) const
status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod)
{
- if (mCbf == NULL) return INVALID_OPERATION;
-
- uint32_t curPosition;
- getPosition(&curPosition);
+ if (mCbf == NULL) {
+ return INVALID_OPERATION;
+ }
AutoMutex lock(mLock);
- mNewPosition = curPosition + updatePeriod;
+ mNewPosition = mProxy->getPosition() + updatePeriod;
mUpdatePeriod = updatePeriod;
return NO_ERROR;
@@ -406,7 +393,9 @@ status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod)
status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod) const
{
- if (updatePeriod == NULL) return BAD_VALUE;
+ if (updatePeriod == NULL) {
+ return BAD_VALUE;
+ }
AutoMutex lock(mLock);
*updatePeriod = mUpdatePeriod;
@@ -416,10 +405,12 @@ status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod) const
status_t AudioRecord::getPosition(uint32_t *position) const
{
- if (position == NULL) return BAD_VALUE;
+ if (position == NULL) {
+ return BAD_VALUE;
+ }
AutoMutex lock(mLock);
- *position = mCblk->user;
+ *position = mProxy->getPosition();
return NO_ERROR;
}
@@ -427,7 +418,7 @@ status_t AudioRecord::getPosition(uint32_t *position) const
unsigned int AudioRecord::getInputFramesLost() const
{
// no need to check mActive, because if inactive this will return 0, which is what we want
- return AudioSystem::getInputFramesLost(mInput);
+ return AudioSystem::getInputFramesLost(getInput());
}
// -------------------------------------------------------------------------
@@ -437,7 +428,8 @@ status_t AudioRecord::openRecord_l(
uint32_t sampleRate,
audio_format_t format,
size_t frameCount,
- audio_io_handle_t input)
+ audio_io_handle_t input,
+ size_t epoch)
{
status_t status;
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
@@ -447,7 +439,7 @@ status_t AudioRecord::openRecord_l(
}
pid_t tid = -1;
- // FIXME see similar logic at AudioTrack
+ // FIXME see similar logic at AudioTrack for tid
int originalSessionId = mSessionId;
sp<IAudioRecord> record = audioFlinger->openRecord(input,
@@ -470,133 +462,138 @@ status_t AudioRecord::openRecord_l(
ALOGE("Could not get control block");
return NO_INIT;
}
- mAudioRecord.clear();
+ if (mAudioRecord != 0) {
+ mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
+ mDeathNotifier.clear();
+ }
mAudioRecord = record;
- mCblkMemory.clear();
mCblkMemory = iMem;
audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMem->pointer());
mCblk = cblk;
- mBuffers = (char*)cblk + sizeof(audio_track_cblk_t);
- cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
- cblk->waitTimeMs = 0;
+
+ // starting address of buffers in shared memory
+ void *buffers = (char*)cblk + sizeof(audio_track_cblk_t);
// update proxy
- delete mProxy;
- mProxy = new AudioRecordClientProxy(cblk, mBuffers, frameCount, mFrameSize);
+ mProxy = new AudioRecordClientProxy(cblk, buffers, frameCount, mFrameSize);
+ mProxy->setEpoch(epoch);
+ mProxy->setMinimum(mNotificationFrames);
+
+ mDeathNotifier = new DeathNotifier(this);
+ mAudioRecord->asBinder()->linkToDeath(mDeathNotifier, this);
return NO_ERROR;
}
status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
- ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+ if (audioBuffer == NULL) {
+ return BAD_VALUE;
+ }
+ if (mTransfer != TRANSFER_OBTAIN) {
+ audioBuffer->frameCount = 0;
+ audioBuffer->size = 0;
+ audioBuffer->raw = NULL;
+ return INVALID_OPERATION;
+ }
- AutoMutex lock(mLock);
- bool active;
- status_t result = NO_ERROR;
- audio_track_cblk_t* cblk = mCblk;
- uint32_t framesReq = audioBuffer->frameCount;
- uint32_t waitTimeMs = (waitCount < 0) ? cblk->bufferTimeoutMs : WAIT_PERIOD_MS;
-
- audioBuffer->frameCount = 0;
- audioBuffer->size = 0;
-
- size_t framesReady = mProxy->framesReady();
-
- if (framesReady == 0) {
- cblk->lock.lock();
- goto start_loop_here;
- while (framesReady == 0) {
- active = mActive;
- if (CC_UNLIKELY(!active)) {
- cblk->lock.unlock();
- return NO_MORE_BUFFERS;
- }
- if (CC_UNLIKELY(!waitCount)) {
- cblk->lock.unlock();
- return WOULD_BLOCK;
- }
- if (!(cblk->flags & CBLK_INVALID)) {
- mLock.unlock();
- // this condition is in shared memory, so if IAudioRecord and control block
- // are replaced due to mediaserver death or IAudioRecord invalidation then
- // cv won't be signalled, but fortunately the timeout will limit the wait
- result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
- cblk->lock.unlock();
- mLock.lock();
- if (!mActive) {
- return status_t(STOPPED);
- }
- // IAudioRecord may have been re-created while mLock was unlocked
- cblk = mCblk;
- cblk->lock.lock();
- }
- if (cblk->flags & CBLK_INVALID) {
- goto create_new_record;
- }
- if (CC_UNLIKELY(result != NO_ERROR)) {
- cblk->waitTimeMs += waitTimeMs;
- if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) {
- ALOGW( "obtainBuffer timed out (is the CPU pegged?) "
- "user=%08x, server=%08x", cblk->user, cblk->server);
- cblk->lock.unlock();
- // callback thread or sync event hasn't changed
- result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0);
- cblk->lock.lock();
- if (result == DEAD_OBJECT) {
- android_atomic_or(CBLK_INVALID, &cblk->flags);
-create_new_record:
- audio_track_cblk_t* temp = cblk;
- result = AudioRecord::restoreRecord_l(temp);
- cblk = temp;
- }
- if (result != NO_ERROR) {
- ALOGW("obtainBuffer create Track error %d", result);
- cblk->lock.unlock();
- return result;
+ const struct timespec *requested;
+ if (waitCount == -1) {
+ requested = &ClientProxy::kForever;
+ } else if (waitCount == 0) {
+ requested = &ClientProxy::kNonBlocking;
+ } else if (waitCount > 0) {
+ long long ms = WAIT_PERIOD_MS * (long long) waitCount;
+ struct timespec timeout;
+ timeout.tv_sec = ms / 1000;
+ timeout.tv_nsec = (int) (ms % 1000) * 1000000;
+ requested = &timeout;
+ } else {
+ ALOGE("%s invalid waitCount %d", __func__, waitCount);
+ requested = NULL;
+ }
+ return obtainBuffer(audioBuffer, requested);
+}
+
+status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
+ struct timespec *elapsed, size_t *nonContig)
+{
+ // previous and new IAudioRecord sequence numbers are used to detect track re-creation
+ uint32_t oldSequence = 0;
+ uint32_t newSequence;
+
+ Proxy::Buffer buffer;
+ status_t status = NO_ERROR;
+
+ static const int32_t kMaxTries = 5;
+ int32_t tryCounter = kMaxTries;
+
+ do {
+ // obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to
+ // keep them from going away if another thread re-creates the track during obtainBuffer()
+ sp<AudioRecordClientProxy> proxy;
+ sp<IMemory> iMem;
+ {
+ // start of lock scope
+ AutoMutex lock(mLock);
+
+ newSequence = mSequence;
+ // did previous obtainBuffer() fail due to media server death or voluntary invalidation?
+ if (status == DEAD_OBJECT) {
+ // re-create track, unless someone else has already done so
+ if (newSequence == oldSequence) {
+ status = restoreRecord_l("obtainBuffer");
+ if (status != NO_ERROR) {
+ break;
}
- cblk->waitTimeMs = 0;
- }
- if (--waitCount == 0) {
- cblk->lock.unlock();
- return TIMED_OUT;
}
}
- // read the server count again
-start_loop_here:
- framesReady = mProxy->framesReady();
- }
- cblk->lock.unlock();
- }
+ oldSequence = newSequence;
- cblk->waitTimeMs = 0;
- // reset time out to running value after obtaining a buffer
- cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
+ // Keep the extra references
+ proxy = mProxy;
+ iMem = mCblkMemory;
- if (framesReq > framesReady) {
- framesReq = framesReady;
- }
+ // Non-blocking if track is stopped
+ if (!mActive) {
+ requested = &ClientProxy::kNonBlocking;
+ }
- uint32_t u = cblk->user;
- uint32_t bufferEnd = cblk->userBase + mFrameCount;
+ } // end of lock scope
- if (framesReq > bufferEnd - u) {
- framesReq = bufferEnd - u;
- }
+ buffer.mFrameCount = audioBuffer->frameCount;
+ // FIXME starts the requested timeout and elapsed over from scratch
+ status = proxy->obtainBuffer(&buffer, requested, elapsed);
+
+ } while ((status == DEAD_OBJECT) && (tryCounter-- > 0));
- audioBuffer->frameCount = framesReq;
- audioBuffer->size = framesReq * mFrameSize;
- audioBuffer->raw = mProxy->buffer(u);
- active = mActive;
- return active ? status_t(NO_ERROR) : status_t(STOPPED);
+ audioBuffer->frameCount = buffer.mFrameCount;
+ audioBuffer->size = buffer.mFrameCount * mFrameSize;
+ audioBuffer->raw = buffer.mRaw;
+ if (nonContig != NULL) {
+ *nonContig = buffer.mNonContig;
+ }
+ return status;
}
void AudioRecord::releaseBuffer(Buffer* audioBuffer)
{
- ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+ // all TRANSFER_* are valid
+
+ size_t stepCount = audioBuffer->size / mFrameSize;
+ if (stepCount == 0) {
+ return;
+ }
+
+ Proxy::Buffer buffer;
+ buffer.mFrameCount = stepCount;
+ buffer.mRaw = audioBuffer->raw;
AutoMutex lock(mLock);
- (void) mProxy->stepUser(audioBuffer->frameCount);
+ mInOverrun = false;
+ mProxy->releaseBuffer(&buffer);
+
+ // the server does not automatically disable recorder on overrun, so no need to restart
}
audio_io_handle_t AudioRecord::getInput() const
@@ -616,215 +613,304 @@ audio_io_handle_t AudioRecord::getInput_l()
return mInput;
}
-int AudioRecord::getSessionId() const
-{
- // no lock needed because session ID doesn't change after first set()
- return mSessionId;
-}
-
// -------------------------------------------------------------------------
ssize_t AudioRecord::read(void* buffer, size_t userSize)
{
- ssize_t read = 0;
- Buffer audioBuffer;
- int8_t *dst = static_cast<int8_t*>(buffer);
+ if (mTransfer != TRANSFER_SYNC) {
+ return INVALID_OPERATION;
+ }
- if (ssize_t(userSize) < 0) {
- // sanity-check. user is most-likely passing an error code.
- ALOGE("AudioRecord::read(buffer=%p, size=%u (%d)",
- buffer, userSize, userSize);
+ if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
+ // sanity-check. user is most-likely passing an error code, and it would
+ // make the return value ambiguous (actualSize vs error).
+ ALOGE("AudioRecord::read(buffer=%p, size=%u (%d)", buffer, userSize, userSize);
return BAD_VALUE;
}
- mLock.lock();
- // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
- // while we are accessing the cblk
- sp<IAudioRecord> audioRecord = mAudioRecord;
- sp<IMemory> iMem = mCblkMemory;
- mLock.unlock();
-
- do {
+ ssize_t read = 0;
+ Buffer audioBuffer;
- audioBuffer.frameCount = userSize/frameSize();
+ while (userSize >= mFrameSize) {
+ audioBuffer.frameCount = userSize / mFrameSize;
- // By using a wait count corresponding to twice the timeout period in
- // obtainBuffer() we give a chance to recover once for a read timeout
- // (if media_server crashed for instance) before returning a length of
- // 0 bytes read to the client
- status_t err = obtainBuffer(&audioBuffer, ((2 * MAX_RUN_TIMEOUT_MS) / WAIT_PERIOD_MS));
+ status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever);
if (err < 0) {
- // out of buffers, return #bytes written
- if (err == status_t(NO_MORE_BUFFERS)) {
+ if (read > 0) {
break;
}
- if (err == status_t(TIMED_OUT)) {
- // return partial transfer count
- return read;
- }
return ssize_t(err);
}
size_t bytesRead = audioBuffer.size;
- memcpy(dst, audioBuffer.i8, bytesRead);
-
- dst += bytesRead;
+ memcpy(buffer, audioBuffer.i8, bytesRead);
+ buffer = ((char *) buffer) + bytesRead;
userSize -= bytesRead;
read += bytesRead;
releaseBuffer(&audioBuffer);
- } while (userSize);
+ }
return read;
}
// -------------------------------------------------------------------------
-bool AudioRecord::processAudioBuffer(const sp<AudioRecordThread>& thread)
+nsecs_t AudioRecord::processAudioBuffer(const sp<AudioRecordThread>& thread)
{
- Buffer audioBuffer;
- uint32_t frames = mRemainingFrames;
- size_t readSize;
-
mLock.lock();
- // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
- // while we are accessing the cblk
- sp<IAudioRecord> audioRecord = mAudioRecord;
- sp<IMemory> iMem = mCblkMemory;
- audio_track_cblk_t* cblk = mCblk;
+
+ // Can only reference mCblk while locked
+ int32_t flags = android_atomic_and(~CBLK_OVERRUN, &mCblk->flags);
+
+ // Check for track invalidation
+ if (flags & CBLK_INVALID) {
+ (void) restoreRecord_l("processAudioBuffer");
+ mLock.unlock();
+ // Run again immediately, but with a new IAudioRecord
+ return 0;
+ }
+
bool active = mActive;
- uint32_t markerPosition = mMarkerPosition;
- uint32_t newPosition = mNewPosition;
- uint32_t user = cblk->user;
- // determine whether a marker callback will be needed, while locked
- bool needMarker = !mMarkerReached && (mMarkerPosition > 0) && (user >= mMarkerPosition);
- if (needMarker) {
- mMarkerReached = true;
- }
- // determine the number of new position callback(s) that will be needed, while locked
+
+ // Manage overrun callback, must be done under lock to avoid race with releaseBuffer()
+ bool newOverrun = false;
+ if (flags & CBLK_OVERRUN) {
+ if (!mInOverrun) {
+ mInOverrun = true;
+ newOverrun = true;
+ }
+ }
+
+ // Get current position of server
+ size_t position = mProxy->getPosition();
+
+ // Manage marker callback
+ bool markerReached = false;
+ size_t markerPosition = mMarkerPosition;
+ // FIXME fails for wraparound, need 64 bits
+ if (!mMarkerReached && (markerPosition > 0) && (position >= markerPosition)) {
+ mMarkerReached = markerReached = true;
+ }
+
+ // Determine the number of new position callback(s) that will be needed, while locked
+ size_t newPosCount = 0;
+ size_t newPosition = mNewPosition;
uint32_t updatePeriod = mUpdatePeriod;
- uint32_t needNewPos = updatePeriod > 0 && user >= newPosition ?
- ((user - newPosition) / updatePeriod) + 1 : 0;
- mNewPosition = newPosition + updatePeriod * needNewPos;
+ // FIXME fails for wraparound, need 64 bits
+ if (updatePeriod > 0 && position >= newPosition) {
+ newPosCount = ((position - newPosition) / updatePeriod) + 1;
+ mNewPosition += updatePeriod * newPosCount;
+ }
+
+ // Cache other fields that will be needed soon
+ size_t notificationFrames = mNotificationFrames;
+ if (mRefreshRemaining) {
+ mRefreshRemaining = false;
+ mRemainingFrames = notificationFrames;
+ mRetryOnPartialBuffer = false;
+ }
+ size_t misalignment = mProxy->getMisalignment();
+ int32_t sequence = mSequence;
+
+ // These fields don't need to be cached, because they are assigned only by set():
+ // mTransfer, mCbf, mUserData, mSampleRate
+
mLock.unlock();
- // perform marker callback, while unlocked
- if (needMarker) {
+ // perform callbacks while unlocked
+ if (newOverrun) {
+ mCbf(EVENT_OVERRUN, mUserData, NULL);
+ }
+ if (markerReached) {
mCbf(EVENT_MARKER, mUserData, &markerPosition);
}
-
- // perform new position callback(s), while unlocked
- for (; needNewPos > 0; --needNewPos) {
- uint32_t temp = newPosition;
+ while (newPosCount > 0) {
+ size_t temp = newPosition;
mCbf(EVENT_NEW_POS, mUserData, &temp);
newPosition += updatePeriod;
+ newPosCount--;
+ }
+ if (mObservedSequence != sequence) {
+ mObservedSequence = sequence;
+ mCbf(EVENT_NEW_IAUDIORECORD, mUserData, NULL);
}
- do {
- audioBuffer.frameCount = frames;
- // Calling obtainBuffer() with a wait count of 1
- // limits wait time to WAIT_PERIOD_MS. This prevents from being
- // stuck here not being able to handle timed events (position, markers).
- status_t err = obtainBuffer(&audioBuffer, 1);
- if (err < NO_ERROR) {
- if (err != TIMED_OUT) {
- ALOGE_IF(err != status_t(NO_MORE_BUFFERS),
- "Error obtaining an audio buffer, giving up.");
- return false;
+ // if inactive, then don't run me again until re-started
+ if (!active) {
+ return NS_INACTIVE;
+ }
+
+ // Compute the estimated time until the next timed event (position, markers)
+ uint32_t minFrames = ~0;
+ if (!markerReached && position < markerPosition) {
+ minFrames = markerPosition - position;
+ }
+ if (updatePeriod > 0 && updatePeriod < minFrames) {
+ minFrames = updatePeriod;
+ }
+
+ // If > 0, poll periodically to recover from a stuck server. A good value is 2.
+ static const uint32_t kPoll = 0;
+ if (kPoll > 0 && mTransfer == TRANSFER_CALLBACK && kPoll * notificationFrames < minFrames) {
+ minFrames = kPoll * notificationFrames;
+ }
+
+ // Convert frame units to time units
+ nsecs_t ns = NS_WHENEVER;
+ if (minFrames != (uint32_t) ~0) {
+ // This "fudge factor" avoids soaking CPU, and compensates for late progress by server
+ static const nsecs_t kFudgeNs = 10000000LL; // 10 ms
+ ns = ((minFrames * 1000000000LL) / mSampleRate) + kFudgeNs;
+ }
+
+ // If not supplying data by EVENT_MORE_DATA, then we're done
+ if (mTransfer != TRANSFER_CALLBACK) {
+ return ns;
+ }
+
+ struct timespec timeout;
+ const struct timespec *requested = &ClientProxy::kForever;
+ if (ns != NS_WHENEVER) {
+ timeout.tv_sec = ns / 1000000000LL;
+ timeout.tv_nsec = ns % 1000000000LL;
+ ALOGV("timeout %ld.%03d", timeout.tv_sec, (int) timeout.tv_nsec / 1000000);
+ requested = &timeout;
+ }
+
+ while (mRemainingFrames > 0) {
+
+ Buffer audioBuffer;
+ audioBuffer.frameCount = mRemainingFrames;
+ size_t nonContig;
+ status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig);
+ LOG_ALWAYS_FATAL_IF((err != NO_ERROR) != (audioBuffer.frameCount == 0),
+ "obtainBuffer() err=%d frameCount=%u", err, audioBuffer.frameCount);
+ requested = &ClientProxy::kNonBlocking;
+ size_t avail = audioBuffer.frameCount + nonContig;
+ ALOGV("obtainBuffer(%u) returned %u = %u + %u",
+ mRemainingFrames, avail, audioBuffer.frameCount, nonContig);
+ if (err != NO_ERROR) {
+ if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) {
+ break;
+ }
+ ALOGE("Error %d obtaining an audio buffer, giving up.", err);
+ return NS_NEVER;
+ }
+
+ if (mRetryOnPartialBuffer) {
+ mRetryOnPartialBuffer = false;
+ if (avail < mRemainingFrames) {
+ int64_t myns = ((mRemainingFrames - avail) *
+ 1100000000LL) / mSampleRate;
+ if (ns < 0 || myns < ns) {
+ ns = myns;
+ }
+ return ns;
}
- break;
}
- if (err == status_t(STOPPED)) return false;
size_t reqSize = audioBuffer.size;
mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
- readSize = audioBuffer.size;
+ size_t readSize = audioBuffer.size;
// Sanity check on returned size
- if (ssize_t(readSize) <= 0) {
- // The callback is done filling buffers
+ if (ssize_t(readSize) < 0 || readSize > reqSize) {
+ ALOGE("EVENT_MORE_DATA requested %u bytes but callback returned %d bytes",
+ reqSize, (int) readSize);
+ return NS_NEVER;
+ }
+
+ if (readSize == 0) {
+ // The callback is done consuming buffers
// Keep this thread going to handle timed events and
- // still try to get more data in intervals of WAIT_PERIOD_MS
+ // still try to provide more data in intervals of WAIT_PERIOD_MS
// but don't just loop and block the CPU, so wait
- usleep(WAIT_PERIOD_MS*1000);
- break;
+ return WAIT_PERIOD_MS * 1000000LL;
}
- if (readSize > reqSize) readSize = reqSize;
- audioBuffer.size = readSize;
- audioBuffer.frameCount = readSize/frameSize();
- frames -= audioBuffer.frameCount;
+ size_t releasedFrames = readSize / mFrameSize;
+ audioBuffer.frameCount = releasedFrames;
+ mRemainingFrames -= releasedFrames;
+ if (misalignment >= releasedFrames) {
+ misalignment -= releasedFrames;
+ } else {
+ misalignment = 0;
+ }
releaseBuffer(&audioBuffer);
- } while (frames);
+ // FIXME here is where we would repeat EVENT_MORE_DATA again on same advanced buffer
+ // if callback doesn't like to accept the full chunk
+ if (readSize < reqSize) {
+ continue;
+ }
+ // There could be enough non-contiguous frames available to satisfy the remaining request
+ if (mRemainingFrames <= nonContig) {
+ continue;
+ }
- // Manage overrun callback
- if (active && (mProxy->framesAvailable() == 0)) {
- // The value of active is stale, but we are almost sure to be active here because
- // otherwise we would have exited when obtainBuffer returned STOPPED earlier.
- ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
- if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) {
- mCbf(EVENT_OVERRUN, mUserData, NULL);
+#if 0
+ // This heuristic tries to collapse a series of EVENT_MORE_DATA that would total to a
+ // sum <= notificationFrames. It replaces that series by at most two EVENT_MORE_DATA
+ // that total to a sum == notificationFrames.
+ if (0 < misalignment && misalignment <= mRemainingFrames) {
+ mRemainingFrames = misalignment;
+ return (mRemainingFrames * 1100000000LL) / mSampleRate;
}
- }
+#endif
- if (frames == 0) {
- mRemainingFrames = mNotificationFrames;
- } else {
- mRemainingFrames = frames;
}
- return true;
+ mRemainingFrames = notificationFrames;
+ mRetryOnPartialBuffer = true;
+
+ // A lot has transpired since ns was calculated, so run again immediately and re-calculate
+ return 0;
}
-// must be called with mLock and cblk.lock held. Callers must also hold strong references on
-// the IAudioRecord and IMemory in case they are recreated here.
-// If the IAudioRecord is successfully restored, the cblk pointer is updated
-status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& refCblk)
+status_t AudioRecord::restoreRecord_l(const char *from)
{
+ ALOGW("dead IAudioRecord, creating a new one from %s()", from);
+ ++mSequence;
status_t result;
- audio_track_cblk_t* cblk = refCblk;
- audio_track_cblk_t* newCblk = cblk;
- ALOGW("dead IAudioRecord, creating a new one");
-
- // signal old cblk condition so that other threads waiting for available buffers stop
- // waiting now
- cblk->cv.broadcast();
- cblk->lock.unlock();
-
// if the new IAudioRecord is created, openRecord_l() will modify the
// following member variables: mAudioRecord, mCblkMemory and mCblk.
// It will also delete the strong references on previous IAudioRecord and IMemory
- result = openRecord_l(mSampleRate, mFormat, mFrameCount, getInput_l());
+ size_t position = mProxy->getPosition();
+ mNewPosition = position + mUpdatePeriod;
+ result = openRecord_l(mSampleRate, mFormat, mFrameCount, getInput_l(), position);
if (result == NO_ERROR) {
- newCblk = mCblk;
- // callback thread or sync event hasn't changed
- result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0);
+ if (mActive) {
+ // callback thread or sync event hasn't changed
+ // FIXME this fails if we have a new AudioFlinger instance
+ result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0);
+ }
}
if (result != NO_ERROR) {
+ ALOGW("restoreRecord_l() failed status %d", result);
mActive = false;
}
- ALOGV("restoreRecord_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x",
- result, mActive, newCblk, cblk, newCblk->flags, cblk->flags);
-
- if (result == NO_ERROR) {
- // from now on we switch to the newly created cblk
- refCblk = newCblk;
- }
- newCblk->lock.lock();
+ return result;
+}
- ALOGW_IF(result != NO_ERROR, "restoreRecord_l() error %d", result);
+// =========================================================================
- return result;
+void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who)
+{
+ sp<AudioRecord> audioRecord = mAudioRecord.promote();
+ if (audioRecord != 0) {
+ AutoMutex lock(audioRecord->mLock);
+ audioRecord->mProxy->binderDied();
+ }
}
// =========================================================================
AudioRecord::AudioRecordThread::AudioRecordThread(AudioRecord& receiver, bool bCanCallJava)
- : Thread(bCanCallJava), mReceiver(receiver), mPaused(true)
+ : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mResumeLatch(false)
{
}
@@ -842,10 +928,26 @@ bool AudioRecord::AudioRecordThread::threadLoop()
return true;
}
}
- if (!mReceiver.processAudioBuffer(this)) {
- pause();
+ nsecs_t ns = mReceiver.processAudioBuffer(this);
+ switch (ns) {
+ case 0:
+ return true;
+ case NS_WHENEVER:
+ sleep(1);
+ return true;
+ case NS_INACTIVE:
+ pauseConditional();
+ return true;
+ case NS_NEVER:
+ return false;
+ default:
+ LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %lld", ns);
+ struct timespec req;
+ req.tv_sec = ns / 1000000000LL;
+ req.tv_nsec = ns % 1000000000LL;
+ nanosleep(&req, NULL /*rem*/);
+ return true;
}
- return true;
}
void AudioRecord::AudioRecordThread::requestExit()
@@ -859,6 +961,17 @@ void AudioRecord::AudioRecordThread::pause()
{
AutoMutex _l(mMyLock);
mPaused = true;
+ mResumeLatch = false;
+}
+
+void AudioRecord::AudioRecordThread::pauseConditional()
+{
+ AutoMutex _l(mMyLock);
+ if (mResumeLatch) {
+ mResumeLatch = false;
+ } else {
+ mPaused = true;
+ }
}
void AudioRecord::AudioRecordThread::resume()
@@ -866,7 +979,10 @@ void AudioRecord::AudioRecordThread::resume()
AutoMutex _l(mMyLock);
if (mPaused) {
mPaused = false;
+ mResumeLatch = false;
mMyCond.signal();
+ } else {
+ mResumeLatch = true;
}
}
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 77fc6f6..faca054 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -19,31 +19,14 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioTrack"
-#include <stdint.h>
-#include <sys/types.h>
-#include <limits.h>
-
-#include <sched.h>
#include <sys/resource.h>
-
-#include <private/media/AudioTrackShared.h>
-
-#include <media/AudioSystem.h>
+#include <audio_utils/primitives.h>
+#include <binder/IPCThreadState.h>
#include <media/AudioTrack.h>
-
#include <utils/Log.h>
-#include <binder/Parcel.h>
-#include <binder/IPCThreadState.h>
-#include <utils/Timers.h>
-#include <utils/Atomic.h>
-
-#include <cutils/bitops.h>
-#include <cutils/compiler.h>
+#include <private/media/AudioTrackShared.h>
-#include <system/audio.h>
-#include <system/audio_policy.h>
-
-#include <audio_utils/primitives.h>
+#define WAIT_PERIOD_MS 10
namespace android {
// ---------------------------------------------------------------------------
@@ -82,7 +65,9 @@ status_t AudioTrack::getMinFrameCount(
// Ensure that buffer depth covers at least audio hardware latency
uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
- if (minBufCount < 2) minBufCount = 2;
+ if (minBufCount < 2) {
+ minBufCount = 2;
+ }
*frameCount = (sampleRate == 0) ? afFrameCount * minBufCount :
afFrameCount * minBufCount * sampleRate / afSampleRate;
@@ -97,8 +82,7 @@ AudioTrack::AudioTrack()
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mProxy(NULL)
+ mPreviousSchedulingGroup(SP_DEFAULT)
{
}
@@ -112,16 +96,16 @@ AudioTrack::AudioTrack(
callback_t cbf,
void* user,
int notificationFrames,
- int sessionId)
+ int sessionId,
+ transfer_type transferType)
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mProxy(NULL)
+ mPreviousSchedulingGroup(SP_DEFAULT)
{
mStatus = set(streamType, sampleRate, format, channelMask,
frameCount, flags, cbf, user, notificationFrames,
- 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId);
+ 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType);
}
AudioTrack::AudioTrack(
@@ -134,27 +118,20 @@ AudioTrack::AudioTrack(
callback_t cbf,
void* user,
int notificationFrames,
- int sessionId)
+ int sessionId,
+ transfer_type transferType)
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mProxy(NULL)
+ mPreviousSchedulingGroup(SP_DEFAULT)
{
- if (sharedBuffer == 0) {
- ALOGE("sharedBuffer must be non-0");
- mStatus = BAD_VALUE;
- return;
- }
mStatus = set(streamType, sampleRate, format, channelMask,
0 /*frameCount*/, flags, cbf, user, notificationFrames,
- sharedBuffer, false /*threadCanCallJava*/, sessionId);
+ sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType);
}
AudioTrack::~AudioTrack()
{
- ALOGV_IF(mSharedBuffer != 0, "Destructor sharedBuffer: %p", mSharedBuffer->pointer());
-
if (mStatus == NO_ERROR) {
// Make sure that callback function exits in the case where
// it is looping on buffer full condition in obtainBuffer().
@@ -165,11 +142,13 @@ AudioTrack::~AudioTrack()
mAudioTrackThread->requestExitAndWait();
mAudioTrackThread.clear();
}
- mAudioTrack.clear();
+ if (mAudioTrack != 0) {
+ mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this);
+ mAudioTrack.clear();
+ }
IPCThreadState::self()->flushCommands();
AudioSystem::releaseAudioSessionId(mSessionId);
}
- delete mProxy;
}
status_t AudioTrack::set(
@@ -184,8 +163,44 @@ status_t AudioTrack::set(
int notificationFrames,
const sp<IMemory>& sharedBuffer,
bool threadCanCallJava,
- int sessionId)
+ int sessionId,
+ transfer_type transferType)
{
+ switch (transferType) {
+ case TRANSFER_DEFAULT:
+ if (sharedBuffer != 0) {
+ transferType = TRANSFER_SHARED;
+ } else if (cbf == NULL || threadCanCallJava) {
+ transferType = TRANSFER_SYNC;
+ } else {
+ transferType = TRANSFER_CALLBACK;
+ }
+ break;
+ case TRANSFER_CALLBACK:
+ if (cbf == NULL || sharedBuffer != 0) {
+ ALOGE("Transfer type TRANSFER_CALLBACK but cbf == NULL || sharedBuffer != 0");
+ return BAD_VALUE;
+ }
+ break;
+ case TRANSFER_OBTAIN:
+ case TRANSFER_SYNC:
+ if (sharedBuffer != 0) {
+ ALOGE("Transfer type TRANSFER_OBTAIN but sharedBuffer != 0");
+ return BAD_VALUE;
+ }
+ break;
+ case TRANSFER_SHARED:
+ if (sharedBuffer == 0) {
+ ALOGE("Transfer type TRANSFER_SHARED but sharedBuffer == 0");
+ return BAD_VALUE;
+ }
+ break;
+ default:
+ ALOGE("Invalid transfer type %d", transferType);
+ return BAD_VALUE;
+ }
+ mTransfer = transferType;
+
// FIXME "int" here is legacy and will be replaced by size_t later
if (frameCountInt < 0) {
ALOGE("Invalid frame count %d", frameCountInt);
@@ -199,6 +214,7 @@ status_t AudioTrack::set(
ALOGV("set() streamType %d frameCount %u flags %04x", streamType, frameCount, flags);
AutoMutex lock(mLock);
+
if (mAudioTrack != 0) {
ALOGE("Track already in use");
return INVALID_OPERATION;
@@ -228,7 +244,7 @@ status_t AudioTrack::set(
// validate parameters
if (!audio_is_valid_format(format)) {
- ALOGE("Invalid format");
+ ALOGE("Invalid format %d", format);
return BAD_VALUE;
}
@@ -281,6 +297,7 @@ status_t AudioTrack::set(
mFrameCount = frameCount;
mReqFrameCount = frameCount;
mNotificationFramesReq = notificationFrames;
+ mNotificationFramesAct = 0;
mSessionId = sessionId;
mAuxEffectId = 0;
mFlags = flags;
@@ -298,7 +315,8 @@ status_t AudioTrack::set(
frameCount,
flags,
sharedBuffer,
- output);
+ output,
+ 0 /*epoch*/);
if (status != NO_ERROR) {
if (mAudioTrackThread != 0) {
@@ -309,20 +327,21 @@ status_t AudioTrack::set(
}
mStatus = NO_ERROR;
-
mStreamType = streamType;
mFormat = format;
-
mSharedBuffer = sharedBuffer;
- mActive = false;
+ mState = STATE_STOPPED;
mUserData = user;
- mLoopCount = 0;
+ mLoopPeriod = 0;
mMarkerPosition = 0;
mMarkerReached = false;
mNewPosition = 0;
mUpdatePeriod = 0;
- mFlushed = false;
AudioSystem::acquireAudioSessionId(mSessionId);
+ mSequence = 1;
+ mObservedSequence = mSequence;
+ mInUnderrun = false;
+
return NO_ERROR;
}
@@ -330,87 +349,45 @@ status_t AudioTrack::set(
void AudioTrack::start()
{
- sp<AudioTrackThread> t = mAudioTrackThread;
-
- ALOGV("start %p", this);
-
AutoMutex lock(mLock);
- // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
- // while we are accessing the cblk
- sp<IAudioTrack> audioTrack = mAudioTrack;
- sp<IMemory> iMem = mCblkMemory;
- audio_track_cblk_t* cblk = mCblk;
+ if (mState == STATE_ACTIVE) {
+ return;
+ }
- if (!mActive) {
- mFlushed = false;
- mActive = true;
- mNewPosition = cblk->server + mUpdatePeriod;
- cblk->lock.lock();
- cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
- cblk->waitTimeMs = 0;
- android_atomic_and(~CBLK_DISABLED, &cblk->flags);
- if (t != 0) {
- t->resume();
- } else {
- mPreviousPriority = getpriority(PRIO_PROCESS, 0);
- get_sched_policy(0, &mPreviousSchedulingGroup);
- androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
- }
+ mInUnderrun = true;
- ALOGV("start %p before lock cblk %p", this, cblk);
- status_t status = NO_ERROR;
- if (!(cblk->flags & CBLK_INVALID)) {
- cblk->lock.unlock();
- ALOGV("mAudioTrack->start()");
- status = mAudioTrack->start();
- cblk->lock.lock();
- if (status == DEAD_OBJECT) {
- android_atomic_or(CBLK_INVALID, &cblk->flags);
- }
- }
- if (cblk->flags & CBLK_INVALID) {
- audio_track_cblk_t* temp = cblk;
- status = restoreTrack_l(temp, true /*fromStart*/);
- cblk = temp;
- }
- cblk->lock.unlock();
- if (status != NO_ERROR) {
- ALOGV("start() failed");
- mActive = false;
- if (t != 0) {
- t->pause();
- } else {
- setpriority(PRIO_PROCESS, 0, mPreviousPriority);
- set_sched_policy(0, mPreviousSchedulingGroup);
- }
- }
+ State previousState = mState;
+ mState = STATE_ACTIVE;
+ if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
+ // reset current position as seen by client to 0
+ mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
}
+ mNewPosition = mProxy->getPosition() + mUpdatePeriod;
+ int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->flags);
-}
-
-void AudioTrack::stop()
-{
sp<AudioTrackThread> t = mAudioTrackThread;
+ if (t != 0) {
+ t->resume();
+ } else {
+ mPreviousPriority = getpriority(PRIO_PROCESS, 0);
+ get_sched_policy(0, &mPreviousSchedulingGroup);
+ androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
+ }
- ALOGV("stop %p", this);
-
- AutoMutex lock(mLock);
- if (mActive) {
- mActive = false;
- mCblk->cv.signal();
- mAudioTrack->stop();
- // Cancel loops (If we are in the middle of a loop, playback
- // would not stop until loopCount reaches 0).
- setLoop_l(0, 0, 0);
- // the playback head position will reset to 0, so if a marker is set, we need
- // to activate it again
- mMarkerReached = false;
- // Force flush if a shared buffer is used otherwise audioflinger
- // will not stop before end of buffer is reached.
- // It may be needed to make sure that we stop playback, likely in case looping is on.
- if (mSharedBuffer != 0) {
- flush_l();
+ status_t status = NO_ERROR;
+ if (!(flags & CBLK_INVALID)) {
+ status = mAudioTrack->start();
+ if (status == DEAD_OBJECT) {
+ flags |= CBLK_INVALID;
}
+ }
+ if (flags & CBLK_INVALID) {
+ status = restoreTrack_l("start");
+ }
+
+ if (status != NO_ERROR) {
+ ALOGE("start() status %d", status);
+ mState = previousState;
if (t != 0) {
t->pause();
} else {
@@ -419,57 +396,85 @@ void AudioTrack::stop()
}
}
+ // FIXME discarding status
+}
+
+void AudioTrack::stop()
+{
+ AutoMutex lock(mLock);
+ // FIXME pause then stop should not be a nop
+ if (mState != STATE_ACTIVE) {
+ return;
+ }
+
+ mState = STATE_STOPPED;
+ mProxy->interrupt();
+ mAudioTrack->stop();
+ // the playback head position will reset to 0, so if a marker is set, we need
+ // to activate it again
+ mMarkerReached = false;
+#if 0
+ // Force flush if a shared buffer is used otherwise audioflinger
+ // will not stop before end of buffer is reached.
+ // It may be needed to make sure that we stop playback, likely in case looping is on.
+ if (mSharedBuffer != 0) {
+ flush_l();
+ }
+#endif
+ sp<AudioTrackThread> t = mAudioTrackThread;
+ if (t != 0) {
+ t->pause();
+ } else {
+ setpriority(PRIO_PROCESS, 0, mPreviousPriority);
+ set_sched_policy(0, mPreviousSchedulingGroup);
+ }
}
bool AudioTrack::stopped() const
{
AutoMutex lock(mLock);
- return stopped_l();
+ return mState != STATE_ACTIVE;
}
void AudioTrack::flush()
{
+ if (mSharedBuffer != 0) {
+ return;
+ }
AutoMutex lock(mLock);
- if (!mActive && mSharedBuffer == 0) {
- flush_l();
+ if (mState == STATE_ACTIVE || mState == STATE_FLUSHED) {
+ return;
}
+ flush_l();
}
void AudioTrack::flush_l()
{
- ALOGV("flush");
- ALOG_ASSERT(!mActive);
+ ALOG_ASSERT(mState != STATE_ACTIVE);
// clear playback marker and periodic update counter
mMarkerPosition = 0;
mMarkerReached = false;
mUpdatePeriod = 0;
- mFlushed = true;
+ mState = STATE_FLUSHED;
+ mProxy->flush();
mAudioTrack->flush();
- // Release AudioTrack callback thread in case it was waiting for new buffers
- // in AudioTrack::obtainBuffer()
- mCblk->cv.signal();
}
void AudioTrack::pause()
{
- ALOGV("pause");
AutoMutex lock(mLock);
- if (mActive) {
- mActive = false;
- mCblk->cv.signal();
- mAudioTrack->pause();
+ if (mState != STATE_ACTIVE) {
+ return;
}
+ mState = STATE_PAUSED;
+ mProxy->interrupt();
+ mAudioTrack->pause();
}
status_t AudioTrack::setVolume(float left, float right)
{
- if (mStatus != NO_ERROR) {
- return mStatus;
- }
- ALOG_ASSERT(mProxy != NULL);
-
if (left < 0.0f || left > 1.0f || right < 0.0f || right > 1.0f) {
return BAD_VALUE;
}
@@ -490,18 +495,11 @@ status_t AudioTrack::setVolume(float volume)
status_t AudioTrack::setAuxEffectSendLevel(float level)
{
- ALOGV("setAuxEffectSendLevel(%f)", level);
-
- if (mStatus != NO_ERROR) {
- return mStatus;
- }
- ALOG_ASSERT(mProxy != NULL);
-
if (level < 0.0f || level > 1.0f) {
return BAD_VALUE;
}
- AutoMutex lock(mLock);
+ AutoMutex lock(mLock);
mSendLevel = level;
mProxy->setSendLevel(level);
@@ -511,18 +509,17 @@ status_t AudioTrack::setAuxEffectSendLevel(float level)
void AudioTrack::getAuxEffectSendLevel(float* level) const
{
if (level != NULL) {
- *level = mSendLevel;
+ *level = mSendLevel;
}
}
status_t AudioTrack::setSampleRate(uint32_t rate)
{
- uint32_t afSamplingRate;
-
if (mIsTimed) {
return INVALID_OPERATION;
}
+ uint32_t afSamplingRate;
if (AudioSystem::getOutputSamplingRate(&afSamplingRate, mStreamType) != NO_ERROR) {
return NO_INIT;
}
@@ -550,78 +547,44 @@ uint32_t AudioTrack::getSampleRate() const
status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
{
- AutoMutex lock(mLock);
- return setLoop_l(loopStart, loopEnd, loopCount);
-}
-
-// must be called with mLock held
-status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount)
-{
if (mSharedBuffer == 0 || mIsTimed) {
return INVALID_OPERATION;
}
- if (loopCount < 0 && loopCount != -1) {
- return BAD_VALUE;
- }
-
-#if 0
- // This will be for the new interpretation of loopStart and loopEnd
-
- if (loopCount != 0) {
- if (loopStart >= mFrameCount || loopEnd >= mFrameCount || loopStart >= loopEnd) {
- return BAD_VALUE;
- }
- uint32_t periodFrames = loopEnd - loopStart;
- if (periodFrames < PERIOD_FRAMES_MIN) {
- return BAD_VALUE;
- }
- }
-
- // The remainder of this code still uses the old interpretation
-#endif
-
- audio_track_cblk_t* cblk = mCblk;
-
- Mutex::Autolock _l(cblk->lock);
-
if (loopCount == 0) {
- cblk->loopStart = UINT_MAX;
- cblk->loopEnd = UINT_MAX;
- cblk->loopCount = 0;
- mLoopCount = 0;
- return NO_ERROR;
- }
-
- if (loopStart >= loopEnd ||
- loopEnd - loopStart > mFrameCount ||
- cblk->server > loopStart) {
- ALOGE("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, "
- "user %d", loopStart, loopEnd, loopCount, mFrameCount, cblk->user);
+ ;
+ } else if (loopCount >= -1 && loopStart < loopEnd && loopEnd <= mFrameCount &&
+ loopEnd - loopStart >= MIN_LOOP) {
+ ;
+ } else {
return BAD_VALUE;
}
- if ((mSharedBuffer != 0) && (loopEnd > mFrameCount)) {
- ALOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, "
- "framecount %d",
- loopStart, loopEnd, mFrameCount);
- return BAD_VALUE;
+ AutoMutex lock(mLock);
+ // See setPosition() regarding setting parameters such as loop points or position while active
+ if (mState == STATE_ACTIVE) {
+ return INVALID_OPERATION;
}
-
- cblk->loopStart = loopStart;
- cblk->loopEnd = loopEnd;
- cblk->loopCount = loopCount;
- mLoopCount = loopCount;
-
+ setLoop_l(loopStart, loopEnd, loopCount);
return NO_ERROR;
}
+void AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount)
+{
+ // FIXME If setting a loop also sets position to start of loop, then
+ // this is correct. Otherwise it should be removed.
+ mNewPosition = mProxy->getPosition() + mUpdatePeriod;
+ mLoopPeriod = loopCount != 0 ? loopEnd - loopStart : 0;
+ mStaticProxy->setLoop(loopStart, loopEnd, loopCount);
+}
+
status_t AudioTrack::setMarkerPosition(uint32_t marker)
{
if (mCbf == NULL) {
return INVALID_OPERATION;
}
+ AutoMutex lock(mLock);
mMarkerPosition = marker;
mMarkerReached = false;
@@ -634,6 +597,7 @@ status_t AudioTrack::getMarkerPosition(uint32_t *marker) const
return BAD_VALUE;
}
+ AutoMutex lock(mLock);
*marker = mMarkerPosition;
return NO_ERROR;
@@ -645,9 +609,8 @@ status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod)
return INVALID_OPERATION;
}
- uint32_t curPosition;
- getPosition(&curPosition);
- mNewPosition = curPosition + updatePeriod;
+ AutoMutex lock(mLock);
+ mNewPosition = mProxy->getPosition() + updatePeriod;
mUpdatePeriod = updatePeriod;
return NO_ERROR;
@@ -659,6 +622,7 @@ status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const
return BAD_VALUE;
}
+ AutoMutex lock(mLock);
*updatePeriod = mUpdatePeriod;
return NO_ERROR;
@@ -669,49 +633,44 @@ status_t AudioTrack::setPosition(uint32_t position)
if (mSharedBuffer == 0 || mIsTimed) {
return INVALID_OPERATION;
}
-
- AutoMutex lock(mLock);
-
- if (!stopped_l()) {
- return INVALID_OPERATION;
- }
-
-#if 0
- // This will be for the new interpretation of position
-
- if (position >= mFrameCount) {
+ if (position > mFrameCount) {
return BAD_VALUE;
}
- // The remainder of this code still uses the old interpretation
-#endif
-
- audio_track_cblk_t* cblk = mCblk;
- Mutex::Autolock _l(cblk->lock);
-
- if (position > cblk->user) {
- return BAD_VALUE;
+ AutoMutex lock(mLock);
+ // Currently we require that the player is inactive before setting parameters such as position
+ // or loop points. Otherwise, there could be a race condition: the application could read the
+ // current position, compute a new position or loop parameters, and then set that position or
+ // loop parameters but it would do the "wrong" thing since the position has continued to advance
+ // in the mean time. If we ever provide a sequencer in server, we could allow a way for the app
+ // to specify how it wants to handle such scenarios.
+ if (mState == STATE_ACTIVE) {
+ return INVALID_OPERATION;
}
-
- cblk->server = position;
- android_atomic_or(CBLK_FORCEREADY, &cblk->flags);
+ mNewPosition = mProxy->getPosition() + mUpdatePeriod;
+ mLoopPeriod = 0;
+ // FIXME Check whether loops and setting position are incompatible in old code.
+ // If we use setLoop for both purposes we lose the capability to set the position while looping.
+ mStaticProxy->setLoop(position, mFrameCount, 0);
return NO_ERROR;
}
-status_t AudioTrack::getPosition(uint32_t *position)
+status_t AudioTrack::getPosition(uint32_t *position) const
{
if (position == NULL) {
return BAD_VALUE;
}
+
AutoMutex lock(mLock);
- *position = mFlushed ? 0 : mCblk->server;
+ // IAudioTrack::stop() isn't synchronous; we don't know when presentation completes
+ *position = (mState == STATE_STOPPED || mState == STATE_FLUSHED) ? 0 :
+ mProxy->getPosition();
return NO_ERROR;
}
-#if 0
-status_t AudioTrack::getBufferPosition(uint32_t *position)
+status_t AudioTrack::getBufferPosition(size_t *position)
{
if (mSharedBuffer == 0 || mIsTimed) {
return INVALID_OPERATION;
@@ -719,33 +678,28 @@ status_t AudioTrack::getBufferPosition(uint32_t *position)
if (position == NULL) {
return BAD_VALUE;
}
- *position = 0;
+ AutoMutex lock(mLock);
+ *position = mStaticProxy->getBufferPosition();
return NO_ERROR;
}
-#endif
status_t AudioTrack::reload()
{
- if (mStatus != NO_ERROR) {
- return mStatus;
- }
- ALOG_ASSERT(mProxy != NULL);
-
if (mSharedBuffer == 0 || mIsTimed) {
return INVALID_OPERATION;
}
AutoMutex lock(mLock);
-
- if (!stopped_l()) {
+ // See setPosition() regarding setting parameters such as loop points or position while active
+ if (mState == STATE_ACTIVE) {
return INVALID_OPERATION;
}
-
- flush_l();
-
- (void) mProxy->stepUser(mFrameCount);
-
+ mNewPosition = mUpdatePeriod;
+ mLoopPeriod = 0;
+ // FIXME The new code cannot reload while keeping a loop specified.
+ // Need to check how the old code handled this, and whether it's a significant change.
+ mStaticProxy->setLoop(0, mFrameCount, 0);
return NO_ERROR;
}
@@ -764,7 +718,7 @@ audio_io_handle_t AudioTrack::getOutput_l()
status_t AudioTrack::attachAuxEffect(int effectId)
{
- ALOGV("attachAuxEffect(%d)", effectId);
+ AutoMutex lock(mLock);
status_t status = mAudioTrack->attachAuxEffect(effectId);
if (status == NO_ERROR) {
mAuxEffectId = effectId;
@@ -782,7 +736,8 @@ status_t AudioTrack::createTrack_l(
size_t frameCount,
audio_output_flags_t flags,
const sp<IMemory>& sharedBuffer,
- audio_io_handle_t output)
+ audio_io_handle_t output,
+ size_t epoch)
{
status_t status;
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
@@ -792,7 +747,8 @@ status_t AudioTrack::createTrack_l(
}
uint32_t afLatency;
- if (AudioSystem::getLatency(output, streamType, &afLatency) != NO_ERROR) {
+ if ((status = AudioSystem::getLatency(output, streamType, &afLatency)) != NO_ERROR) {
+ ALOGE("getLatency(%d) failed status %d", output, status);
return NO_INIT;
}
@@ -820,7 +776,10 @@ status_t AudioTrack::createTrack_l(
frameCount = sharedBuffer->size();
} else if (frameCount == 0) {
size_t afFrameCount;
- if (AudioSystem::getFrameCount(output, streamType, &afFrameCount) != NO_ERROR) {
+ status = AudioSystem::getFrameCount(output, streamType, &afFrameCount);
+ if (status != NO_ERROR) {
+ ALOGE("getFrameCount(output=%d, streamType=%d) status %d", output, streamType,
+ status);
return NO_INIT;
}
frameCount = afFrameCount;
@@ -851,11 +810,16 @@ status_t AudioTrack::createTrack_l(
// FIXME move these calculations and associated checks to server
uint32_t afSampleRate;
- if (AudioSystem::getSamplingRate(output, streamType, &afSampleRate) != NO_ERROR) {
+ status = AudioSystem::getSamplingRate(output, streamType, &afSampleRate);
+ if (status != NO_ERROR) {
+ ALOGE("getSamplingRate(output=%d, streamType=%d) status %d", output, streamType,
+ status);
return NO_INIT;
}
size_t afFrameCount;
- if (AudioSystem::getFrameCount(output, streamType, &afFrameCount) != NO_ERROR) {
+ status = AudioSystem::getFrameCount(output, streamType, &afFrameCount);
+ if (status != NO_ERROR) {
+ ALOGE("getFrameCount(output=%d, streamType=%d) status %d", output, streamType, status);
return NO_INIT;
}
@@ -875,12 +839,9 @@ status_t AudioTrack::createTrack_l(
if (frameCount == 0) {
frameCount = minFrameCount;
}
- if (mNotificationFramesAct == 0) {
- mNotificationFramesAct = frameCount/2;
- }
// Make sure that application is notified with sufficient margin
// before underrun
- if (mNotificationFramesAct > frameCount/2) {
+ if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) {
mNotificationFramesAct = frameCount/2;
}
if (frameCount < minFrameCount) {
@@ -930,6 +891,10 @@ status_t AudioTrack::createTrack_l(
ALOGE("Could not get control block");
return NO_INIT;
}
+ if (mAudioTrack != 0) {
+ mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this);
+ mDeathNotifier.clear();
+ }
mAudioTrack = track;
mCblkMemory = iMem;
audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMem->pointer());
@@ -947,26 +912,38 @@ status_t AudioTrack::createTrack_l(
if (trackFlags & IAudioFlinger::TRACK_FAST) {
ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", frameCount);
mAwaitBoost = true;
+ if (sharedBuffer == 0) {
+ // double-buffering is not required for fast tracks, due to tighter scheduling
+ if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount) {
+ mNotificationFramesAct = frameCount;
+ }
+ }
} else {
ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %u", frameCount);
// once denied, do not request again if IAudioTrack is re-created
flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_FAST);
mFlags = flags;
- }
- if (sharedBuffer == 0) {
- mNotificationFramesAct = frameCount/2;
+ if (sharedBuffer == 0) {
+ if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) {
+ mNotificationFramesAct = frameCount/2;
+ }
+ }
}
}
+ mRefreshRemaining = true;
+
+ // Starting address of buffers in shared memory. If there is a shared buffer, buffers
+ // is the value of pointer() for the shared buffer, otherwise buffers points
+ // immediately after the control block. This address is for the mapping within client
+ // address space. AudioFlinger::TrackBase::mBuffer is for the server address space.
+ void* buffers;
if (sharedBuffer == 0) {
- mBuffers = (char*)cblk + sizeof(audio_track_cblk_t);
+ buffers = (char*)cblk + sizeof(audio_track_cblk_t);
} else {
- mBuffers = sharedBuffer->pointer();
+ buffers = sharedBuffer->pointer();
}
mAudioTrack->attachAuxEffect(mAuxEffectId);
- cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
- cblk->waitTimeMs = 0;
- mRemainingFrames = mNotificationFramesAct;
// FIXME don't believe this lie
mLatency = afLatency + (1000*frameCount) / sampleRate;
mFrameCount = frameCount;
@@ -977,147 +954,143 @@ status_t AudioTrack::createTrack_l(
}
// update proxy
- delete mProxy;
- mProxy = new AudioTrackClientProxy(cblk, mBuffers, frameCount, mFrameSizeAF);
+ if (sharedBuffer == 0) {
+ mStaticProxy.clear();
+ mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
+ } else {
+ mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
+ mProxy = mStaticProxy;
+ }
mProxy->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) |
uint16_t(mVolume[LEFT] * 0x1000));
mProxy->setSendLevel(mSendLevel);
mProxy->setSampleRate(mSampleRate);
- if (sharedBuffer != 0) {
- // Force buffer full condition as data is already present in shared memory
- mProxy->stepUser(frameCount);
- }
+ mProxy->setEpoch(epoch);
+ mProxy->setMinimum(mNotificationFramesAct);
+
+ mDeathNotifier = new DeathNotifier(this);
+ mAudioTrack->asBinder()->linkToDeath(mDeathNotifier, this);
return NO_ERROR;
}
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
- ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+ if (audioBuffer == NULL) {
+ return BAD_VALUE;
+ }
+ if (mTransfer != TRANSFER_OBTAIN) {
+ audioBuffer->frameCount = 0;
+ audioBuffer->size = 0;
+ audioBuffer->raw = NULL;
+ return INVALID_OPERATION;
+ }
+
+ const struct timespec *requested;
+ if (waitCount == -1) {
+ requested = &ClientProxy::kForever;
+ } else if (waitCount == 0) {
+ requested = &ClientProxy::kNonBlocking;
+ } else if (waitCount > 0) {
+ long long ms = WAIT_PERIOD_MS * (long long) waitCount;
+ struct timespec timeout;
+ timeout.tv_sec = ms / 1000;
+ timeout.tv_nsec = (int) (ms % 1000) * 1000000;
+ requested = &timeout;
+ } else {
+ ALOGE("%s invalid waitCount %d", __func__, waitCount);
+ requested = NULL;
+ }
+ return obtainBuffer(audioBuffer, requested);
+}
+
+status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
+ struct timespec *elapsed, size_t *nonContig)
+{
+ // previous and new IAudioTrack sequence numbers are used to detect track re-creation
+ uint32_t oldSequence = 0;
+ uint32_t newSequence;
- AutoMutex lock(mLock);
- bool active;
- status_t result = NO_ERROR;
- audio_track_cblk_t* cblk = mCblk;
- uint32_t framesReq = audioBuffer->frameCount;
- uint32_t waitTimeMs = (waitCount < 0) ? cblk->bufferTimeoutMs : WAIT_PERIOD_MS;
+ Proxy::Buffer buffer;
+ status_t status = NO_ERROR;
- audioBuffer->frameCount = 0;
- audioBuffer->size = 0;
+ static const int32_t kMaxTries = 5;
+ int32_t tryCounter = kMaxTries;
- size_t framesAvail = mProxy->framesAvailable();
+ do {
+ // obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to
+ // keep them from going away if another thread re-creates the track during obtainBuffer()
+ sp<AudioTrackClientProxy> proxy;
+ sp<IMemory> iMem;
- cblk->lock.lock();
- if (cblk->flags & CBLK_INVALID) {
- goto create_new_track;
- }
- cblk->lock.unlock();
-
- if (framesAvail == 0) {
- cblk->lock.lock();
- goto start_loop_here;
- while (framesAvail == 0) {
- active = mActive;
- if (CC_UNLIKELY(!active)) {
- ALOGV("Not active and NO_MORE_BUFFERS");
- cblk->lock.unlock();
- return NO_MORE_BUFFERS;
- }
- if (CC_UNLIKELY(!waitCount)) {
- cblk->lock.unlock();
- return WOULD_BLOCK;
- }
- if (!(cblk->flags & CBLK_INVALID)) {
- mLock.unlock();
- // this condition is in shared memory, so if IAudioTrack and control block
- // are replaced due to mediaserver death or IAudioTrack invalidation then
- // cv won't be signalled, but fortunately the timeout will limit the wait
- result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
- cblk->lock.unlock();
- mLock.lock();
- if (!mActive) {
- return status_t(STOPPED);
- }
- // IAudioTrack may have been re-created while mLock was unlocked
- cblk = mCblk;
- cblk->lock.lock();
- }
+ { // start of lock scope
+ AutoMutex lock(mLock);
- if (cblk->flags & CBLK_INVALID) {
- goto create_new_track;
- }
- if (CC_UNLIKELY(result != NO_ERROR)) {
- cblk->waitTimeMs += waitTimeMs;
- if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) {
- // timing out when a loop has been set and we have already written upto loop end
- // is a normal condition: no need to wake AudioFlinger up.
- if (cblk->user < cblk->loopEnd) {
- ALOGW("obtainBuffer timed out (is the CPU pegged?) %p name=%#x user=%08x, "
- "server=%08x", this, cblk->mName, cblk->user, cblk->server);
- //unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140)
- cblk->lock.unlock();
- result = mAudioTrack->start();
- cblk->lock.lock();
- if (result == DEAD_OBJECT) {
- android_atomic_or(CBLK_INVALID, &cblk->flags);
-create_new_track:
- audio_track_cblk_t* temp = cblk;
- result = restoreTrack_l(temp, false /*fromStart*/);
- cblk = temp;
- }
- if (result != NO_ERROR) {
- ALOGW("obtainBuffer create Track error %d", result);
- cblk->lock.unlock();
- return result;
- }
+ newSequence = mSequence;
+ // did previous obtainBuffer() fail due to media server death or voluntary invalidation?
+ if (status == DEAD_OBJECT) {
+ // re-create track, unless someone else has already done so
+ if (newSequence == oldSequence) {
+ status = restoreTrack_l("obtainBuffer");
+ if (status != NO_ERROR) {
+ break;
}
- cblk->waitTimeMs = 0;
}
+ }
+ oldSequence = newSequence;
- if (--waitCount == 0) {
- cblk->lock.unlock();
- return TIMED_OUT;
- }
+ // Keep the extra references
+ proxy = mProxy;
+ iMem = mCblkMemory;
+
+ // Non-blocking if track is stopped or paused
+ if (mState != STATE_ACTIVE) {
+ requested = &ClientProxy::kNonBlocking;
}
- // read the server count again
- start_loop_here:
- framesAvail = mProxy->framesAvailable_l();
- }
- cblk->lock.unlock();
- }
- cblk->waitTimeMs = 0;
+ } // end of lock scope
- if (framesReq > framesAvail) {
- framesReq = framesAvail;
- }
+ buffer.mFrameCount = audioBuffer->frameCount;
+ // FIXME starts the requested timeout and elapsed over from scratch
+ status = proxy->obtainBuffer(&buffer, requested, elapsed);
- uint32_t u = cblk->user;
- uint32_t bufferEnd = cblk->userBase + mFrameCount;
+ } while ((status == DEAD_OBJECT) && (tryCounter-- > 0));
- if (framesReq > bufferEnd - u) {
- framesReq = bufferEnd - u;
+ audioBuffer->frameCount = buffer.mFrameCount;
+ audioBuffer->size = buffer.mFrameCount * mFrameSizeAF;
+ audioBuffer->raw = buffer.mRaw;
+ if (nonContig != NULL) {
+ *nonContig = buffer.mNonContig;
}
-
- audioBuffer->frameCount = framesReq;
- audioBuffer->size = framesReq * mFrameSizeAF;
- audioBuffer->raw = mProxy->buffer(u);
- active = mActive;
- return active ? status_t(NO_ERROR) : status_t(STOPPED);
+ return status;
}
void AudioTrack::releaseBuffer(Buffer* audioBuffer)
{
- ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+ if (mTransfer == TRANSFER_SHARED) {
+ return;
+ }
+
+ size_t stepCount = audioBuffer->size / mFrameSizeAF;
+ if (stepCount == 0) {
+ return;
+ }
+
+ Proxy::Buffer buffer;
+ buffer.mFrameCount = stepCount;
+ buffer.mRaw = audioBuffer->raw;
AutoMutex lock(mLock);
- audio_track_cblk_t* cblk = mCblk;
- (void) mProxy->stepUser(audioBuffer->frameCount);
- if (audioBuffer->frameCount > 0) {
- // restart track if it was disabled by audioflinger due to previous underrun
- if (mActive && (cblk->flags & CBLK_DISABLED)) {
- android_atomic_and(~CBLK_DISABLED, &cblk->flags);
- ALOGW("releaseBuffer() track %p name=%#x disabled, restarting", this, cblk->mName);
+ mInUnderrun = false;
+ mProxy->releaseBuffer(&buffer);
+
+ // restart track if it was disabled by audioflinger due to previous underrun
+ if (mState == STATE_ACTIVE) {
+ audio_track_cblk_t* cblk = mCblk;
+ if (android_atomic_and(~CBLK_DISABLED, &cblk->flags) & CBLK_DISABLED) {
+ ALOGW("releaseBuffer() track %p name=%#x disabled due to previous underrun, restarting",
+ this, cblk->mName);
+ // FIXME ignoring status
mAudioTrack->start();
}
}
@@ -1127,68 +1100,46 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer)
ssize_t AudioTrack::write(const void* buffer, size_t userSize)
{
-
- if (mSharedBuffer != 0 || mIsTimed) {
+ if (mTransfer != TRANSFER_SYNC || mIsTimed) {
return INVALID_OPERATION;
}
- if (ssize_t(userSize) < 0) {
+ if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
// Sanity-check: user is most-likely passing an error code, and it would
// make the return value ambiguous (actualSize vs error).
- ALOGE("AudioTrack::write(buffer=%p, size=%u (%d)",
- buffer, userSize, userSize);
+ ALOGE("AudioTrack::write(buffer=%p, size=%u (%d)", buffer, userSize, userSize);
return BAD_VALUE;
}
- ALOGV("write %p: %d bytes, mActive=%d", this, userSize, mActive);
-
- if (userSize == 0) {
- return 0;
- }
-
- // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
- // while we are accessing the cblk
- mLock.lock();
- sp<IAudioTrack> audioTrack = mAudioTrack;
- sp<IMemory> iMem = mCblkMemory;
- mLock.unlock();
-
- // since mLock is unlocked the IAudioTrack and shared memory may be re-created,
- // so all cblk references might still refer to old shared memory, but that should be benign
-
- ssize_t written = 0;
- const int8_t *src = (const int8_t *)buffer;
+ size_t written = 0;
Buffer audioBuffer;
- size_t frameSz = frameSize();
- do {
- audioBuffer.frameCount = userSize/frameSz;
+ while (userSize >= mFrameSize) {
+ audioBuffer.frameCount = userSize / mFrameSize;
- status_t err = obtainBuffer(&audioBuffer, -1);
+ status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever);
if (err < 0) {
- // out of buffers, return #bytes written
- if (err == status_t(NO_MORE_BUFFERS)) {
+ if (written > 0) {
break;
}
return ssize_t(err);
}
size_t toWrite;
-
if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
// Divide capacity by 2 to take expansion into account
- toWrite = audioBuffer.size>>1;
- memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) src, toWrite);
+ toWrite = audioBuffer.size >> 1;
+ memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) buffer, toWrite);
} else {
toWrite = audioBuffer.size;
- memcpy(audioBuffer.i8, src, toWrite);
+ memcpy(audioBuffer.i8, buffer, toWrite);
}
- src += toWrite;
+ buffer = ((const char *) buffer) + toWrite;
userSize -= toWrite;
written += toWrite;
releaseBuffer(&audioBuffer);
- } while (userSize >= frameSz);
+ }
return written;
}
@@ -1204,10 +1155,12 @@ status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp<IMemory>* buffer)
AutoMutex lock(mLock);
status_t result = UNKNOWN_ERROR;
+#if 1
// acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
// while we are accessing the cblk
sp<IAudioTrack> audioTrack = mAudioTrack;
sp<IMemory> iMem = mCblkMemory;
+#endif
// If the track is not invalid already, try to allocate a buffer. alloc
// fails indicating that the server is dead, flag the track as invalid so
@@ -1223,13 +1176,9 @@ status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp<IMemory>* buffer)
// If the track is invalid at this point, attempt to restore it. and try the
// allocation one more time.
if (cblk->flags & CBLK_INVALID) {
- cblk->lock.lock();
- audio_track_cblk_t* temp = cblk;
- result = restoreTrack_l(temp, false /*fromStart*/);
- cblk = temp;
- cblk->lock.unlock();
+ result = restoreTrack_l("allocateTimedBuffer");
- if (result == OK) {
+ if (result == NO_ERROR) {
result = mAudioTrack->allocateTimedBuffer(size, buffer);
}
}
@@ -1246,9 +1195,10 @@ status_t TimedAudioTrack::queueTimedBuffer(const sp<IMemory>& buffer,
audio_track_cblk_t* cblk = mCblk;
// restart track if it was disabled by audioflinger due to previous underrun
if (buffer->size() != 0 && status == NO_ERROR &&
- mActive && (cblk->flags & CBLK_DISABLED)) {
+ (mState == STATE_ACTIVE) && (cblk->flags & CBLK_DISABLED)) {
android_atomic_and(~CBLK_DISABLED, &cblk->flags);
ALOGW("queueTimedBuffer() track %p disabled, restarting", this);
+ // FIXME ignoring status
mAudioTrack->start();
}
}
@@ -1263,12 +1213,8 @@ status_t TimedAudioTrack::setMediaTimeTransform(const LinearTransform& xform,
// -------------------------------------------------------------------------
-bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
+nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
{
- Buffer audioBuffer;
- uint32_t frames;
- size_t writtenSize;
-
mLock.lock();
if (mAwaitBoost) {
mAwaitBoost = false;
@@ -1289,86 +1235,181 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
}
return true;
}
- // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
- // while we are accessing the cblk
- sp<IAudioTrack> audioTrack = mAudioTrack;
- sp<IMemory> iMem = mCblkMemory;
- audio_track_cblk_t* cblk = mCblk;
- bool active = mActive;
- mLock.unlock();
- // since mLock is unlocked the IAudioTrack and shared memory may be re-created,
- // so all cblk references might still refer to old shared memory, but that should be benign
+ // Can only reference mCblk while locked
+ int32_t flags = android_atomic_and(
+ ~(CBLK_UNDERRUN | CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END), &mCblk->flags);
- // Manage underrun callback
- if (active && (mProxy->framesAvailable() == mFrameCount)) {
- ALOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
- if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) {
- mCbf(EVENT_UNDERRUN, mUserData, 0);
- if (cblk->server == mFrameCount) {
- mCbf(EVENT_BUFFER_END, mUserData, 0);
- }
- if (mSharedBuffer != 0) {
- return false;
- }
- }
+ // Check for track invalidation
+ if (flags & CBLK_INVALID) {
+ (void) restoreTrack_l("processAudioBuffer");
+ mLock.unlock();
+ // Run again immediately, but with a new IAudioTrack
+ return 0;
}
- // Manage loop end callback
- while (mLoopCount > cblk->loopCount) {
- int loopCount = -1;
- mLoopCount--;
- if (mLoopCount >= 0) loopCount = mLoopCount;
+ bool active = mState == STATE_ACTIVE;
- mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount);
+ // Manage underrun callback, must be done under lock to avoid race with releaseBuffer()
+ bool newUnderrun = false;
+ if (flags & CBLK_UNDERRUN) {
+#if 0
+ // Currently in shared buffer mode, when the server reaches the end of buffer,
+ // the track stays active in continuous underrun state. It's up to the application
+ // to pause or stop the track, or set the position to a new offset within buffer.
+ // This was some experimental code to auto-pause on underrun. Keeping it here
+ // in "if 0" so we can re-visit this if we add a real sequencer for shared memory content.
+ if (mTransfer == TRANSFER_SHARED) {
+ mState = STATE_PAUSED;
+ active = false;
+ }
+#endif
+ if (!mInUnderrun) {
+ mInUnderrun = true;
+ newUnderrun = true;
+ }
}
+ // Get current position of server
+ size_t position = mProxy->getPosition();
+
// Manage marker callback
- if (!mMarkerReached && (mMarkerPosition > 0)) {
- if (cblk->server >= mMarkerPosition) {
- mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
- mMarkerReached = true;
- }
+ bool markerReached = false;
+ size_t markerPosition = mMarkerPosition;
+ // FIXME fails for wraparound, need 64 bits
+ if (!mMarkerReached && (markerPosition > 0) && (position >= markerPosition)) {
+ mMarkerReached = markerReached = true;
+ }
+
+ // Determine number of new position callback(s) that will be needed, while locked
+ size_t newPosCount = 0;
+ size_t newPosition = mNewPosition;
+ size_t updatePeriod = mUpdatePeriod;
+ // FIXME fails for wraparound, need 64 bits
+ if (updatePeriod > 0 && position >= newPosition) {
+ newPosCount = ((position - newPosition) / updatePeriod) + 1;
+ mNewPosition += updatePeriod * newPosCount;
+ }
+
+ // Cache other fields that will be needed soon
+ uint32_t loopPeriod = mLoopPeriod;
+ uint32_t sampleRate = mSampleRate;
+ size_t notificationFrames = mNotificationFramesAct;
+ if (mRefreshRemaining) {
+ mRefreshRemaining = false;
+ mRemainingFrames = notificationFrames;
+ mRetryOnPartialBuffer = false;
+ }
+ size_t misalignment = mProxy->getMisalignment();
+ int32_t sequence = mSequence;
+
+ // These fields don't need to be cached, because they are assigned only by set():
+ // mTransfer, mCbf, mUserData, mFormat, mFrameSize, mFrameSizeAF, mFlags
+ // mFlags is also assigned by createTrack_l(), but not the bit we care about.
+
+ mLock.unlock();
+
+ // perform callbacks while unlocked
+ if (newUnderrun) {
+ mCbf(EVENT_UNDERRUN, mUserData, NULL);
+ }
+ // FIXME we will miss loops if loop cycle was signaled several times since last call
+ // to processAudioBuffer()
+ if (flags & (CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL)) {
+ mCbf(EVENT_LOOP_END, mUserData, NULL);
+ }
+ if (flags & CBLK_BUFFER_END) {
+ mCbf(EVENT_BUFFER_END, mUserData, NULL);
+ }
+ if (markerReached) {
+ mCbf(EVENT_MARKER, mUserData, &markerPosition);
+ }
+ while (newPosCount > 0) {
+ size_t temp = newPosition;
+ mCbf(EVENT_NEW_POS, mUserData, &temp);
+ newPosition += updatePeriod;
+ newPosCount--;
+ }
+ if (mObservedSequence != sequence) {
+ mObservedSequence = sequence;
+ mCbf(EVENT_NEW_IAUDIOTRACK, mUserData, NULL);
}
- // Manage new position callback
- if (mUpdatePeriod > 0) {
- while (cblk->server >= mNewPosition) {
- mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
- mNewPosition += mUpdatePeriod;
- }
+ // if inactive, then don't run me again until re-started
+ if (!active) {
+ return NS_INACTIVE;
}
- // If Shared buffer is used, no data is requested from client.
- if (mSharedBuffer != 0) {
- frames = 0;
- } else {
- frames = mRemainingFrames;
+ // Compute the estimated time until the next timed event (position, markers, loops)
+ // FIXME only for non-compressed audio
+ uint32_t minFrames = ~0;
+ if (!markerReached && position < markerPosition) {
+ minFrames = markerPosition - position;
+ }
+ if (loopPeriod > 0 && loopPeriod < minFrames) {
+ minFrames = loopPeriod;
+ }
+ if (updatePeriod > 0 && updatePeriod < minFrames) {
+ minFrames = updatePeriod;
}
- // See description of waitCount parameter at declaration of obtainBuffer().
- // The logic below prevents us from being stuck below at obtainBuffer()
- // not being able to handle timed events (position, markers, loops).
- int32_t waitCount = -1;
- if (mUpdatePeriod || (!mMarkerReached && mMarkerPosition) || mLoopCount) {
- waitCount = 1;
+ // If > 0, poll periodically to recover from a stuck server. A good value is 2.
+ static const uint32_t kPoll = 0;
+ if (kPoll > 0 && mTransfer == TRANSFER_CALLBACK && kPoll * notificationFrames < minFrames) {
+ minFrames = kPoll * notificationFrames;
}
- do {
+ // Convert frame units to time units
+ nsecs_t ns = NS_WHENEVER;
+ if (minFrames != (uint32_t) ~0) {
+ // This "fudge factor" avoids soaking CPU, and compensates for late progress by server
+ static const nsecs_t kFudgeNs = 10000000LL; // 10 ms
+ ns = ((minFrames * 1000000000LL) / sampleRate) + kFudgeNs;
+ }
+
+ // If not supplying data by EVENT_MORE_DATA, then we're done
+ if (mTransfer != TRANSFER_CALLBACK) {
+ return ns;
+ }
- audioBuffer.frameCount = frames;
+ struct timespec timeout;
+ const struct timespec *requested = &ClientProxy::kForever;
+ if (ns != NS_WHENEVER) {
+ timeout.tv_sec = ns / 1000000000LL;
+ timeout.tv_nsec = ns % 1000000000LL;
+ ALOGV("timeout %ld.%03d", timeout.tv_sec, (int) timeout.tv_nsec / 1000000);
+ requested = &timeout;
+ }
+
+ while (mRemainingFrames > 0) {
- status_t err = obtainBuffer(&audioBuffer, waitCount);
- if (err < NO_ERROR) {
- if (err != TIMED_OUT) {
- ALOGE_IF(err != status_t(NO_MORE_BUFFERS),
- "Error obtaining an audio buffer, giving up.");
- return false;
+ Buffer audioBuffer;
+ audioBuffer.frameCount = mRemainingFrames;
+ size_t nonContig;
+ status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig);
+ LOG_ALWAYS_FATAL_IF((err != NO_ERROR) != (audioBuffer.frameCount == 0),
+ "obtainBuffer() err=%d frameCount=%u", err, audioBuffer.frameCount);
+ requested = &ClientProxy::kNonBlocking;
+ size_t avail = audioBuffer.frameCount + nonContig;
+ ALOGV("obtainBuffer(%u) returned %u = %u + %u",
+ mRemainingFrames, avail, audioBuffer.frameCount, nonContig);
+ if (err != NO_ERROR) {
+ if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) {
+ return 0;
}
- break;
+ ALOGE("Error %d obtaining an audio buffer, giving up.", err);
+ return NS_NEVER;
}
- if (err == status_t(STOPPED)) {
- return false;
+
+ if (mRetryOnPartialBuffer) {
+ mRetryOnPartialBuffer = false;
+ if (avail < mRemainingFrames) {
+ int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate;
+ if (ns < 0 || myns < ns) {
+ ns = myns;
+ }
+ return ns;
+ }
}
// Divide buffer size by 2 to take into account the expansion
@@ -1380,66 +1421,76 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
size_t reqSize = audioBuffer.size;
mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
- writtenSize = audioBuffer.size;
+ size_t writtenSize = audioBuffer.size;
+ size_t writtenFrames = writtenSize / mFrameSize;
// Sanity check on returned size
- if (ssize_t(writtenSize) <= 0) {
+ if (ssize_t(writtenSize) < 0 || writtenSize > reqSize) {
+ ALOGE("EVENT_MORE_DATA requested %u bytes but callback returned %d bytes",
+ reqSize, (int) writtenSize);
+ return NS_NEVER;
+ }
+
+ if (writtenSize == 0) {
// The callback is done filling buffers
// Keep this thread going to handle timed events and
// still try to get more data in intervals of WAIT_PERIOD_MS
// but don't just loop and block the CPU, so wait
- usleep(WAIT_PERIOD_MS*1000);
- break;
- }
-
- if (writtenSize > reqSize) {
- writtenSize = reqSize;
+ return WAIT_PERIOD_MS * 1000000LL;
}
if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
// 8 to 16 bit conversion, note that source and destination are the same address
memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) audioBuffer.i8, writtenSize);
- writtenSize <<= 1;
+ audioBuffer.size <<= 1;
}
- audioBuffer.size = writtenSize;
- // NOTE: cblk->frameSize is not equal to AudioTrack::frameSize() for
- // 8 bit PCM data: in this case, cblk->frameSize is based on a sample size of
- // 16 bit.
- audioBuffer.frameCount = writtenSize / mFrameSizeAF;
-
- frames -= audioBuffer.frameCount;
+ size_t releasedFrames = audioBuffer.size / mFrameSizeAF;
+ audioBuffer.frameCount = releasedFrames;
+ mRemainingFrames -= releasedFrames;
+ if (misalignment >= releasedFrames) {
+ misalignment -= releasedFrames;
+ } else {
+ misalignment = 0;
+ }
releaseBuffer(&audioBuffer);
- }
- while (frames);
- if (frames == 0) {
- mRemainingFrames = mNotificationFramesAct;
- } else {
- mRemainingFrames = frames;
+ // FIXME here is where we would repeat EVENT_MORE_DATA again on same advanced buffer
+ // if callback doesn't like to accept the full chunk
+ if (writtenSize < reqSize) {
+ continue;
+ }
+
+ // There could be enough non-contiguous frames available to satisfy the remaining request
+ if (mRemainingFrames <= nonContig) {
+ continue;
+ }
+
+#if 0
+ // This heuristic tries to collapse a series of EVENT_MORE_DATA that would total to a
+ // sum <= notificationFrames. It replaces that series by at most two EVENT_MORE_DATA
+ // that total to a sum == notificationFrames.
+ if (0 < misalignment && misalignment <= mRemainingFrames) {
+ mRemainingFrames = misalignment;
+ return (mRemainingFrames * 1100000000LL) / sampleRate;
+ }
+#endif
+
}
- return true;
+ mRemainingFrames = notificationFrames;
+ mRetryOnPartialBuffer = true;
+
+ // A lot has transpired since ns was calculated, so run again immediately and re-calculate
+ return 0;
}
-// must be called with mLock and refCblk.lock held. Callers must also hold strong references on
-// the IAudioTrack and IMemory in case they are recreated here.
-// If the IAudioTrack is successfully restored, the refCblk pointer is updated
-// FIXME Don't depend on caller to hold strong references.
-status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart)
+status_t AudioTrack::restoreTrack_l(const char *from)
{
+ ALOGW("dead IAudioTrack, creating a new one from %s()", from);
+ ++mSequence;
status_t result;
- audio_track_cblk_t* cblk = refCblk;
- audio_track_cblk_t* newCblk = cblk;
- ALOGW("dead IAudioTrack, creating a new one from %s",
- fromStart ? "start()" : "obtainBuffer()");
-
- // signal old cblk condition so that other threads waiting for available buffers stop
- // waiting now
- cblk->cv.broadcast();
- cblk->lock.unlock();
-
// refresh the audio configuration cache in this process to make sure we get new
// output parameters in getOutput_l() and createTrack_l()
AudioSystem::clearAudioConfigCache();
@@ -1447,68 +1498,47 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart
// if the new IAudioTrack is created, createTrack_l() will modify the
// following member variables: mAudioTrack, mCblkMemory and mCblk.
// It will also delete the strong references on previous IAudioTrack and IMemory
+ size_t position = mProxy->getPosition();
+ mNewPosition = position + mUpdatePeriod;
+ size_t bufferPosition = mStaticProxy != NULL ? mStaticProxy->getBufferPosition() : 0;
result = createTrack_l(mStreamType,
mSampleRate,
mFormat,
mReqFrameCount, // so that frame count never goes down
mFlags,
mSharedBuffer,
- getOutput_l());
+ getOutput_l(),
+ position /*epoch*/);
if (result == NO_ERROR) {
- uint32_t user = cblk->user;
- uint32_t server = cblk->server;
+ // continue playback from last known position, but
+ // don't attempt to restore loop after invalidation; it's difficult and not worthwhile
+ if (mStaticProxy != NULL) {
+ mLoopPeriod = 0;
+ mStaticProxy->setLoop(bufferPosition, mFrameCount, 0);
+ }
+ // FIXME How do we simulate the fact that all frames present in the buffer at the time of
+ // track destruction have been played? This is critical for SoundPool implementation
+ // This must be broken, and needs to be tested/debugged.
+#if 0
// restore write index and set other indexes to reflect empty buffer status
- newCblk = mCblk;
- newCblk->user = user;
- newCblk->server = user;
- newCblk->userBase = user;
- newCblk->serverBase = user;
- // restore loop: this is not guaranteed to succeed if new frame count is not
- // compatible with loop length
- setLoop_l(cblk->loopStart, cblk->loopEnd, cblk->loopCount);
- size_t frames = 0;
- if (!fromStart) {
- newCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
+ if (!strcmp(from, "start")) {
// Make sure that a client relying on callback events indicating underrun or
// the actual amount of audio frames played (e.g SoundPool) receives them.
if (mSharedBuffer == 0) {
- if (user > server) {
- frames = ((user - server) > mFrameCount) ?
- mFrameCount : (user - server);
- memset(mBuffers, 0, frames * mFrameSizeAF);
- }
// restart playback even if buffer is not completely filled.
- android_atomic_or(CBLK_FORCEREADY, &newCblk->flags);
+ android_atomic_or(CBLK_FORCEREADY, &mCblk->flags);
}
}
- if (mSharedBuffer != 0) {
- frames = mFrameCount;
- }
- if (frames > 0) {
- // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to
- // the client
- mProxy->stepUser(frames);
- }
- if (mActive) {
+#endif
+ if (mState == STATE_ACTIVE) {
result = mAudioTrack->start();
- ALOGW_IF(result != NO_ERROR, "restoreTrack_l() start() failed status %d", result);
- }
- if (fromStart && result == NO_ERROR) {
- mNewPosition = newCblk->server + mUpdatePeriod;
}
}
- ALOGW_IF(result != NO_ERROR, "restoreTrack_l() failed status %d", result);
- ALOGV("restoreTrack_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x",
- result, mActive, newCblk, cblk, newCblk->flags, cblk->flags);
-
- if (result == NO_ERROR) {
- // from now on we switch to the newly created cblk
- refCblk = newCblk;
+ if (result != NO_ERROR) {
+ ALOGW("restoreTrack_l() failed status %d", result);
+ mState = STATE_STOPPED;
}
- newCblk->lock.lock();
-
- ALOGW_IF(result != NO_ERROR, "restoreTrack_l() error %d", result);
return result;
}
@@ -1529,16 +1559,33 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args) const
result.append(buffer);
snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus);
result.append(buffer);
- snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency);
+ snprintf(buffer, 255, " state(%d), latency (%d)\n", mState, mLatency);
result.append(buffer);
::write(fd, result.string(), result.size());
return NO_ERROR;
}
+uint32_t AudioTrack::getUnderrunFrames() const
+{
+ AutoMutex lock(mLock);
+ return mProxy->getUnderrunFrames();
+}
+
+// =========================================================================
+
+void AudioTrack::DeathNotifier::binderDied(const wp<IBinder>& who)
+{
+ sp<AudioTrack> audioTrack = mAudioTrack.promote();
+ if (audioTrack != 0) {
+ AutoMutex lock(audioTrack->mLock);
+ audioTrack->mProxy->binderDied();
+ }
+}
+
// =========================================================================
AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava)
- : Thread(bCanCallJava), mReceiver(receiver), mPaused(true)
+ : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mResumeLatch(false)
{
}
@@ -1556,10 +1603,26 @@ bool AudioTrack::AudioTrackThread::threadLoop()
return true;
}
}
- if (!mReceiver.processAudioBuffer(this)) {
- pause();
+ nsecs_t ns = mReceiver.processAudioBuffer(this);
+ switch (ns) {
+ case 0:
+ return true;
+ case NS_WHENEVER:
+ sleep(1);
+ return true;
+ case NS_INACTIVE:
+ pauseConditional();
+ return true;
+ case NS_NEVER:
+ return false;
+ default:
+ LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %lld", ns);
+ struct timespec req;
+ req.tv_sec = ns / 1000000000LL;
+ req.tv_nsec = ns % 1000000000LL;
+ nanosleep(&req, NULL /*rem*/);
+ return true;
}
- return true;
}
void AudioTrack::AudioTrackThread::requestExit()
@@ -1573,6 +1636,17 @@ void AudioTrack::AudioTrackThread::pause()
{
AutoMutex _l(mMyLock);
mPaused = true;
+ mResumeLatch = false;
+}
+
+void AudioTrack::AudioTrackThread::pauseConditional()
+{
+ AutoMutex _l(mMyLock);
+ if (mResumeLatch) {
+ mResumeLatch = false;
+ } else {
+ mPaused = true;
+ }
}
void AudioTrack::AudioTrackThread::resume()
@@ -1580,7 +1654,10 @@ void AudioTrack::AudioTrackThread::resume()
AutoMutex _l(mMyLock);
if (mPaused) {
mPaused = false;
+ mResumeLatch = false;
mMyCond.signal();
+ } else {
+ mResumeLatch = true;
}
}
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 13d47c9..f034164 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -19,178 +19,664 @@
#include <private/media/AudioTrackShared.h>
#include <utils/Log.h>
+extern "C" {
+#include "../private/bionic_futex.h"
+}
namespace android {
audio_track_cblk_t::audio_track_cblk_t()
- : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
- userBase(0), serverBase(0), frameCount_(0),
- loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000),
- mSampleRate(0), mSendLevel(0), flags(0)
+ : server(0), frameCount_(0), mFutex(0), mMinimum(0),
+ mVolumeLR(0x10001000), mSampleRate(0), mSendLevel(0), mName(0), flags(0)
{
+ memset(&u, 0, sizeof(u));
}
-uint32_t audio_track_cblk_t::stepUser(size_t stepCount, size_t frameCount, bool isOut)
+// ---------------------------------------------------------------------------
+
+Proxy::Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize,
+ bool isOut, bool clientInServer)
+ : mCblk(cblk), mBuffers(buffers), mFrameCount(frameCount), mFrameSize(frameSize),
+ mFrameCountP2(roundup(frameCount)), mIsOut(isOut), mClientInServer(clientInServer),
+ mIsShutdown(false)
{
- ALOGV("stepuser %08x %08x %d", user, server, stepCount);
+}
- uint32_t u = user;
- u += stepCount;
- // Ensure that user is never ahead of server for AudioRecord
- if (isOut) {
- // If stepServer() has been called once, switch to normal obtainBuffer() timeout period
- if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) {
- bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
- }
- } else if (u > server) {
- ALOGW("stepUser occurred after track reset");
- u = server;
+// ---------------------------------------------------------------------------
+
+ClientProxy::ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
+ size_t frameSize, bool isOut, bool clientInServer)
+ : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), mEpoch(0)
+{
+}
+
+const struct timespec ClientProxy::kForever = {INT_MAX /*tv_sec*/, 0 /*tv_nsec*/};
+const struct timespec ClientProxy::kNonBlocking = {0 /*tv_sec*/, 0 /*tv_nsec*/};
+
+#define MEASURE_NS 10000000 // attempt to provide accurate timeouts if requested >= MEASURE_NS
+
+// To facilitate quicker recovery from server failure, this value limits the timeout per each futex
+// wait. However it does not protect infinite timeouts. If defined to be zero, there is no limit.
+// FIXME May not be compatible with audio tunneling requirements where timeout should be in the
+// order of minutes.
+#define MAX_SEC 5
+
+status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,
+ struct timespec *elapsed)
+{
+ if (buffer == NULL || buffer->mFrameCount == 0) {
+ ALOGE("%s BAD_VALUE", __func__);
+ return BAD_VALUE;
}
+ struct timespec total; // total elapsed time spent waiting
+ total.tv_sec = 0;
+ total.tv_nsec = 0;
+ bool measure = elapsed != NULL; // whether to measure total elapsed time spent waiting
- if (u >= frameCount) {
- // common case, user didn't just wrap
- if (u - frameCount >= userBase ) {
- userBase += frameCount;
+ status_t status;
+ enum {
+ TIMEOUT_ZERO, // requested == NULL || *requested == 0
+ TIMEOUT_INFINITE, // *requested == infinity
+ TIMEOUT_FINITE, // 0 < *requested < infinity
+ TIMEOUT_CONTINUE, // additional chances after TIMEOUT_FINITE
+ } timeout;
+ if (requested == NULL) {
+ timeout = TIMEOUT_ZERO;
+ } else if (requested->tv_sec == 0 && requested->tv_nsec == 0) {
+ timeout = TIMEOUT_ZERO;
+ } else if (requested->tv_sec == INT_MAX) {
+ timeout = TIMEOUT_INFINITE;
+ } else {
+ timeout = TIMEOUT_FINITE;
+ if (requested->tv_sec > 0 || requested->tv_nsec >= MEASURE_NS) {
+ measure = true;
+ }
+ }
+ struct timespec before;
+ bool beforeIsValid = false;
+ audio_track_cblk_t* cblk = mCblk;
+ bool ignoreInitialPendingInterrupt = true;
+ // check for shared memory corruption
+ if (mIsShutdown) {
+ status = NO_INIT;
+ goto end;
+ }
+ for (;;) {
+ int32_t flags = android_atomic_and(~CBLK_INTERRUPT, &cblk->flags);
+ // check for track invalidation by server, or server death detection
+ if (flags & CBLK_INVALID) {
+ ALOGV("Track invalidated");
+ status = DEAD_OBJECT;
+ goto end;
+ }
+ // check for obtainBuffer interrupted by client
+ if (!ignoreInitialPendingInterrupt && (flags & CBLK_INTERRUPT)) {
+ ALOGV("obtainBuffer() interrupted by client");
+ status = -EINTR;
+ goto end;
+ }
+ ignoreInitialPendingInterrupt = false;
+ // compute number of frames available to write (AudioTrack) or read (AudioRecord)
+ int32_t front;
+ int32_t rear;
+ if (mIsOut) {
+ // The barrier following the read of mFront is probably redundant.
+ // We're about to perform a conditional branch based on 'filled',
+ // which will force the processor to observe the read of mFront
+ // prior to allowing data writes starting at mRaw.
+ // However, the processor may support speculative execution,
+ // and be unable to undo speculative writes into shared memory.
+ // The barrier will prevent such speculative execution.
+ front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
+ rear = cblk->u.mStreaming.mRear;
+ } else {
+ // On the other hand, this barrier is required.
+ rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
+ front = cblk->u.mStreaming.mFront;
+ }
+ ssize_t filled = rear - front;
+ // pipe should not be overfull
+ if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
+ ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
+ mIsShutdown = true;
+ status = NO_INIT;
+ goto end;
+ }
+ // don't allow filling pipe beyond the nominal size
+ size_t avail = mIsOut ? mFrameCount - filled : filled;
+ if (avail > 0) {
+ // 'avail' may be non-contiguous, so return only the first contiguous chunk
+ size_t part1;
+ if (mIsOut) {
+ rear &= mFrameCountP2 - 1;
+ part1 = mFrameCountP2 - rear;
+ } else {
+ front &= mFrameCountP2 - 1;
+ part1 = mFrameCountP2 - front;
+ }
+ if (part1 > avail) {
+ part1 = avail;
+ }
+ if (part1 > buffer->mFrameCount) {
+ part1 = buffer->mFrameCount;
+ }
+ buffer->mFrameCount = part1;
+ buffer->mRaw = part1 > 0 ?
+ &((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL;
+ buffer->mNonContig = avail - part1;
+ // mUnreleased = part1;
+ status = NO_ERROR;
+ break;
+ }
+ struct timespec remaining;
+ const struct timespec *ts;
+ switch (timeout) {
+ case TIMEOUT_ZERO:
+ status = WOULD_BLOCK;
+ goto end;
+ case TIMEOUT_INFINITE:
+ ts = NULL;
+ break;
+ case TIMEOUT_FINITE:
+ timeout = TIMEOUT_CONTINUE;
+ if (MAX_SEC == 0) {
+ ts = requested;
+ break;
+ }
+ // fall through
+ case TIMEOUT_CONTINUE:
+ // FIXME we do not retry if requested < 10ms? needs documentation on this state machine
+ if (!measure || requested->tv_sec < total.tv_sec ||
+ (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) {
+ status = TIMED_OUT;
+ goto end;
+ }
+ remaining.tv_sec = requested->tv_sec - total.tv_sec;
+ if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0) {
+ remaining.tv_nsec += 1000000000;
+ remaining.tv_sec++;
+ }
+ if (0 < MAX_SEC && MAX_SEC < remaining.tv_sec) {
+ remaining.tv_sec = MAX_SEC;
+ remaining.tv_nsec = 0;
+ }
+ ts = &remaining;
+ break;
+ default:
+ LOG_FATAL("%s timeout=%d", timeout);
+ ts = NULL;
+ break;
+ }
+ int32_t old = android_atomic_dec(&cblk->mFutex);
+ if (old <= 0) {
+ int rc;
+ if (measure && !beforeIsValid) {
+ clock_gettime(CLOCK_MONOTONIC, &before);
+ beforeIsValid = true;
+ }
+ int ret = __futex_syscall4(&cblk->mFutex,
+ mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old - 1, ts);
+ // update total elapsed time spent waiting
+ if (measure) {
+ struct timespec after;
+ clock_gettime(CLOCK_MONOTONIC, &after);
+ total.tv_sec += after.tv_sec - before.tv_sec;
+ long deltaNs = after.tv_nsec - before.tv_nsec;
+ if (deltaNs < 0) {
+ deltaNs += 1000000000;
+ total.tv_sec--;
+ }
+ if ((total.tv_nsec += deltaNs) >= 1000000000) {
+ total.tv_nsec -= 1000000000;
+ total.tv_sec++;
+ }
+ before = after;
+ beforeIsValid = true;
+ }
+ switch (ret) {
+ case 0: // normal wakeup by server, or by binderDied()
+ case -EWOULDBLOCK: // benign race condition with server
+ case -EINTR: // wait was interrupted by signal or other spurious wakeup
+ case -ETIMEDOUT: // time-out expired
+ break;
+ default:
+ ALOGE("%s unexpected error %d", __func__, ret);
+ status = -ret;
+ goto end;
+ }
}
- } else if (u >= userBase + frameCount) {
- // user just wrapped
- userBase += frameCount;
}
- user = u;
-
- // Clear flow control error condition as new data has been written/read to/from buffer.
- if (flags & CBLK_UNDERRUN) {
- android_atomic_and(~CBLK_UNDERRUN, &flags);
+end:
+ if (status != NO_ERROR) {
+ buffer->mFrameCount = 0;
+ buffer->mRaw = NULL;
+ buffer->mNonContig = 0;
+ }
+ if (elapsed != NULL) {
+ *elapsed = total;
}
+ if (requested == NULL) {
+ requested = &kNonBlocking;
+ }
+ if (measure) {
+ ALOGV("requested %d.%03d elapsed %d.%03d", requested->tv_sec, requested->tv_nsec / 1000000,
+ total.tv_sec, total.tv_nsec / 1000000);
+ }
+ return status;
+}
- return u;
+void ClientProxy::releaseBuffer(Buffer* buffer)
+{
+ size_t stepCount = buffer->mFrameCount;
+ // FIXME
+ // check mUnreleased
+ // verify that stepCount <= frameCount returned by the last obtainBuffer()
+ // verify stepCount not > total frame count of pipe
+ if (stepCount == 0) {
+ return;
+ }
+ audio_track_cblk_t* cblk = mCblk;
+ // Both of these barriers are required
+ if (mIsOut) {
+ int32_t rear = cblk->u.mStreaming.mRear;
+ android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
+ } else {
+ int32_t front = cblk->u.mStreaming.mFront;
+ android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront);
+ }
}
-bool audio_track_cblk_t::stepServer(size_t stepCount, size_t frameCount, bool isOut)
+void ClientProxy::binderDied()
{
- ALOGV("stepserver %08x %08x %d", user, server, stepCount);
+ audio_track_cblk_t* cblk = mCblk;
+ if (!(android_atomic_or(CBLK_INVALID, &cblk->flags) & CBLK_INVALID)) {
+ // it seems that a FUTEX_WAKE_PRIVATE will not wake a FUTEX_WAIT, even within same process
+ (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+ 1);
+ }
+}
- if (!tryLock()) {
- ALOGW("stepServer() could not lock cblk");
- return false;
+void ClientProxy::interrupt()
+{
+ audio_track_cblk_t* cblk = mCblk;
+ if (!(android_atomic_or(CBLK_INTERRUPT, &cblk->flags) & CBLK_INTERRUPT)) {
+ (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+ 1);
}
+}
- uint32_t s = server;
- bool flushed = (s == user);
+size_t ClientProxy::getMisalignment()
+{
+ audio_track_cblk_t* cblk = mCblk;
+ return (mFrameCountP2 - (mIsOut ? cblk->u.mStreaming.mRear : cblk->u.mStreaming.mFront)) &
+ (mFrameCountP2 - 1);
+}
- s += stepCount;
- if (isOut) {
- // Mark that we have read the first buffer so that next time stepUser() is called
- // we switch to normal obtainBuffer() timeout period
- if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) {
- bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS - 1;
- }
- // It is possible that we receive a flush()
- // while the mixer is processing a block: in this case,
- // stepServer() is called After the flush() has reset u & s and
- // we have s > u
- if (flushed) {
- ALOGW("stepServer occurred after track reset");
- s = user;
+// ---------------------------------------------------------------------------
+
+void AudioTrackClientProxy::flush()
+{
+ mCblk->u.mStreaming.mFlush++;
+}
+
+// ---------------------------------------------------------------------------
+
+StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers,
+ size_t frameCount, size_t frameSize)
+ : AudioTrackClientProxy(cblk, buffers, frameCount, frameSize),
+ mMutator(&cblk->u.mStatic.mSingleStateQueue), mBufferPosition(0)
+{
+}
+
+void StaticAudioTrackClientProxy::flush()
+{
+ LOG_FATAL("static flush");
+}
+
+void StaticAudioTrackClientProxy::setLoop(size_t loopStart, size_t loopEnd, int loopCount)
+{
+ StaticAudioTrackState newState;
+ newState.mLoopStart = loopStart;
+ newState.mLoopEnd = loopEnd;
+ newState.mLoopCount = loopCount;
+ mBufferPosition = loopStart;
+ (void) mMutator.push(newState);
+}
+
+size_t StaticAudioTrackClientProxy::getBufferPosition()
+{
+ size_t bufferPosition;
+ if (mMutator.ack()) {
+ bufferPosition = mCblk->u.mStatic.mBufferPosition;
+ if (bufferPosition > mFrameCount) {
+ bufferPosition = mFrameCount;
}
+ } else {
+ bufferPosition = mBufferPosition;
}
+ return bufferPosition;
+}
+
+// ---------------------------------------------------------------------------
- if (s >= loopEnd) {
- ALOGW_IF(s > loopEnd, "stepServer: s %u > loopEnd %u", s, loopEnd);
- s = loopStart;
- if (--loopCount == 0) {
- loopEnd = UINT_MAX;
- loopStart = UINT_MAX;
+ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
+ size_t frameSize, bool isOut, bool clientInServer)
+ : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), mUnreleased(0),
+ mAvailToClient(0), mFlush(0), mDeferWake(false)
+{
+}
+
+status_t ServerProxy::obtainBuffer(Buffer* buffer)
+{
+ if (mIsShutdown) {
+ buffer->mFrameCount = 0;
+ buffer->mRaw = NULL;
+ buffer->mNonContig = 0;
+ mUnreleased = 0;
+ return NO_INIT;
+ }
+ audio_track_cblk_t* cblk = mCblk;
+ // compute number of frames available to write (AudioTrack) or read (AudioRecord),
+ // or use previous cached value from framesReady(), with added barrier if it omits.
+ int32_t front;
+ int32_t rear;
+ // See notes on barriers at ClientProxy::obtainBuffer()
+ if (mIsOut) {
+ int32_t flush = cblk->u.mStreaming.mFlush;
+ rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
+ if (flush != mFlush) {
+ front = rear;
+ mFlush = flush;
+ } else {
+ front = cblk->u.mStreaming.mFront;
}
+ } else {
+ front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
+ rear = cblk->u.mStreaming.mRear;
+ }
+ ssize_t filled = rear - front;
+ // pipe should not already be overfull
+ if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
+ ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
+ mIsShutdown = true;
+ }
+ if (mIsShutdown) {
+ buffer->mFrameCount = 0;
+ buffer->mRaw = NULL;
+ buffer->mNonContig = 0;
+ mUnreleased = 0;
+ return NO_INIT;
}
+ // don't allow filling pipe beyond the nominal size
+ size_t availToServer;
+ if (mIsOut) {
+ availToServer = filled;
+ mAvailToClient = mFrameCount - filled;
+ } else {
+ availToServer = mFrameCount - filled;
+ mAvailToClient = filled;
+ }
+ // 'availToServer' may be non-contiguous, so return only the first contiguous chunk
+ size_t part1;
+ if (mIsOut) {
+ front &= mFrameCountP2 - 1;
+ part1 = mFrameCountP2 - front;
+ } else {
+ rear &= mFrameCountP2 - 1;
+ part1 = mFrameCountP2 - rear;
+ }
+ if (part1 > availToServer) {
+ part1 = availToServer;
+ }
+ size_t ask = buffer->mFrameCount;
+ if (part1 > ask) {
+ part1 = ask;
+ }
+ // is assignment redundant in some cases?
+ buffer->mFrameCount = part1;
+ buffer->mRaw = part1 > 0 ?
+ &((char *) mBuffers)[(mIsOut ? front : rear) * mFrameSize] : NULL;
+ buffer->mNonContig = availToServer - part1;
+ mUnreleased = part1;
+ // optimization to avoid waking up the client too early
+ // FIXME need to test for recording
+ mDeferWake = part1 < ask && availToServer >= ask;
+ return part1 > 0 ? NO_ERROR : WOULD_BLOCK;
+}
- if (s >= frameCount) {
- // common case, server didn't just wrap
- if (s - frameCount >= serverBase ) {
- serverBase += frameCount;
- }
- } else if (s >= serverBase + frameCount) {
- // server just wrapped
- serverBase += frameCount;
+void ServerProxy::releaseBuffer(Buffer* buffer)
+{
+ if (mIsShutdown) {
+ buffer->mFrameCount = 0;
+ buffer->mRaw = NULL;
+ buffer->mNonContig = 0;
+ return;
+ }
+ size_t stepCount = buffer->mFrameCount;
+ LOG_ALWAYS_FATAL_IF(stepCount > mUnreleased);
+ if (stepCount == 0) {
+ buffer->mRaw = NULL;
+ buffer->mNonContig = 0;
+ return;
+ }
+ mUnreleased -= stepCount;
+ audio_track_cblk_t* cblk = mCblk;
+ if (mIsOut) {
+ int32_t front = cblk->u.mStreaming.mFront;
+ android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront);
+ } else {
+ int32_t rear = cblk->u.mStreaming.mRear;
+ android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
}
- server = s;
+ mCblk->server += stepCount;
- if (!(flags & CBLK_INVALID)) {
- cv.signal();
+ size_t half = mFrameCount / 2;
+ if (half == 0) {
+ half = 1;
+ }
+ size_t minimum = cblk->mMinimum;
+ if (minimum == 0) {
+ minimum = mIsOut ? half : 1;
+ } else if (minimum > half) {
+ minimum = half;
+ }
+ if (!mDeferWake && mAvailToClient + stepCount >= minimum) {
+ ALOGV("mAvailToClient=%u stepCount=%u minimum=%u", mAvailToClient, stepCount, minimum);
+ // could client be sleeping, or not need this increment and counter overflows?
+ int32_t old = android_atomic_inc(&cblk->mFutex);
+ if (old == -1) {
+ (void) __futex_syscall3(&cblk->mFutex,
+ mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
+ }
}
- lock.unlock();
- return true;
+
+ buffer->mFrameCount = 0;
+ buffer->mRaw = NULL;
+ buffer->mNonContig = 0;
}
-void* audio_track_cblk_t::buffer(void *buffers, size_t frameSize, uint32_t offset) const
+// ---------------------------------------------------------------------------
+
+size_t AudioTrackServerProxy::framesReady()
{
- return (int8_t *)buffers + (offset - userBase) * frameSize;
+ LOG_ALWAYS_FATAL_IF(!mIsOut);
+
+ if (mIsShutdown) {
+ return 0;
+ }
+ audio_track_cblk_t* cblk = mCblk;
+ // the acquire might not be necessary since not doing a subsequent read
+ int32_t rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
+ ssize_t filled = rear - cblk->u.mStreaming.mFront;
+ // pipe should not already be overfull
+ if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
+ ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
+ mIsShutdown = true;
+ return 0;
+ }
+ // cache this value for later use by obtainBuffer(), with added barrier
+ // and racy if called by normal mixer thread
+ // ignores flush(), so framesReady() may report a larger mFrameCount than obtainBuffer()
+ return filled;
}
-uint32_t audio_track_cblk_t::framesAvailable(size_t frameCount, bool isOut)
+// ---------------------------------------------------------------------------
+
+StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
+ size_t frameCount, size_t frameSize)
+ : AudioTrackServerProxy(cblk, buffers, frameCount, frameSize),
+ mObserver(&cblk->u.mStatic.mSingleStateQueue), mPosition(0),
+ mEnd(frameCount), mFramesReadyIsCalledByMultipleThreads(false)
{
- Mutex::Autolock _l(lock);
- return framesAvailable_l(frameCount, isOut);
+ mState.mLoopStart = 0;
+ mState.mLoopEnd = 0;
+ mState.mLoopCount = 0;
}
-uint32_t audio_track_cblk_t::framesAvailable_l(size_t frameCount, bool isOut)
+void StaticAudioTrackServerProxy::framesReadyIsCalledByMultipleThreads()
{
- uint32_t u = user;
- uint32_t s = server;
+ mFramesReadyIsCalledByMultipleThreads = true;
+}
- if (isOut) {
- uint32_t limit = (s < loopStart) ? s : loopStart;
- return limit + frameCount - u;
- } else {
- return frameCount + u - s;
+size_t StaticAudioTrackServerProxy::framesReady()
+{
+ // FIXME
+ // This is racy if called by normal mixer thread,
+ // as we're reading 2 independent variables without a lock.
+ // Can't call mObserver.poll(), as we might be called from wrong thread.
+ // If looping is enabled, should return a higher number (since includes non-contiguous).
+ size_t position = mPosition;
+ if (!mFramesReadyIsCalledByMultipleThreads) {
+ ssize_t positionOrStatus = pollPosition();
+ if (positionOrStatus >= 0) {
+ position = (size_t) positionOrStatus;
+ }
}
+ size_t end = mEnd;
+ return position < end ? end - position : 0;
}
-uint32_t audio_track_cblk_t::framesReady(bool isOut)
+ssize_t StaticAudioTrackServerProxy::pollPosition()
{
- uint32_t u = user;
- uint32_t s = server;
-
- if (isOut) {
- if (u < loopEnd) {
- return u - s;
- } else {
- // do not block on mutex shared with client on AudioFlinger side
- if (!tryLock()) {
- ALOGW("framesReady() could not lock cblk");
- return 0;
+ size_t position = mPosition;
+ StaticAudioTrackState state;
+ if (mObserver.poll(state)) {
+ bool valid = false;
+ size_t loopStart = state.mLoopStart;
+ size_t loopEnd = state.mLoopEnd;
+ if (state.mLoopCount == 0) {
+ if (loopStart > mFrameCount) {
+ loopStart = mFrameCount;
}
- uint32_t frames = UINT_MAX;
- if (loopCount >= 0) {
- frames = (loopEnd - loopStart)*loopCount + u - s;
+ // ignore loopEnd
+ mPosition = position = loopStart;
+ mEnd = mFrameCount;
+ mState.mLoopCount = 0;
+ valid = true;
+ } else {
+ if (loopStart < loopEnd && loopEnd <= mFrameCount &&
+ loopEnd - loopStart >= MIN_LOOP) {
+ if (!(loopStart <= position && position < loopEnd)) {
+ mPosition = position = loopStart;
+ }
+ mEnd = loopEnd;
+ mState = state;
+ valid = true;
}
- lock.unlock();
- return frames;
}
- } else {
- return s - u;
+ if (!valid) {
+ ALOGE("%s client pushed an invalid state, shutting down", __func__);
+ mIsShutdown = true;
+ return (ssize_t) NO_INIT;
+ }
+ mCblk->u.mStatic.mBufferPosition = position;
}
+ return (ssize_t) position;
}
-bool audio_track_cblk_t::tryLock()
+status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer)
{
- // the code below simulates lock-with-timeout
- // we MUST do this to protect the AudioFlinger server
- // as this lock is shared with the client.
- status_t err;
+ if (mIsShutdown) {
+ buffer->mFrameCount = 0;
+ buffer->mRaw = NULL;
+ buffer->mNonContig = 0;
+ mUnreleased = 0;
+ return NO_INIT;
+ }
+ ssize_t positionOrStatus = pollPosition();
+ if (positionOrStatus < 0) {
+ buffer->mFrameCount = 0;
+ buffer->mRaw = NULL;
+ buffer->mNonContig = 0;
+ mUnreleased = 0;
+ return (status_t) positionOrStatus;
+ }
+ size_t position = (size_t) positionOrStatus;
+ size_t avail;
+ if (position < mEnd) {
+ avail = mEnd - position;
+ size_t wanted = buffer->mFrameCount;
+ if (avail < wanted) {
+ buffer->mFrameCount = avail;
+ } else {
+ avail = wanted;
+ }
+ buffer->mRaw = &((char *) mBuffers)[position * mFrameSize];
+ } else {
+ avail = 0;
+ buffer->mFrameCount = 0;
+ buffer->mRaw = NULL;
+ }
+ buffer->mNonContig = 0; // FIXME should be > 0 for looping
+ mUnreleased = avail;
+ return NO_ERROR;
+}
- err = lock.tryLock();
- if (err == -EBUSY) { // just wait a bit
- usleep(1000);
- err = lock.tryLock();
+void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer)
+{
+ size_t stepCount = buffer->mFrameCount;
+ LOG_ALWAYS_FATAL_IF(stepCount > mUnreleased);
+ if (stepCount == 0) {
+ buffer->mRaw = NULL;
+ buffer->mNonContig = 0;
+ return;
}
- if (err != NO_ERROR) {
- // probably, the client just died.
- return false;
+ mUnreleased -= stepCount;
+ audio_track_cblk_t* cblk = mCblk;
+ size_t position = mPosition;
+ size_t newPosition = position + stepCount;
+ int32_t setFlags = 0;
+ if (!(position <= newPosition && newPosition <= mFrameCount)) {
+ ALOGW("%s newPosition %u outside [%u, %u]", __func__, newPosition, position, mFrameCount);
+ newPosition = mFrameCount;
+ } else if (mState.mLoopCount != 0 && newPosition == mState.mLoopEnd) {
+ if (mState.mLoopCount == -1 || --mState.mLoopCount != 0) {
+ newPosition = mState.mLoopStart;
+ setFlags = CBLK_LOOP_CYCLE;
+ } else {
+ mEnd = mFrameCount; // this is what allows playback to continue after the loop
+ setFlags = CBLK_LOOP_FINAL;
+ }
}
- return true;
+ if (newPosition == mFrameCount) {
+ setFlags |= CBLK_BUFFER_END;
+ }
+ mPosition = newPosition;
+
+ cblk->server += stepCount;
+ cblk->u.mStatic.mBufferPosition = newPosition;
+ if (setFlags != 0) {
+ (void) android_atomic_or(setFlags, &cblk->flags);
+ // this would be a good place to wake a futex
+ }
+
+ buffer->mFrameCount = 0;
+ buffer->mRaw = NULL;
+ buffer->mNonContig = 0;
}
+// ---------------------------------------------------------------------------
+
} // namespace android
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index ebe1ba1..f9ad31d 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -1060,7 +1060,9 @@ bool ToneGenerator::initAudioTrack() {
this, // user
0, // notificationFrames
0, // sharedBuffer
- mThreadCanCallJava);
+ mThreadCanCallJava,
+ 0, // sessionId
+ AudioTrack::TRANSFER_CALLBACK);
if (mpAudioTrack->initCheck() != NO_ERROR) {
ALOGE("AudioTrack->initCheck failed");