summaryrefslogtreecommitdiffstats
path: root/services/audioflinger/Tracks.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'services/audioflinger/Tracks.cpp')
-rw-r--r--services/audioflinger/Tracks.cpp150
1 files changed, 72 insertions, 78 deletions
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index e970036..dc9f249 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -20,6 +20,7 @@
//#define LOG_NDEBUG 0
#include "Configuration.h"
+#include <linux/futex.h>
#include <math.h>
#include <sys/syscall.h>
#include <utils/Log.h>
@@ -404,9 +405,7 @@ AudioFlinger::PlaybackThread::Track::Track(
mAudioTrackServerProxy(NULL),
mResumeToStopping(false),
mFlushHwPending(false),
- mPreviousValid(false),
- mPreviousFramesWritten(0)
- // mPreviousTimestamp
+ mPreviousTimestampValid(false)
{
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -443,8 +442,6 @@ AudioFlinger::PlaybackThread::Track::Track(
// this means we are potentially denying other more important fast tracks from
// being created. It would be better to allocate the index dynamically.
mFastIndex = i;
- // Read the initial underruns because this field is never cleared by the fast mixer
- mObservedUnderruns = thread->getFastTrackUnderruns(i);
thread->mFastTrackAvailMask &= ~(1 << i);
}
}
@@ -693,6 +690,12 @@ status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t ev
}
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if (isFastTrack()) {
+ // refresh fast track underruns on start because that field is never cleared
+ // by the fast mixer; furthermore, the same track can be recycled, i.e. start
+ // after stop.
+ mObservedUnderruns = playbackThread->getFastTrackUnderruns(mFastIndex);
+ }
status = playbackThread->addTrack_l(this);
if (status == INVALID_OPERATION || status == PERMISSION_DENIED) {
triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
@@ -742,6 +745,7 @@ void AudioFlinger::PlaybackThread::Track::stop()
// move to STOPPING_2 when drain completes and then STOPPED
mState = STOPPING_1;
}
+ playbackThread->broadcast_l();
ALOGV("not stopping/stopped => stopping/stopped (%d) on thread %p", mName,
playbackThread);
}
@@ -859,6 +863,7 @@ void AudioFlinger::PlaybackThread::Track::reset()
if (mState == FLUSHED) {
mState = IDLE;
}
+ mPreviousTimestampValid = false;
}
}
@@ -880,19 +885,22 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times
{
// Client should implement this using SSQ; the unpresented frame count in latch is irrelevant
if (isFastTrack()) {
- // FIXME no lock held to set mPreviousValid = false
+ // FIXME no lock held to set mPreviousTimestampValid = false
return INVALID_OPERATION;
}
sp<ThreadBase> thread = mThread.promote();
if (thread == 0) {
- // FIXME no lock held to set mPreviousValid = false
+ // FIXME no lock held to set mPreviousTimestampValid = false
return INVALID_OPERATION;
}
+
Mutex::Autolock _l(thread->mLock);
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+
+ status_t result = INVALID_OPERATION;
if (!isOffloaded() && !isDirect()) {
if (!playbackThread->mLatchQValid) {
- mPreviousValid = false;
+ mPreviousTimestampValid = false;
return INVALID_OPERATION;
}
uint32_t unpresentedFrames =
@@ -908,36 +916,54 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times
uint32_t framesWritten = i >= 0 ?
playbackThread->mLatchQ.mFramesReleased[i] :
mAudioTrackServerProxy->framesReleased();
- bool checkPreviousTimestamp = mPreviousValid && framesWritten >= mPreviousFramesWritten;
if (framesWritten < unpresentedFrames) {
- mPreviousValid = false;
- return INVALID_OPERATION;
+ mPreviousTimestampValid = false;
+ // return invalid result
+ } else {
+ timestamp.mPosition = framesWritten - unpresentedFrames;
+ timestamp.mTime = playbackThread->mLatchQ.mTimestamp.mTime;
+ result = NO_ERROR;
}
- mPreviousFramesWritten = framesWritten;
- uint32_t position = framesWritten - unpresentedFrames;
- struct timespec time = playbackThread->mLatchQ.mTimestamp.mTime;
- if (checkPreviousTimestamp) {
- if (time.tv_sec < mPreviousTimestamp.mTime.tv_sec ||
- (time.tv_sec == mPreviousTimestamp.mTime.tv_sec &&
- time.tv_nsec < mPreviousTimestamp.mTime.tv_nsec)) {
- ALOGW("Time is going backwards");
+ } else { // offloaded or direct
+ result = playbackThread->getTimestamp_l(timestamp);
+ }
+
+ // Prevent retrograde motion in timestamp.
+ if (result == NO_ERROR) {
+ if (mPreviousTimestampValid) {
+ if (timestamp.mTime.tv_sec < mPreviousTimestamp.mTime.tv_sec ||
+ (timestamp.mTime.tv_sec == mPreviousTimestamp.mTime.tv_sec &&
+ timestamp.mTime.tv_nsec < mPreviousTimestamp.mTime.tv_nsec)) {
+ ALOGW("WARNING - retrograde timestamp time");
+ // FIXME Consider blocking this from propagating upwards.
}
+
+ // Looking at signed delta will work even when the timestamps
+ // are wrapping around.
+ int32_t deltaPosition = static_cast<int32_t>(timestamp.mPosition
+ - mPreviousTimestamp.mPosition);
// position can bobble slightly as an artifact; this hides the bobble
- static const uint32_t MINIMUM_POSITION_DELTA = 8u;
- if ((position <= mPreviousTimestamp.mPosition) ||
- (position - mPreviousTimestamp.mPosition) < MINIMUM_POSITION_DELTA) {
- position = mPreviousTimestamp.mPosition;
- time = mPreviousTimestamp.mTime;
+ static const int32_t MINIMUM_POSITION_DELTA = 8;
+ if (deltaPosition < 0) {
+#define TIME_TO_NANOS(time) ((uint64_t)time.tv_sec * 1000000000 + time.tv_nsec)
+ ALOGW("WARNING - retrograde timestamp position corrected,"
+ " %d = %u - %u, (at %llu, %llu nanos)",
+ deltaPosition,
+ timestamp.mPosition,
+ mPreviousTimestamp.mPosition,
+ TIME_TO_NANOS(timestamp.mTime),
+ TIME_TO_NANOS(mPreviousTimestamp.mTime));
+#undef TIME_TO_NANOS
+ }
+ if (deltaPosition < MINIMUM_POSITION_DELTA) {
+ // Current timestamp is bad. Use last valid timestamp.
+ timestamp = mPreviousTimestamp;
}
}
- timestamp.mPosition = position;
- timestamp.mTime = time;
mPreviousTimestamp = timestamp;
- mPreviousValid = true;
- return NO_ERROR;
+ mPreviousTimestampValid = true;
}
-
- return playbackThread->getTimestamp_l(timestamp);
+ return result;
}
status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId)
@@ -1709,36 +1735,18 @@ void AudioFlinger::PlaybackThread::OutputTrack::stop()
mActive = false;
}
-bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t frames)
+bool AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frames)
{
Buffer *pInBuffer;
Buffer inBuffer;
- uint32_t channelCount = mChannelCount;
bool outputBufferFull = false;
inBuffer.frameCount = frames;
- inBuffer.i16 = data;
+ inBuffer.raw = data;
uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs();
if (!mActive && frames != 0) {
- start();
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- MixerThread *mixerThread = (MixerThread *)thread.get();
- if (mFrameCount > frames) {
- if (mBufferQueue.size() < kMaxOverFlowBuffers) {
- uint32_t startFrames = (mFrameCount - frames);
- pInBuffer = new Buffer;
- pInBuffer->mBuffer = new int16_t[startFrames * channelCount];
- pInBuffer->frameCount = startFrames;
- pInBuffer->i16 = pInBuffer->mBuffer;
- memset(pInBuffer->raw, 0, startFrames * channelCount * sizeof(int16_t));
- mBufferQueue.add(pInBuffer);
- } else {
- ALOGW("OutputTrack::write() %p no more buffers in queue", this);
- }
- }
- }
+ (void) start();
}
while (waitTimeLeftMs) {
@@ -1773,20 +1781,20 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount :
pInBuffer->frameCount;
- memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t));
+ memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * mFrameSize);
Proxy::Buffer buf;
buf.mFrameCount = outFrames;
buf.mRaw = NULL;
mClientProxy->releaseBuffer(&buf);
pInBuffer->frameCount -= outFrames;
- pInBuffer->i16 += outFrames * channelCount;
+ pInBuffer->raw = (int8_t *)pInBuffer->raw + outFrames * mFrameSize;
mOutBuffer.frameCount -= outFrames;
- mOutBuffer.i16 += outFrames * channelCount;
+ mOutBuffer.raw = (int8_t *)mOutBuffer.raw + outFrames * mFrameSize;
if (pInBuffer->frameCount == 0) {
if (mBufferQueue.size()) {
mBufferQueue.removeAt(0);
- delete [] pInBuffer->mBuffer;
+ free(pInBuffer->mBuffer);
delete pInBuffer;
ALOGV("OutputTrack::write() %p thread %p released overflow buffer %d", this,
mThread.unsafe_get(), mBufferQueue.size());
@@ -1802,11 +1810,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
if (thread != 0 && !thread->standby()) {
if (mBufferQueue.size() < kMaxOverFlowBuffers) {
pInBuffer = new Buffer;
- pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channelCount];
+ pInBuffer->mBuffer = malloc(inBuffer.frameCount * mFrameSize);
pInBuffer->frameCount = inBuffer.frameCount;
- pInBuffer->i16 = pInBuffer->mBuffer;
- memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channelCount *
- sizeof(int16_t));
+ pInBuffer->raw = pInBuffer->mBuffer;
+ memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * mFrameSize);
mBufferQueue.add(pInBuffer);
ALOGV("OutputTrack::write() %p thread %p adding overflow buffer %d", this,
mThread.unsafe_get(), mBufferQueue.size());
@@ -1817,23 +1824,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
}
}
- // Calling write() with a 0 length buffer, means that no more data will be written:
- // If no more buffers are pending, fill output track buffer to make sure it is started
- // by output mixer.
- if (frames == 0 && mBufferQueue.size() == 0) {
- // FIXME borken, replace by getting framesReady() from proxy
- size_t user = 0; // was mCblk->user
- if (user < mFrameCount) {
- frames = mFrameCount - user;
- pInBuffer = new Buffer;
- pInBuffer->mBuffer = new int16_t[frames * channelCount];
- pInBuffer->frameCount = frames;
- pInBuffer->i16 = pInBuffer->mBuffer;
- memset(pInBuffer->raw, 0, frames * channelCount * sizeof(int16_t));
- mBufferQueue.add(pInBuffer);
- } else if (mActive) {
- stop();
- }
+ // Calling write() with a 0 length buffer means that no more data will be written:
+ // We rely on stop() to set the appropriate flags to allow the remaining frames to play out.
+ if (frames == 0 && mBufferQueue.size() == 0 && mActive) {
+ stop();
}
return outputBufferFull;
@@ -1859,7 +1853,7 @@ void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue()
for (size_t i = 0; i < size; i++) {
Buffer *pBuffer = mBufferQueue.itemAt(i);
- delete [] pBuffer->mBuffer;
+ free(pBuffer->mBuffer);
delete pBuffer;
}
mBufferQueue.clear();
@@ -2212,4 +2206,4 @@ void AudioFlinger::RecordThread::PatchRecord::releaseBuffer(Proxy::Buffer* buffe
mProxy->releaseBuffer(buffer);
}
-}; // namespace android
+} // namespace android