AudioTrack: Advance timestamp time during pause

Update MediaPlayer code to match.

This will change the behavior on the AudioTrack Java side,
but is within the API and should make timestamp handling easier.

Test: Photos app with pause and play.
Bug: 30502030
Change-Id: I0e9129ce6af5b920ad71e63ad0c205de7561acf2
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index c96f16a..96fe907 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -50,6 +50,8 @@
     return x > y ? x : y;
 }
 
+static const int32_t NANOS_PER_SECOND = 1000000000;
+
 static inline nsecs_t framesToNanoseconds(ssize_t frames, uint32_t sampleRate, float speed)
 {
     return ((double)frames * 1000000000) / ((double)sampleRate * speed);
@@ -60,6 +62,11 @@
     return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
 }
 
+static inline nsecs_t convertTimespecToNs(const struct timespec &tv)
+{
+    return tv.tv_sec * (long long)NANOS_PER_SECOND + tv.tv_nsec;
+}
+
 // current monotonic time in microseconds.
 static int64_t getNowUs()
 {
@@ -2393,6 +2400,26 @@
                     ALOGV_IF(mPreviousLocation == ExtendedTimestamp::LOCATION_SERVER,
                             "getTimestamp() location moved from server to kernel");
                 }
+
+                // We update the timestamp time even when paused.
+                if (mState == STATE_PAUSED /* not needed: STATE_PAUSED_STOPPING */) {
+                    const int64_t now = systemTime();
+                    const int64_t at = convertTimespecToNs(timestamp.mTime);
+                    const int64_t lag =
+                            (ets.mTimeNs[ExtendedTimestamp::LOCATION_SERVER_LASTKERNELOK] < 0 ||
+                                ets.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL_LASTKERNELOK] < 0)
+                            ? int64_t(mAfLatency * 1000000LL)
+                            : (ets.mPosition[ExtendedTimestamp::LOCATION_SERVER_LASTKERNELOK]
+                             - ets.mPosition[ExtendedTimestamp::LOCATION_KERNEL_LASTKERNELOK])
+                             * NANOS_PER_SECOND / mSampleRate;
+                    const int64_t limit = now - lag; // no earlier than this limit
+                    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.
+                    }
+                }
                 mPreviousLocation = location;
             } else {
                 // right after AudioTrack is started, one may not find a timestamp
@@ -2412,6 +2439,7 @@
             // use cached paused position in case another offloaded track is running.
             timestamp.mPosition = mPausedPosition;
             clock_gettime(CLOCK_MONOTONIC, &timestamp.mTime);
+            // TODO: adjust for delay
             return NO_ERROR;
         }
 
@@ -2498,21 +2526,18 @@
     // This is sometimes caused by erratic reports of the available space in the ALSA drivers.
     if (status == NO_ERROR) {
         if (previousTimestampValid) {
-#define TIME_TO_NANOS(time) ((int64_t)(time).tv_sec * 1000000000 + (time).tv_nsec)
-            const int64_t previousTimeNanos = TIME_TO_NANOS(mPreviousTimestamp.mTime);
-            const int64_t currentTimeNanos = TIME_TO_NANOS(timestamp.mTime);
-#undef TIME_TO_NANOS
+            const int64_t previousTimeNanos = convertTimespecToNs(mPreviousTimestamp.mTime);
+            const int64_t currentTimeNanos = convertTimespecToNs(timestamp.mTime);
             if (currentTimeNanos < previousTimeNanos) {
-                ALOGW("retrograde timestamp time");
-                // FIXME Consider blocking this from propagating upwards.
+                ALOGW("retrograde timestamp time corrected, %lld < %lld",
+                        (long long)currentTimeNanos, (long long)previousTimeNanos);
+                timestamp.mTime = mPreviousTimestamp.mTime;
             }
 
             // Looking at signed delta will work even when the timestamps
             // are wrapping around.
             int32_t deltaPosition = (Modulo<uint32_t>(timestamp.mPosition)
                     - mPreviousTimestamp.mPosition).signedValue();
-            // position can bobble slightly as an artifact; this hides the bobble
-            static const int32_t MINIMUM_POSITION_DELTA = 8;
             if (deltaPosition < 0) {
                 // Only report once per position instead of spamming the log.
                 if (!mRetrogradeMotionReported) {
@@ -2525,9 +2550,21 @@
             } else {
                 mRetrogradeMotionReported = false;
             }
-            if (deltaPosition < MINIMUM_POSITION_DELTA) {
-                timestamp = mPreviousTimestamp;  // Use last valid timestamp.
+            if (deltaPosition < 0) {
+                timestamp.mPosition = mPreviousTimestamp.mPosition;
+                deltaPosition = 0;
             }
+#if 0
+            // Uncomment this to verify audio timestamp rate.
+            const int64_t deltaTime =
+                    convertTimespecToNs(timestamp.mTime) - previousTimeNanos;
+            if (deltaTime != 0) {
+                const int64_t computedSampleRate =
+                        deltaPosition * (long long)NANOS_PER_SECOND / deltaTime;
+                ALOGD("computedSampleRate:%u  sampleRate:%u",
+                        (unsigned)computedSampleRate, mSampleRate);
+            }
+#endif
         }
         mPreviousTimestamp = timestamp;
         mPreviousTimestampValid = true;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index d45dbcd..f619e1d 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1521,57 +1521,45 @@
     }
 
     uint32_t numFramesPlayed;
-    int64_t numFramesPlayedAt;
+    int64_t numFramesPlayedAtUs;
     AudioTimestamp ts;
-    static const int64_t kStaleTimestamp100ms = 100000;
 
     status_t res = mTrack->getTimestamp(ts);
     if (res == OK) {                 // case 1: mixing audio tracks and offloaded tracks.
         numFramesPlayed = ts.mPosition;
-        numFramesPlayedAt = ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
-        const int64_t timestampAge = nowUs - numFramesPlayedAt;
-        if (timestampAge > kStaleTimestamp100ms) {
-            // This is an audio FIXME.
-            // getTimestamp returns a timestamp which may come from audio mixing threads.
-            // After pausing, the MixerThread may go idle, thus the mTime estimate may
-            // become stale. Assuming that the MixerThread runs 20ms, with FastMixer at 5ms,
-            // the max latency should be about 25ms with an average around 12ms (to be verified).
-            // For safety we use 100ms.
-            ALOGV("getTimestamp: returned stale timestamp nowUs(%lld) numFramesPlayedAt(%lld)",
-                    (long long)nowUs, (long long)numFramesPlayedAt);
-            numFramesPlayedAt = nowUs - kStaleTimestamp100ms;
-        }
-        //ALOGD("getTimestamp: OK %d %lld", numFramesPlayed, (long long)numFramesPlayedAt);
+        numFramesPlayedAtUs = ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
+        //ALOGD("getTimestamp: OK %d %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
     } else if (res == WOULD_BLOCK) { // case 2: transitory state on start of a new track
         numFramesPlayed = 0;
-        numFramesPlayedAt = nowUs;
+        numFramesPlayedAtUs = nowUs;
         //ALOGD("getTimestamp: WOULD_BLOCK %d %lld",
-        //        numFramesPlayed, (long long)numFramesPlayedAt);
+        //        numFramesPlayed, (long long)numFramesPlayedAtUs);
     } else {                         // case 3: transitory at new track or audio fast tracks.
         res = mTrack->getPosition(&numFramesPlayed);
         CHECK_EQ(res, (status_t)OK);
-        numFramesPlayedAt = nowUs;
-        numFramesPlayedAt += 1000LL * mTrack->latency() / 2; /* XXX */
-        //ALOGD("getPosition: %u %lld", numFramesPlayed, (long long)numFramesPlayedAt);
+        numFramesPlayedAtUs = nowUs;
+        numFramesPlayedAtUs += 1000LL * mTrack->latency() / 2; /* XXX */
+        //ALOGD("getPosition: %u %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
     }
 
     // CHECK_EQ(numFramesPlayed & (1 << 31), 0);  // can't be negative until 12.4 hrs, test
     // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours.
     int64_t durationUs = (int64_t)((int32_t)numFramesPlayed * 1000000LL / mSampleRateHz)
-            + nowUs - numFramesPlayedAt;
+            + nowUs - numFramesPlayedAtUs;
     if (durationUs < 0) {
         // Occurs when numFramesPlayed position is very small and the following:
         // (1) In case 1, the time nowUs is computed before getTimestamp() is called and
-        //     numFramesPlayedAt is greater than nowUs by time more than numFramesPlayed.
+        //     numFramesPlayedAtUs is greater than nowUs by time more than numFramesPlayed.
         // (2) In case 3, using getPosition and adding mAudioSink->latency() to
-        //     numFramesPlayedAt, by a time amount greater than numFramesPlayed.
+        //     numFramesPlayedAtUs, by a time amount greater than numFramesPlayed.
         //
         // Both of these are transitory conditions.
         ALOGV("getPlayedOutDurationUs: negative duration %lld set to zero", (long long)durationUs);
         durationUs = 0;
     }
     ALOGV("getPlayedOutDurationUs(%lld) nowUs(%lld) frames(%u) framesAt(%lld)",
-            (long long)durationUs, (long long)nowUs, numFramesPlayed, (long long)numFramesPlayedAt);
+            (long long)durationUs, (long long)nowUs,
+            numFramesPlayed, (long long)numFramesPlayedAtUs);
     return durationUs;
 }
 
diff --git a/media/libstagefright/MediaSync.cpp b/media/libstagefright/MediaSync.cpp
index 6f2d868..0cf6fbf 100644
--- a/media/libstagefright/MediaSync.cpp
+++ b/media/libstagefright/MediaSync.cpp
@@ -478,59 +478,43 @@
     CHECK(mAudioTrack != NULL);
 
     uint32_t numFramesPlayed;
-    int64_t numFramesPlayedAt;
+    int64_t numFramesPlayedAtUs;
     AudioTimestamp ts;
-    static const int64_t kStaleTimestamp100ms = 100000;
 
     status_t res = mAudioTrack->getTimestamp(ts);
     if (res == OK) {
         // case 1: mixing audio tracks.
         numFramesPlayed = ts.mPosition;
-        numFramesPlayedAt =
-            ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
-        const int64_t timestampAge = nowUs - numFramesPlayedAt;
-        if (timestampAge > kStaleTimestamp100ms) {
-            // This is an audio FIXME.
-            // getTimestamp returns a timestamp which may come from audio
-            // mixing threads. After pausing, the MixerThread may go idle,
-            // thus the mTime estimate may become stale. Assuming that the
-            // MixerThread runs 20ms, with FastMixer at 5ms, the max latency
-            // should be about 25ms with an average around 12ms (to be
-            // verified). For safety we use 100ms.
-            ALOGV("getTimestamp: returned stale timestamp nowUs(%lld) "
-                  "numFramesPlayedAt(%lld)",
-                  (long long)nowUs, (long long)numFramesPlayedAt);
-            numFramesPlayedAt = nowUs - kStaleTimestamp100ms;
-        }
+        numFramesPlayedAtUs = ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
         //ALOGD("getTimestamp: OK %d %lld",
-        //      numFramesPlayed, (long long)numFramesPlayedAt);
+        //      numFramesPlayed, (long long)numFramesPlayedAtUs);
     } else if (res == WOULD_BLOCK) {
         // case 2: transitory state on start of a new track
         numFramesPlayed = 0;
-        numFramesPlayedAt = nowUs;
+        numFramesPlayedAtUs = nowUs;
         //ALOGD("getTimestamp: WOULD_BLOCK %d %lld",
-        //      numFramesPlayed, (long long)numFramesPlayedAt);
+        //      numFramesPlayed, (long long)numFramesPlayedAtUs);
     } else {
         // case 3: transitory at new track or audio fast tracks.
         res = mAudioTrack->getPosition(&numFramesPlayed);
         CHECK_EQ(res, (status_t)OK);
-        numFramesPlayedAt = nowUs;
-        numFramesPlayedAt += 1000LL * mAudioTrack->latency() / 2; /* XXX */
-        //ALOGD("getPosition: %d %lld", numFramesPlayed, (long long)numFramesPlayedAt);
+        numFramesPlayedAtUs = nowUs;
+        numFramesPlayedAtUs += 1000LL * mAudioTrack->latency() / 2; /* XXX */
+        //ALOGD("getPosition: %d %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
     }
 
     //can't be negative until 12.4 hrs, test.
     //CHECK_EQ(numFramesPlayed & (1 << 31), 0);
     int64_t durationUs =
         getDurationIfPlayedAtNativeSampleRate_l(numFramesPlayed)
-            + nowUs - numFramesPlayedAt;
+            + nowUs - numFramesPlayedAtUs;
     if (durationUs < 0) {
         // Occurs when numFramesPlayed position is very small and the following:
         // (1) In case 1, the time nowUs is computed before getTimestamp() is
-        //     called and numFramesPlayedAt is greater than nowUs by time more
+        //     called and numFramesPlayedAtUs is greater than nowUs by time more
         //     than numFramesPlayed.
         // (2) In case 3, using getPosition and adding mAudioTrack->latency()
-        //     to numFramesPlayedAt, by a time amount greater than
+        //     to numFramesPlayedAtUs, by a time amount greater than
         //     numFramesPlayed.
         //
         // Both of these are transitory conditions.
@@ -541,7 +525,7 @@
     ALOGV("getPlayedOutAudioDurationMedia_l(%lld) nowUs(%lld) frames(%u) "
           "framesAt(%lld)",
           (long long)durationUs, (long long)nowUs, numFramesPlayed,
-          (long long)numFramesPlayedAt);
+          (long long)numFramesPlayedAtUs);
     return durationUs;
 }