Implement client playback timestamps with 64 bit accuracy
Provide server timestamps if the HAL doesn't provide it.
Provide monotonic - boottime translation.
Bug: 17472992
Bug: 26682703
Bug: 27749434
Change-Id: I6c9b213d9f9284092e34d57f52870e02c72df62a
diff --git a/include/media/AudioTimestamp.h b/include/media/AudioTimestamp.h
index 531b548..969003c 100644
--- a/include/media/AudioTimestamp.h
+++ b/include/media/AudioTimestamp.h
@@ -69,12 +69,23 @@
// or NTP adjustment.
int64_t mTimebaseOffset[TIMEBASE_MAX];
+ // Playback only:
+ // mFlushed is number of flushed frames before entering the server mix;
+ // hence not included in mPosition. This is used for adjusting server positions
+ // information for frames "dropped".
+ // FIXME: This variable should be eliminated, with the offset added on the server side
+ // before sending to client, but differences in legacy position offset handling
+ // and new extended timestamps require this to be maintained as a separate quantity.
+ int64_t mFlushed;
+
+ // Call to reset the timestamp to the original (invalid) state
void clear() {
memset(mPosition, 0, sizeof(mPosition)); // actually not necessary if time is -1
for (int i = 0; i < LOCATION_MAX; ++i) {
mTimeNs[i] = -1;
}
memset(mTimebaseOffset, 0, sizeof(mTimebaseOffset));
+ mFlushed = 0;
}
// Returns the best timestamp as judged from the closest-to-hw stage in the
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index bdd6372..b96a450 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -727,12 +727,51 @@
* because the audio device changed or AudioFlinger died.
* This typically occurs for direct or offload tracks
* or if mDoNotReconnect is true.
- * INVALID_OPERATION if called on a FastTrack, wrong state, or some other error.
+ * INVALID_OPERATION wrong state, or some other error.
*
* The timestamp parameter is undefined on return, if status is not NO_ERROR.
*/
status_t getTimestamp(AudioTimestamp& timestamp);
+ /* Return the extended timestamp, with additional timebase info and improved drain behavior.
+ *
+ * This is similar to the AudioTrack.java API:
+ * getTimestamp(@NonNull AudioTimestamp timestamp, @AudioTimestamp.Timebase int timebase)
+ *
+ * Some differences between this method and the getTimestamp(AudioTimestamp& timestamp) method
+ *
+ * 1. stop() by itself does not reset the frame position.
+ * A following start() resets the frame position to 0.
+ * 2. flush() by itself does not reset the frame position.
+ * The frame position advances by the number of frames flushed,
+ * when the first frame after flush reaches the audio sink.
+ * 3. BOOTTIME clock offsets are provided to help synchronize with
+ * non-audio streams, e.g. sensor data.
+ * 4. Position is returned with 64 bits of resolution.
+ *
+ * Parameters:
+ * timestamp: A pointer to the caller allocated ExtendedTimestamp.
+ *
+ * Returns NO_ERROR on success; timestamp is filled with valid data.
+ * BAD_VALUE if timestamp is NULL.
+ * WOULD_BLOCK if called immediately after start() when the number
+ * of frames consumed is less than the
+ * overall hardware latency to physical output. In WOULD_BLOCK cases,
+ * one might poll again, or use getPosition(), or use 0 position and
+ * current time for the timestamp.
+ * If WOULD_BLOCK is returned, the timestamp is still
+ * modified with the LOCATION_CLIENT portion filled.
+ * DEAD_OBJECT if AudioFlinger dies or the output device changes and
+ * the track cannot be automatically restored.
+ * The application needs to recreate the AudioTrack
+ * because the audio device changed or AudioFlinger died.
+ * This typically occurs for direct or offloaded tracks
+ * or if mDoNotReconnect is true.
+ * INVALID_OPERATION if called on a offloaded or direct track.
+ * Use getTimestamp(AudioTimestamp& timestamp) instead.
+ */
+ status_t getTimestamp(ExtendedTimestamp *timestamp);
+
/* Add an AudioDeviceCallback. The caller will be notified when the audio device to which this
* AudioTrack is routed is updated.
* Replaces any previously installed callback.
@@ -956,6 +995,13 @@
uint32_t mUnderrunCountOffset; // updated when restoring tracks
+ int64_t mFramesWritten; // total frames written. reset to zero after
+ // the start() following stop(). It is not
+ // changed after restoring the track or
+ // after flush.
+ int64_t mFramesWrittenServerOffset; // An offset to server frames due to
+ // restoring AudioTrack, or stop/start.
+
audio_output_flags_t mFlags; // same as mOrigFlags, except for bits that may
// be denied by client or server, such as
// AUDIO_OUTPUT_FLAG_FAST. mLock must be
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 92cf6bb..ffdb9b5 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -522,6 +522,9 @@
mTimestampMutator.push(timestamp);
}
+ // Total count of the number of flushed frames since creation (never reset).
+ virtual int64_t framesFlushed() const { return mFlushed; }
+
// Get dynamic buffer size from the shared control block.
uint32_t getBufferSizeInFrames() const {
return android_atomic_acquire_load((int32_t *)&mCblk->mBufferSizeInFrames);
@@ -531,7 +534,7 @@
size_t mAvailToClient; // estimated frames available to client prior to releaseBuffer()
int32_t mFlush; // our copy of cblk->u.mStreaming.mFlush, for streaming output only
int64_t mReleased; // our copy of cblk->mServer, at 64 bit resolution
-
+ int64_t mFlushed; // flushed frames to account for client-server discrepancy
ExtendedTimestampQueue::Mutator mTimestampMutator;
};
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index e70c611..1de91bf 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -500,6 +500,8 @@
mTimestampStartupGlitchReported = false;
mRetrogradeMotionReported = false;
mUnderrunCountOffset = 0;
+ mFramesWritten = 0;
+ mFramesWrittenServerOffset = 0;
return NO_ERROR;
}
@@ -537,6 +539,14 @@
// Note: the if is technically unnecessary because previousState == STATE_FLUSHED
// is only for streaming tracks, and mMarkerReached is already set to false.
if (previousState == STATE_STOPPED) {
+ // read last server side position change via timestamp
+ ExtendedTimestamp ets;
+ if (mProxy->getTimestamp(&ets) == OK &&
+ ets.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] > 0) {
+ mFramesWrittenServerOffset = -(ets.mPosition[ExtendedTimestamp::LOCATION_SERVER]
+ + ets.mFlushed);
+ }
+ mFramesWritten = 0;
mProxy->clearTimestamp(); // need new server push for valid timestamp
mMarkerReached = false;
}
@@ -1657,6 +1667,9 @@
releaseBuffer(&audioBuffer);
}
+ if (written > 0) {
+ mFramesWritten += written / mFrameSize;
+ }
return written;
}
@@ -1923,6 +1936,7 @@
requested = &timeout;
}
+ size_t writtenFrames = 0;
while (mRemainingFrames > 0) {
Buffer audioBuffer;
@@ -2024,6 +2038,7 @@
}
releaseBuffer(&audioBuffer);
+ writtenFrames += releasedFrames;
// 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
@@ -2047,6 +2062,10 @@
#endif
}
+ if (writtenFrames > 0) {
+ AutoMutex lock(mLock);
+ mFramesWritten += writtenFrames;
+ }
mRemainingFrames = notificationFrames;
mRetryOnPartialBuffer = true;
@@ -2109,6 +2128,7 @@
}
if (mState == STATE_ACTIVE) {
result = mAudioTrack->start();
+ mFramesWrittenServerOffset = mFramesWritten; // server resets to zero so we offset
}
}
if (result != NO_ERROR) {
@@ -2162,6 +2182,42 @@
return mAudioTrack->setParameters(keyValuePairs);
}
+status_t AudioTrack::getTimestamp(ExtendedTimestamp *timestamp)
+{
+ if (timestamp == nullptr) {
+ return BAD_VALUE;
+ }
+ AutoMutex lock(mLock);
+ if (mCblk->mFlags & CBLK_INVALID) {
+ const status_t status = restoreTrack_l("getTimestampExtended");
+ if (status != OK) {
+ // per getTimestamp() API doc in header, we return DEAD_OBJECT here,
+ // recommending that the track be recreated.
+ return DEAD_OBJECT;
+ }
+ }
+ // check for offloaded/direct here in case restoring somehow changed those flags.
+ if (isOffloadedOrDirect_l()) {
+ return INVALID_OPERATION; // not supported
+ }
+ status_t status = mProxy->getTimestamp(timestamp);
+ bool found = false;
+ if (status == OK) {
+ timestamp->mPosition[ExtendedTimestamp::LOCATION_CLIENT] = mFramesWritten;
+ timestamp->mTimeNs[ExtendedTimestamp::LOCATION_CLIENT] = 0;
+ // server side frame offset in case AudioTrack has been restored.
+ for (int i = ExtendedTimestamp::LOCATION_SERVER;
+ i < ExtendedTimestamp::LOCATION_MAX; ++i) {
+ if (timestamp->mTimeNs[i] >= 0) {
+ // apply server offset and the "flush frame correction here"
+ timestamp->mPosition[i] += mFramesWrittenServerOffset + timestamp->mFlushed;
+ found = true;
+ }
+ }
+ }
+ return found ? OK : WOULD_BLOCK;
+}
+
status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp)
{
AutoMutex lock(mLock);
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 2396d87..7119517 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -615,7 +615,7 @@
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),
- mAvailToClient(0), mFlush(0), mReleased(0)
+ mAvailToClient(0), mFlush(0), mReleased(0), mFlushed(0)
, mTimestampMutator(&cblk->mExtendedTimestampQueue)
{
cblk->mBufferSizeInFrames = frameCount;
@@ -671,6 +671,7 @@
mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
}
}
+ mFlushed += (newFront - front) & mask;
front = newFront;
}
} else {
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 7cbb6b8..00de4f0 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1113,6 +1113,9 @@
if (!checked) { // no server info, assume drained.
mAudioTrackServerProxy->setDrained(true);
}
+ // Set correction for flushed frames that are not accounted for in released.
+ // This is important for the new 64 bit timestamps which do not reset to 0 on flush.
+ local.mFlushed = mAudioTrackServerProxy->framesFlushed();
mServerProxy->setTimestamp(local);
}