summaryrefslogtreecommitdiffstats
path: root/media/libmedia/AudioTrackShared.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libmedia/AudioTrackShared.cpp')
-rw-r--r--media/libmedia/AudioTrackShared.cpp228
1 files changed, 166 insertions, 62 deletions
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index ff24475..6d5f1af 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -28,7 +28,21 @@ namespace android {
// used to clamp a value to size_t. TODO: move to another file.
template <typename T>
size_t clampToSize(T x) {
- return x > SIZE_MAX ? SIZE_MAX : x < 0 ? 0 : (size_t) x;
+ return sizeof(T) > sizeof(size_t) && x > (T) SIZE_MAX ? SIZE_MAX : x < 0 ? 0 : (size_t) x;
+}
+
+// incrementSequence is used to determine the next sequence value
+// for the loop and position sequence counters. It should return
+// a value between "other" + 1 and "other" + INT32_MAX, the choice of
+// which needs to be the "least recently used" sequence value for "self".
+// In general, this means (new_self) returned is max(self, other) + 1.
+
+static uint32_t incrementSequence(uint32_t self, uint32_t other) {
+ int32_t diff = self - other;
+ if (diff >= 0 && diff < INT32_MAX) {
+ return self + 1; // we're already ahead of other.
+ }
+ return other + 1; // we're behind, so move just ahead of other.
}
audio_track_cblk_t::audio_track_cblk_t()
@@ -409,7 +423,6 @@ status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *request
goto end;
}
// check for obtainBuffer interrupted by client
- // check for obtainBuffer interrupted by client
if (flags & CBLK_INTERRUPT) {
ALOGV("waitStreamEndDone() interrupted by client");
status = -EINTR;
@@ -485,8 +498,11 @@ end:
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)
+ mMutator(&cblk->u.mStatic.mSingleStateQueue),
+ mPosLoopObserver(&cblk->u.mStatic.mPosLoopQueue)
{
+ memset(&mState, 0, sizeof(mState));
+ memset(&mPosLoop, 0, sizeof(mPosLoop));
}
void StaticAudioTrackClientProxy::flush()
@@ -501,30 +517,72 @@ void StaticAudioTrackClientProxy::setLoop(size_t loopStart, size_t loopEnd, int
// FIXME Should return an error status
return;
}
- StaticAudioTrackState newState;
- newState.mLoopStart = (uint32_t) loopStart;
- newState.mLoopEnd = (uint32_t) loopEnd;
- newState.mLoopCount = loopCount;
- size_t bufferPosition;
- if (loopCount == 0 || (bufferPosition = getBufferPosition()) >= loopEnd) {
- bufferPosition = loopStart;
+ mState.mLoopStart = (uint32_t) loopStart;
+ mState.mLoopEnd = (uint32_t) loopEnd;
+ mState.mLoopCount = loopCount;
+ mState.mLoopSequence = incrementSequence(mState.mLoopSequence, mState.mPositionSequence);
+ // set patch-up variables until the mState is acknowledged by the ServerProxy.
+ // observed buffer position and loop count will freeze until then to give the
+ // illusion of a synchronous change.
+ getBufferPositionAndLoopCount(NULL, NULL);
+ // preserve behavior to restart at mState.mLoopStart if position exceeds mState.mLoopEnd.
+ if (mState.mLoopCount != 0 && mPosLoop.mBufferPosition >= mState.mLoopEnd) {
+ mPosLoop.mBufferPosition = mState.mLoopStart;
+ }
+ mPosLoop.mLoopCount = mState.mLoopCount;
+ (void) mMutator.push(mState);
+}
+
+void StaticAudioTrackClientProxy::setBufferPosition(size_t position)
+{
+ // This can only happen on a 64-bit client
+ if (position > UINT32_MAX) {
+ // FIXME Should return an error status
+ return;
+ }
+ mState.mPosition = (uint32_t) position;
+ mState.mPositionSequence = incrementSequence(mState.mPositionSequence, mState.mLoopSequence);
+ // set patch-up variables until the mState is acknowledged by the ServerProxy.
+ // observed buffer position and loop count will freeze until then to give the
+ // illusion of a synchronous change.
+ if (mState.mLoopCount > 0) { // only check if loop count is changing
+ getBufferPositionAndLoopCount(NULL, NULL); // get last position
+ }
+ mPosLoop.mBufferPosition = position;
+ if (position >= mState.mLoopEnd) {
+ // no ongoing loop is possible if position is greater than loopEnd.
+ mPosLoop.mLoopCount = 0;
}
- mBufferPosition = bufferPosition; // snapshot buffer position until loop is acknowledged.
- (void) mMutator.push(newState);
+ (void) mMutator.push(mState);
+}
+
+void StaticAudioTrackClientProxy::setBufferPositionAndLoop(size_t position, size_t loopStart,
+ size_t loopEnd, int loopCount)
+{
+ setLoop(loopStart, loopEnd, loopCount);
+ setBufferPosition(position);
}
size_t StaticAudioTrackClientProxy::getBufferPosition()
{
- size_t bufferPosition;
- if (mMutator.ack()) {
- bufferPosition = (size_t) mCblk->u.mStatic.mBufferPosition;
- if (bufferPosition > mFrameCount) {
- bufferPosition = mFrameCount;
- }
- } else {
- bufferPosition = mBufferPosition;
+ getBufferPositionAndLoopCount(NULL, NULL);
+ return mPosLoop.mBufferPosition;
+}
+
+void StaticAudioTrackClientProxy::getBufferPositionAndLoopCount(
+ size_t *position, int *loopCount)
+{
+ if (mMutator.ack() == StaticAudioTrackSingleStateQueue::SSQ_DONE) {
+ if (mPosLoopObserver.poll(mPosLoop)) {
+ ; // a valid mPosLoop should be available if ackDone is true.
+ }
+ }
+ if (position != NULL) {
+ *position = mPosLoop.mBufferPosition;
+ }
+ if (loopCount != NULL) {
+ *loopCount = mPosLoop.mLoopCount;
}
- return bufferPosition;
}
// ---------------------------------------------------------------------------
@@ -560,7 +618,8 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
ssize_t filled = rear - newFront;
// Rather than shutting down on a corrupt flush, just treat it as a full flush
if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
- ALOGE("mFlush %#x -> %#x, front %#x, rear %#x, mask %#x, newFront %#x, filled %d=%#x",
+ ALOGE("mFlush %#x -> %#x, front %#x, rear %#x, mask %#x, newFront %#x, "
+ "filled %d=%#x",
mFlush, flush, front, rear, mask, newFront, filled, filled);
newFront = rear;
}
@@ -739,13 +798,12 @@ void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
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),
+ mObserver(&cblk->u.mStatic.mSingleStateQueue),
+ mPosLoopMutator(&cblk->u.mStatic.mPosLoopQueue),
mFramesReadySafe(frameCount), mFramesReady(frameCount),
mFramesReadyIsCalledByMultipleThreads(false)
{
- mState.mLoopStart = 0;
- mState.mLoopEnd = 0;
- mState.mLoopCount = 0;
+ memset(&mState, 0, sizeof(mState));
}
void StaticAudioTrackServerProxy::framesReadyIsCalledByMultipleThreads()
@@ -762,55 +820,97 @@ size_t StaticAudioTrackServerProxy::framesReady()
return mFramesReadySafe;
}
-ssize_t StaticAudioTrackServerProxy::pollPosition()
+status_t StaticAudioTrackServerProxy::updateStateWithLoop(
+ StaticAudioTrackState *localState, const StaticAudioTrackState &update) const
{
- size_t position = mPosition;
- StaticAudioTrackState state;
- if (mObserver.poll(state)) {
+ if (localState->mLoopSequence != update.mLoopSequence) {
bool valid = false;
- size_t loopStart = state.mLoopStart;
- size_t loopEnd = state.mLoopEnd;
- if (state.mLoopCount == 0) {
- if (loopStart > mFrameCount) {
- loopStart = mFrameCount;
- }
- // ignore loopEnd
- mPosition = position = loopStart;
- mFramesReady = mFrameCount - mPosition;
- mState.mLoopCount = 0;
+ const size_t loopStart = update.mLoopStart;
+ const size_t loopEnd = update.mLoopEnd;
+ size_t position = localState->mPosition;
+ if (update.mLoopCount == 0) {
valid = true;
- } else if (state.mLoopCount >= -1) {
+ } else if (update.mLoopCount >= -1) {
if (loopStart < loopEnd && loopEnd <= mFrameCount &&
loopEnd - loopStart >= MIN_LOOP) {
// If the current position is greater than the end of the loop
// we "wrap" to the loop start. This might cause an audible pop.
if (position >= loopEnd) {
- mPosition = position = loopStart;
- }
- if (state.mLoopCount == -1) {
- mFramesReady = INT64_MAX;
- } else {
- // mFramesReady is 64 bits to handle the effective number of frames
- // that the static audio track contains, including loops.
- // TODO: Later consider fixing overflow, but does not seem needed now
- // as will not overflow if loopStart and loopEnd are Java "ints".
- mFramesReady = int64_t(state.mLoopCount) * (loopEnd - loopStart)
- + mFrameCount - mPosition;
+ position = loopStart;
}
- mState = state;
valid = true;
}
}
- if (!valid || mPosition > mFrameCount) {
+ if (!valid || position > mFrameCount) {
+ return NO_INIT;
+ }
+ localState->mPosition = position;
+ localState->mLoopCount = update.mLoopCount;
+ localState->mLoopEnd = loopEnd;
+ localState->mLoopStart = loopStart;
+ localState->mLoopSequence = update.mLoopSequence;
+ }
+ return OK;
+}
+
+status_t StaticAudioTrackServerProxy::updateStateWithPosition(
+ StaticAudioTrackState *localState, const StaticAudioTrackState &update) const
+{
+ if (localState->mPositionSequence != update.mPositionSequence) {
+ if (update.mPosition > mFrameCount) {
+ return NO_INIT;
+ } else if (localState->mLoopCount != 0 && update.mPosition >= localState->mLoopEnd) {
+ localState->mLoopCount = 0; // disable loop count if position is beyond loop end.
+ }
+ localState->mPosition = update.mPosition;
+ localState->mPositionSequence = update.mPositionSequence;
+ }
+ return OK;
+}
+
+ssize_t StaticAudioTrackServerProxy::pollPosition()
+{
+ StaticAudioTrackState state;
+ if (mObserver.poll(state)) {
+ StaticAudioTrackState trystate = mState;
+ bool result;
+ const int32_t diffSeq = state.mLoopSequence - state.mPositionSequence;
+
+ if (diffSeq < 0) {
+ result = updateStateWithLoop(&trystate, state) == OK &&
+ updateStateWithPosition(&trystate, state) == OK;
+ } else {
+ result = updateStateWithPosition(&trystate, state) == OK &&
+ updateStateWithLoop(&trystate, state) == OK;
+ }
+ if (!result) {
+ mObserver.done();
+ // caution: no update occurs so server state will be inconsistent with client state.
ALOGE("%s client pushed an invalid state, shutting down", __func__);
mIsShutdown = true;
return (ssize_t) NO_INIT;
}
+ mState = trystate;
+ if (mState.mLoopCount == -1) {
+ mFramesReady = INT64_MAX;
+ } else if (mState.mLoopCount == 0) {
+ mFramesReady = mFrameCount - mState.mPosition;
+ } else if (mState.mLoopCount > 0) {
+ // TODO: Later consider fixing overflow, but does not seem needed now
+ // as will not overflow if loopStart and loopEnd are Java "ints".
+ mFramesReady = int64_t(mState.mLoopCount) * (mState.mLoopEnd - mState.mLoopStart)
+ + mFrameCount - mState.mPosition;
+ }
mFramesReadySafe = clampToSize(mFramesReady);
// This may overflow, but client is not supposed to rely on it
- mCblk->u.mStatic.mBufferPosition = (uint32_t) position;
+ StaticAudioTrackPosLoop posLoop;
+
+ posLoop.mLoopCount = (int32_t) mState.mLoopCount;
+ posLoop.mBufferPosition = (uint32_t) mState.mPosition;
+ mPosLoopMutator.push(posLoop);
+ mObserver.done(); // safe to read mStatic variables.
}
- return (ssize_t) position;
+ return (ssize_t) mState.mPosition;
}
status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush __unused)
@@ -849,7 +949,7 @@ status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush
}
// As mFramesReady is the total remaining frames in the static audio track,
// it is always larger or equal to avail.
- LOG_ALWAYS_FATAL_IF(mFramesReady < avail);
+ LOG_ALWAYS_FATAL_IF(mFramesReady < (int64_t) avail);
buffer->mNonContig = mFramesReady == INT64_MAX ? SIZE_MAX : clampToSize(mFramesReady - avail);
mUnreleased = avail;
return NO_ERROR;
@@ -858,7 +958,7 @@ status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush
void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer)
{
size_t stepCount = buffer->mFrameCount;
- LOG_ALWAYS_FATAL_IF(!(stepCount <= mFramesReady));
+ LOG_ALWAYS_FATAL_IF(!((int64_t) stepCount <= mFramesReady));
LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased));
if (stepCount == 0) {
// prevent accidental re-use of buffer
@@ -868,11 +968,12 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer)
}
mUnreleased -= stepCount;
audio_track_cblk_t* cblk = mCblk;
- size_t position = mPosition;
+ size_t position = mState.mPosition;
size_t newPosition = position + stepCount;
int32_t setFlags = 0;
if (!(position <= newPosition && newPosition <= mFrameCount)) {
- ALOGW("%s newPosition %zu outside [%zu, %zu]", __func__, newPosition, position, mFrameCount);
+ ALOGW("%s newPosition %zu outside [%zu, %zu]", __func__, newPosition, position,
+ mFrameCount);
newPosition = mFrameCount;
} else if (mState.mLoopCount != 0 && newPosition == mState.mLoopEnd) {
newPosition = mState.mLoopStart;
@@ -885,7 +986,7 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer)
if (newPosition == mFrameCount) {
setFlags |= CBLK_BUFFER_END;
}
- mPosition = newPosition;
+ mState.mPosition = newPosition;
if (mFramesReady != INT64_MAX) {
mFramesReady -= stepCount;
}
@@ -893,7 +994,10 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer)
cblk->mServer += stepCount;
// This may overflow, but client is not supposed to rely on it
- cblk->u.mStatic.mBufferPosition = (uint32_t) newPosition;
+ StaticAudioTrackPosLoop posLoop;
+ posLoop.mBufferPosition = mState.mPosition;
+ posLoop.mLoopCount = mState.mLoopCount;
+ mPosLoopMutator.push(posLoop);
if (setFlags != 0) {
(void) android_atomic_or(setFlags, &cblk->mFlags);
// this would be a good place to wake a futex