Loading include/media/AudioTrack.h +25 −0 Original line number Diff line number Diff line Loading @@ -758,6 +758,9 @@ public: * The timestamp parameter is undefined on return, if status is not NO_ERROR. */ status_t getTimestamp(AudioTimestamp& timestamp); private: status_t getTimestamp_l(AudioTimestamp& timestamp); public: /* Return the extended timestamp, with additional timebase info and improved drain behavior. * Loading Loading @@ -840,6 +843,24 @@ public: status_t pendingDuration(int32_t *msec, ExtendedTimestamp::Location location = ExtendedTimestamp::LOCATION_SERVER); /* hasStarted() is used to determine if audio is now audible at the device after * a start() command. The underlying implementation checks a nonzero timestamp position * or increment for the audible assumption. * * hasStarted() returns true if the track has been started() and audio is audible * and no subsequent pause() or flush() has been called. Immediately after pause() or * flush() hasStarted() will return false. * * If stop() has been called, hasStarted() will return true if audio is still being * delivered or has finished delivery (even if no audio was written) for both offloaded * and normal tracks. This property removes a race condition in checking hasStarted() * for very short clips, where stop() must be called to finish drain. * * In all cases, hasStarted() may turn false briefly after a subsequent start() is called * until audio becomes audible again. */ bool hasStarted(); // not const protected: /* copying audio tracks is not allowed */ AudioTrack(const AudioTrack& other); Loading Loading @@ -1041,6 +1062,10 @@ protected: // and could be easily widened to uint64_t int64_t mStartUs; // the start time after flush or stop. // only used for offloaded and direct tracks. ExtendedTimestamp mStartEts; // Extended timestamp at start for normal // AudioTracks. AudioTimestamp mStartTs; // Timestamp at start for offloaded or direct // AudioTracks. bool mPreviousTimestampValid;// true if mPreviousTimestamp is valid bool mTimestampStartupGlitchReported; // reduce log spam Loading media/libmedia/AudioTrack.cpp +90 −7 Original line number Diff line number Diff line Loading @@ -543,6 +543,7 @@ status_t AudioTrack::set( mTimestampStartupGlitchReported = false; mRetrogradeMotionReported = false; mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID; mStartTs.mPosition = 0; mUnderrunCountOffset = 0; mFramesWritten = 0; mFramesWrittenServerOffset = 0; Loading Loading @@ -570,6 +571,17 @@ status_t AudioTrack::start() mState = STATE_ACTIVE; } (void) updateAndGetPosition_l(); // save start timestamp if (isOffloadedOrDirect_l()) { if (getTimestamp_l(mStartTs) != OK) { mStartTs.mPosition = 0; } } else { if (getTimestamp_l(&mStartEts) != OK) { mStartEts.clear(); } } if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) { // reset current position as seen by client to 0 mPosition = 0; Loading @@ -578,19 +590,17 @@ status_t AudioTrack::start() mRetrogradeMotionReported = false; mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID; // read last server side position change via timestamp. ExtendedTimestamp ets; if (mProxy->getTimestamp(&ets) == OK && ets.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] > 0) { if (!isOffloadedOrDirect_l() && mStartEts.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] > 0) { // Server side has consumed something, but is it finished consuming? // It is possible since flush and stop are asynchronous that the server // is still active at this point. ALOGV("start: server read:%lld cumulative flushed:%lld client written:%lld", (long long)(mFramesWrittenServerOffset + ets.mPosition[ExtendedTimestamp::LOCATION_SERVER]), (long long)ets.mFlushed, + mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER]), (long long)mStartEts.mFlushed, (long long)mFramesWritten); mFramesWrittenServerOffset = -ets.mPosition[ExtendedTimestamp::LOCATION_SERVER]; mFramesWrittenServerOffset = -mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER]; } mFramesWritten = 0; mProxy->clearTimestamp(); // need new server push for valid timestamp Loading Loading @@ -2328,7 +2338,11 @@ status_t AudioTrack::getTimestamp_l(ExtendedTimestamp *timestamp) status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) { AutoMutex lock(mLock); return getTimestamp_l(timestamp); } status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) { bool previousTimestampValid = mPreviousTimestampValid; // Set false here to cover all the error return cases. mPreviousTimestampValid = false; Loading Loading @@ -2750,6 +2764,75 @@ status_t AudioTrack::pendingDuration(int32_t *msec, ExtendedTimestamp::Location return NO_ERROR; } bool AudioTrack::hasStarted() { AutoMutex lock(mLock); switch (mState) { case STATE_STOPPED: if (isOffloadedOrDirect_l()) { // check if we have started in the past to return true. return mStartUs > 0; } // A normal audio track may still be draining, so // check if stream has ended. This covers fasttrack position // instability and start/stop without any data written. if (mProxy->getStreamEndDone()) { return true; } // fall through case STATE_ACTIVE: case STATE_STOPPING: break; case STATE_PAUSED: case STATE_PAUSED_STOPPING: case STATE_FLUSHED: return false; // we're not active default: LOG_ALWAYS_FATAL("Invalid mState in hasStarted(): %d", mState); break; } // wait indicates whether we need to wait for a timestamp. // This is conservatively figured - if we encounter an unexpected error // then we will not wait. bool wait = false; if (isOffloadedOrDirect_l()) { AudioTimestamp ts; status_t status = getTimestamp_l(ts); if (status == WOULD_BLOCK) { wait = true; } else if (status == OK) { wait = (ts.mPosition == 0 || ts.mPosition == mStartTs.mPosition); } ALOGV("hasStarted wait:%d ts:%u start position:%lld", (int)wait, ts.mPosition, (long long)mStartTs.mPosition); } else { int location = ExtendedTimestamp::LOCATION_SERVER; // for ALOG ExtendedTimestamp ets; status_t status = getTimestamp_l(&ets); if (status == WOULD_BLOCK) { // no SERVER or KERNEL frame info in ets wait = true; } else if (status == OK) { for (location = ExtendedTimestamp::LOCATION_KERNEL; location >= ExtendedTimestamp::LOCATION_SERVER; --location) { if (ets.mTimeNs[location] < 0 || mStartEts.mTimeNs[location] < 0) { continue; } wait = ets.mPosition[location] == 0 || ets.mPosition[location] == mStartEts.mPosition[location]; break; } } ALOGV("hasStarted wait:%d ets:%lld start position:%lld", (int)wait, (long long)ets.mPosition[location], (long long)mStartEts.mPosition[location]); } return !wait; } // ========================================================================= void AudioTrack::DeathNotifier::binderDied(const wp<IBinder>& who __unused) Loading Loading
include/media/AudioTrack.h +25 −0 Original line number Diff line number Diff line Loading @@ -758,6 +758,9 @@ public: * The timestamp parameter is undefined on return, if status is not NO_ERROR. */ status_t getTimestamp(AudioTimestamp& timestamp); private: status_t getTimestamp_l(AudioTimestamp& timestamp); public: /* Return the extended timestamp, with additional timebase info and improved drain behavior. * Loading Loading @@ -840,6 +843,24 @@ public: status_t pendingDuration(int32_t *msec, ExtendedTimestamp::Location location = ExtendedTimestamp::LOCATION_SERVER); /* hasStarted() is used to determine if audio is now audible at the device after * a start() command. The underlying implementation checks a nonzero timestamp position * or increment for the audible assumption. * * hasStarted() returns true if the track has been started() and audio is audible * and no subsequent pause() or flush() has been called. Immediately after pause() or * flush() hasStarted() will return false. * * If stop() has been called, hasStarted() will return true if audio is still being * delivered or has finished delivery (even if no audio was written) for both offloaded * and normal tracks. This property removes a race condition in checking hasStarted() * for very short clips, where stop() must be called to finish drain. * * In all cases, hasStarted() may turn false briefly after a subsequent start() is called * until audio becomes audible again. */ bool hasStarted(); // not const protected: /* copying audio tracks is not allowed */ AudioTrack(const AudioTrack& other); Loading Loading @@ -1041,6 +1062,10 @@ protected: // and could be easily widened to uint64_t int64_t mStartUs; // the start time after flush or stop. // only used for offloaded and direct tracks. ExtendedTimestamp mStartEts; // Extended timestamp at start for normal // AudioTracks. AudioTimestamp mStartTs; // Timestamp at start for offloaded or direct // AudioTracks. bool mPreviousTimestampValid;// true if mPreviousTimestamp is valid bool mTimestampStartupGlitchReported; // reduce log spam Loading
media/libmedia/AudioTrack.cpp +90 −7 Original line number Diff line number Diff line Loading @@ -543,6 +543,7 @@ status_t AudioTrack::set( mTimestampStartupGlitchReported = false; mRetrogradeMotionReported = false; mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID; mStartTs.mPosition = 0; mUnderrunCountOffset = 0; mFramesWritten = 0; mFramesWrittenServerOffset = 0; Loading Loading @@ -570,6 +571,17 @@ status_t AudioTrack::start() mState = STATE_ACTIVE; } (void) updateAndGetPosition_l(); // save start timestamp if (isOffloadedOrDirect_l()) { if (getTimestamp_l(mStartTs) != OK) { mStartTs.mPosition = 0; } } else { if (getTimestamp_l(&mStartEts) != OK) { mStartEts.clear(); } } if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) { // reset current position as seen by client to 0 mPosition = 0; Loading @@ -578,19 +590,17 @@ status_t AudioTrack::start() mRetrogradeMotionReported = false; mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID; // read last server side position change via timestamp. ExtendedTimestamp ets; if (mProxy->getTimestamp(&ets) == OK && ets.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] > 0) { if (!isOffloadedOrDirect_l() && mStartEts.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] > 0) { // Server side has consumed something, but is it finished consuming? // It is possible since flush and stop are asynchronous that the server // is still active at this point. ALOGV("start: server read:%lld cumulative flushed:%lld client written:%lld", (long long)(mFramesWrittenServerOffset + ets.mPosition[ExtendedTimestamp::LOCATION_SERVER]), (long long)ets.mFlushed, + mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER]), (long long)mStartEts.mFlushed, (long long)mFramesWritten); mFramesWrittenServerOffset = -ets.mPosition[ExtendedTimestamp::LOCATION_SERVER]; mFramesWrittenServerOffset = -mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER]; } mFramesWritten = 0; mProxy->clearTimestamp(); // need new server push for valid timestamp Loading Loading @@ -2328,7 +2338,11 @@ status_t AudioTrack::getTimestamp_l(ExtendedTimestamp *timestamp) status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) { AutoMutex lock(mLock); return getTimestamp_l(timestamp); } status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) { bool previousTimestampValid = mPreviousTimestampValid; // Set false here to cover all the error return cases. mPreviousTimestampValid = false; Loading Loading @@ -2750,6 +2764,75 @@ status_t AudioTrack::pendingDuration(int32_t *msec, ExtendedTimestamp::Location return NO_ERROR; } bool AudioTrack::hasStarted() { AutoMutex lock(mLock); switch (mState) { case STATE_STOPPED: if (isOffloadedOrDirect_l()) { // check if we have started in the past to return true. return mStartUs > 0; } // A normal audio track may still be draining, so // check if stream has ended. This covers fasttrack position // instability and start/stop without any data written. if (mProxy->getStreamEndDone()) { return true; } // fall through case STATE_ACTIVE: case STATE_STOPPING: break; case STATE_PAUSED: case STATE_PAUSED_STOPPING: case STATE_FLUSHED: return false; // we're not active default: LOG_ALWAYS_FATAL("Invalid mState in hasStarted(): %d", mState); break; } // wait indicates whether we need to wait for a timestamp. // This is conservatively figured - if we encounter an unexpected error // then we will not wait. bool wait = false; if (isOffloadedOrDirect_l()) { AudioTimestamp ts; status_t status = getTimestamp_l(ts); if (status == WOULD_BLOCK) { wait = true; } else if (status == OK) { wait = (ts.mPosition == 0 || ts.mPosition == mStartTs.mPosition); } ALOGV("hasStarted wait:%d ts:%u start position:%lld", (int)wait, ts.mPosition, (long long)mStartTs.mPosition); } else { int location = ExtendedTimestamp::LOCATION_SERVER; // for ALOG ExtendedTimestamp ets; status_t status = getTimestamp_l(&ets); if (status == WOULD_BLOCK) { // no SERVER or KERNEL frame info in ets wait = true; } else if (status == OK) { for (location = ExtendedTimestamp::LOCATION_KERNEL; location >= ExtendedTimestamp::LOCATION_SERVER; --location) { if (ets.mTimeNs[location] < 0 || mStartEts.mTimeNs[location] < 0) { continue; } wait = ets.mPosition[location] == 0 || ets.mPosition[location] == mStartEts.mPosition[location]; break; } } ALOGV("hasStarted wait:%d ets:%lld start position:%lld", (int)wait, (long long)ets.mPosition[location], (long long)mStartEts.mPosition[location]); } return !wait; } // ========================================================================= void AudioTrack::DeathNotifier::binderDied(const wp<IBinder>& who __unused) Loading