AudioTrack: Add getUnderrunCount()
This allows an app to detect application-level output glitches.
Underrun counts survive track recreation.
Change-Id: I8eb14e92f6fc1007718a29b0666ab51ace30cdb8
Bug: 25641253
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index fe4611c..602e1f3 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -305,6 +305,11 @@
*/
uint32_t latency() const { return mLatency; }
+ /* Returns the number of application-level buffer underruns
+ * since the AudioTrack was created.
+ */
+ uint32_t getUnderrunCount() const;
+
/* getters, see constructors and set() */
audio_stream_type_t streamType() const;
@@ -784,6 +789,8 @@
// FIXME enum is faster than strcmp() for parameter 'from'
status_t restoreTrack_l(const char *from);
+ uint32_t getUnderrunCount_l() const;
+
bool isOffloaded() const;
bool isDirect() const;
bool isOffloadedOrDirect() const;
@@ -911,6 +918,8 @@
bool mRetrogradeMotionReported; // reduce log spam
AudioTimestamp mPreviousTimestamp; // used to detect retrograde motion
+ uint32_t mUnderrunCountOffset; // updated when restoring tracks
+
audio_output_flags_t mFlags;
// const after set(), except for bits AUDIO_OUTPUT_FLAG_FAST and AUDIO_OUTPUT_FLAG_OFFLOAD.
// mLock must be held to read or write those bits reliably.
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index e458f3c..fd6a160 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -59,7 +59,8 @@
volatile int32_t mRear; // written by producer (output: client, input: server)
volatile int32_t mFlush; // incremented by client to indicate a request to flush;
// server notices and discards all data between mFront and mRear
- volatile uint32_t mUnderrunFrames; // server increments for each unavailable but desired frame
+ volatile uint32_t mUnderrunFrames; // server increments for each unavailable but desired frame
+ volatile uint32_t mUnderrunCount; // server increments for each underrun occurrence
};
// Represents a single state of an AudioTrack that was created in static mode (shared memory buffer
@@ -174,8 +175,6 @@
volatile int32_t mFlags; // combinations of CBLK_*
- // Cache line boundary (32 bytes)
-
public:
union {
AudioTrackSharedStreaming mStreaming;
@@ -347,6 +346,9 @@
virtual uint32_t getUnderrunFrames() const {
return mCblk->u.mStreaming.mUnderrunFrames;
}
+ virtual uint32_t getUnderrunCount() const {
+ return mCblk->u.mStreaming.mUnderrunCount;
+ }
bool clearStreamEndDone(); // and return previous value
@@ -471,7 +473,8 @@
AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
size_t frameSize, bool clientInServer = false, uint32_t sampleRate = 0)
: ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer),
- mPlaybackRateObserver(&cblk->mPlaybackRateQueue) {
+ mPlaybackRateObserver(&cblk->mPlaybackRateQueue),
+ mUnderrunCount(0), mUnderrunning(false) {
mCblk->mSampleRate = sampleRate;
mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
}
@@ -514,6 +517,10 @@
private:
AudioPlaybackRate mPlaybackRate; // last observed playback rate
PlaybackRateQueue::Observer mPlaybackRateObserver;
+
+ // The server keeps a copy here where it is safe from the client.
+ uint32_t mUnderrunCount; // echoed to mCblk
+ bool mUnderrunning; // used to detect edge of underrun
};
class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 5e14940..31e88c3 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -493,6 +493,7 @@
mPreviousTimestampValid = false;
mTimestampStartupGlitchReported = false;
mRetrogradeMotionReported = false;
+ mUnderrunCountOffset = 0;
return NO_ERROR;
}
@@ -2112,6 +2113,9 @@
return DEAD_OBJECT;
}
+ // Save so we can return count since creation.
+ mUnderrunCountOffset = getUnderrunCount_l();
+
// save the old static buffer position
size_t bufferPosition = 0;
int loopCount = 0;
@@ -2427,6 +2431,17 @@
return NO_ERROR;
}
+uint32_t AudioTrack::getUnderrunCount() const
+{
+ AutoMutex lock(mLock);
+ return getUnderrunCount_l();
+}
+
+uint32_t AudioTrack::getUnderrunCount_l() const
+{
+ return mProxy->getUnderrunCount() + mUnderrunCountOffset;
+}
+
uint32_t AudioTrack::getUnderrunFrames() const
{
AutoMutex lock(mLock);
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 9fad500..716a614 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -781,10 +781,25 @@
void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
{
audio_track_cblk_t* cblk = mCblk;
- cblk->u.mStreaming.mUnderrunFrames += frameCount;
+ if (frameCount > 0) {
+ cblk->u.mStreaming.mUnderrunFrames += frameCount;
- // FIXME also wake futex so that underrun is noticed more quickly
- (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
+ if (!mUnderrunning) { // start of underrun?
+ mUnderrunCount++;
+ cblk->u.mStreaming.mUnderrunCount = mUnderrunCount;
+ mUnderrunning = true;
+ ALOGV("tallyUnderrunFrames(%3u) at uf = %u, bump mUnderrunCount = %u",
+ frameCount, cblk->u.mStreaming.mUnderrunFrames, mUnderrunCount);
+ }
+
+ // FIXME also wake futex so that underrun is noticed more quickly
+ (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
+ } else {
+ ALOGV_IF(mUnderrunning,
+ "tallyUnderrunFrames(%3u) at uf = %u, underrun finished",
+ frameCount, cblk->u.mStreaming.mUnderrunFrames);
+ mUnderrunning = false; // so we can detect the next edge
+ }
}
AudioPlaybackRate AudioTrackServerProxy::getPlaybackRate()
@@ -1010,7 +1025,7 @@
buffer->mNonContig = 0;
}
-void StaticAudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount __unused)
+void StaticAudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
{
// Unlike AudioTrackServerProxy::tallyUnderrunFrames() used for streaming tracks,
// we don't have a location to count underrun frames. The underrun frame counter
@@ -1018,7 +1033,9 @@
// possible for static buffer tracks other than at end of buffer, so this is not a loss.
// FIXME also wake futex so that underrun is noticed more quickly
- (void) android_atomic_or(CBLK_UNDERRUN, &mCblk->mFlags);
+ if (frameCount > 0) {
+ (void) android_atomic_or(CBLK_UNDERRUN, &mCblk->mFlags);
+ }
}
// ---------------------------------------------------------------------------
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 65166b0..0458554 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3788,6 +3788,8 @@
recentUnderruns > 0) {
// FIXME fast mixer will pull & mix partial buffers, but we count as a full underrun
track->mAudioTrackServerProxy->tallyUnderrunFrames(recentUnderruns * mFrameCount);
+ } else {
+ track->mAudioTrackServerProxy->tallyUnderrunFrames(0);
}
// This is similar to the state machine for normal tracks,
@@ -4157,7 +4159,10 @@
ALOGV("track(%p) underrun, framesReady(%zu) < framesDesired(%zd)",
track, framesReady, desiredFrames);
track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
+ } else {
+ track->mAudioTrackServerProxy->tallyUnderrunFrames(0);
}
+
// clear effect chain input buffer if an active track underruns to avoid sending
// previous audio buffer again to effects
chain = getEffectChain_l(track->sessionId());
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index b1638ea..c753afd 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -600,7 +600,10 @@
buffer->raw = buf.mRaw;
if (buf.mFrameCount == 0) {
mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
+ } else {
+ mAudioTrackServerProxy->tallyUnderrunFrames(0);
}
+
return status;
}