AudioTrack: Limit timestamp time lag after start
Test: Photos with long pause and resume
Bug: 63614330
Bug: 63976584
Change-Id: I81ac650c821600fee507c72c3b0c1be60f5e7ab6
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 31e1e2b..c212112 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -63,6 +63,14 @@
return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
}
+// TODO move to audio_utils.
+static inline struct timespec convertNsToTimespec(int64_t ns) {
+ struct timespec tv;
+ tv.tv_sec = static_cast<time_t>(ns / NANOS_PER_SECOND);
+ tv.tv_nsec = static_cast<long>(ns % NANOS_PER_SECOND);
+ return tv;
+}
+
// current monotonic time in microseconds.
static int64_t getNowUs()
{
@@ -539,7 +547,8 @@
mUpdatePeriod = 0;
mPosition = 0;
mReleased = 0;
- mStartUs = 0;
+ mStartNs = 0;
+ mStartFromZeroUs = 0;
AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
mSequence = 1;
mObservedSequence = mSequence;
@@ -587,6 +596,7 @@
mStartEts.clear();
}
}
+ mStartNs = systemTime(); // save this for timestamp adjustment after starting.
if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
// reset current position as seen by client to 0
mPosition = 0;
@@ -615,7 +625,7 @@
// since the flush is asynchronous and stop may not fully drain.
// We save the time when the track is started to later verify whether
// the counters are realistic (i.e. start from zero after this time).
- mStartUs = getNowUs();
+ mStartFromZeroUs = mStartNs / 1000;
// force refresh of remaining frames by processAudioBuffer() as last
// write before stop could be partial.
@@ -2571,8 +2581,7 @@
if (at < limit) {
ALOGV("timestamp pause lag:%lld adjusting from %lld to %lld",
(long long)lag, (long long)at, (long long)limit);
- timestamp.mTime.tv_sec = limit / NANOS_PER_SECOND;
- timestamp.mTime.tv_nsec = limit % NANOS_PER_SECOND; // compiler opt.
+ timestamp.mTime = convertNsToTimespec(limit);
}
}
mPreviousLocation = location;
@@ -2615,18 +2624,18 @@
// the previous song under gapless playback.
// However, we sometimes see zero timestamps, then a glitch of
// the previous song's position, and then correct timestamps afterwards.
- if (mStartUs != 0 && mSampleRate != 0) {
+ if (mStartFromZeroUs != 0 && mSampleRate != 0) {
static const int kTimeJitterUs = 100000; // 100 ms
static const int k1SecUs = 1000000;
const int64_t timeNow = getNowUs();
- if (timeNow < mStartUs + k1SecUs) { // within first second of starting
+ if (timeNow < mStartFromZeroUs + k1SecUs) { // within first second of starting
const int64_t timestampTimeUs = convertTimespecToUs(timestamp.mTime);
- if (timestampTimeUs < mStartUs) {
+ if (timestampTimeUs < mStartFromZeroUs) {
return WOULD_BLOCK; // stale timestamp time, occurs before start.
}
- const int64_t deltaTimeUs = timestampTimeUs - mStartUs;
+ const int64_t deltaTimeUs = timestampTimeUs - mStartFromZeroUs;
const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000
/ ((double)mSampleRate * mPlaybackRate.mSpeed);
@@ -2649,10 +2658,10 @@
return WOULD_BLOCK;
}
if (deltaPositionByUs != 0) {
- mStartUs = 0; // don't check again, we got valid nonzero position.
+ mStartFromZeroUs = 0; // don't check again, we got valid nonzero position.
}
} else {
- mStartUs = 0; // don't check again, start time expired.
+ mStartFromZeroUs = 0; // don't check again, start time expired.
}
mTimestampStartupGlitchReported = false;
}
@@ -2690,14 +2699,33 @@
// Prevent retrograde motion in timestamp.
// This is sometimes caused by erratic reports of the available space in the ALSA drivers.
if (status == NO_ERROR) {
+ // previousTimestampValid is set to false when starting after a stop or flush.
if (previousTimestampValid) {
const int64_t previousTimeNanos =
audio_utils_ns_from_timespec(&mPreviousTimestamp.mTime);
- const int64_t currentTimeNanos = audio_utils_ns_from_timespec(×tamp.mTime);
+ int64_t currentTimeNanos = audio_utils_ns_from_timespec(×tamp.mTime);
+
+ // Fix stale time when checking timestamp right after start().
+ //
+ // For offload compatibility, use a default lag value here.
+ // Any time discrepancy between this update and the pause timestamp is handled
+ // by the retrograde check afterwards.
+ const int64_t lagNs = int64_t(mAfLatency * 1000000LL);
+ const int64_t limitNs = mStartNs - lagNs;
+ if (currentTimeNanos < limitNs) {
+ ALOGD("correcting timestamp time for pause, "
+ "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld",
+ (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs);
+ timestamp.mTime = convertNsToTimespec(limitNs);
+ currentTimeNanos = limitNs;
+ }
+
+ // retrograde check
if (currentTimeNanos < previousTimeNanos) {
ALOGW("retrograde timestamp time corrected, %lld < %lld",
(long long)currentTimeNanos, (long long)previousTimeNanos);
timestamp.mTime = mPreviousTimestamp.mTime;
+ // currentTimeNanos not used below.
}
// Looking at signed delta will work even when the timestamps
@@ -2907,7 +2935,7 @@
case STATE_STOPPED:
if (isOffloadedOrDirect_l()) {
// check if we have started in the past to return true.
- return mStartUs > 0;
+ return mStartFromZeroUs > 0;
}
// A normal audio track may still be draining, so
// check if stream has ended. This covers fasttrack position