Merge "Fix frame duration computations after discontinuities" into udc-dev am: 03ac6df71a am: 899452a7a1

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/av/+/23476322

Change-Id: I94811229a496bce765e0581f2ff1785bb962c70a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 178458c..54ca1ea 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -6130,7 +6130,7 @@
             ALOGI("rendring output error %d", err);
         }
     } else {
-        if (mIsSurfaceToDisplay) {
+        if (mIsSurfaceToDisplay && buffer->size() != 0) {
             int64_t mediaTimeUs = INT64_MIN;
             if (buffer->meta()->findInt64("timeUs", &mediaTimeUs)) {
                 mVideoRenderQualityTracker.onFrameSkipped(mediaTimeUs);
diff --git a/media/libstagefright/VideoRenderQualityTracker.cpp b/media/libstagefright/VideoRenderQualityTracker.cpp
index c2ba767..4f12a37 100644
--- a/media/libstagefright/VideoRenderQualityTracker.cpp
+++ b/media/libstagefright/VideoRenderQualityTracker.cpp
@@ -228,6 +228,9 @@
     if (mLastRenderTimeUs == -1) {
         return;
     }
+
+    resetIfDiscontinuity(contentTimeUs, -1);
+
     // Frames skipped at the end of playback shouldn't be counted as skipped frames, since the
     // app could be terminating the playback. The pending count will be added to the metrics if and
     // when the next frame is rendered.
@@ -374,6 +377,9 @@
         mDesiredFrameDurationUs[i] = -1;
         mContentFrameDurationUs[i] = -1;
     }
+    mActualFrameDurationUs.priorTimestampUs = -1;
+    mDesiredFrameDurationUs.priorTimestampUs = -1;
+    mContentFrameDurationUs.priorTimestampUs = -1;
 }
 
 bool VideoRenderQualityTracker::resetIfDiscontinuity(int64_t contentTimeUs,
@@ -397,11 +403,14 @@
         // occur if the time the user spends seeking is equal to the duration of the seek. This is
         // very unlikely to occur in practice but CAN occur - the user starts seeking forward, gets
         // distracted, and then returns to seeking forward.
-        int64_t contentFrameDurationUs = contentTimeUs - mLastContentTimeUs;
-        int64_t desiredFrameDurationUs = desiredRenderTimeUs - mLastRenderTimeUs;
-        bool skippedForwardDueToLiveContentFrameDrops =
-                abs(contentFrameDurationUs - desiredFrameDurationUs) <
-                mConfiguration.liveContentFrameDropToleranceUs;
+        bool skippedForwardDueToLiveContentFrameDrops = false;
+        if (desiredRenderTimeUs != -1) {
+            int64_t contentFrameDurationUs = contentTimeUs - mLastContentTimeUs;
+            int64_t desiredFrameDurationUs = desiredRenderTimeUs - mLastRenderTimeUs;
+            skippedForwardDueToLiveContentFrameDrops =
+                    abs(contentFrameDurationUs - desiredFrameDurationUs) <
+                    mConfiguration.liveContentFrameDropToleranceUs;
+        }
         if (!skippedForwardDueToLiveContentFrameDrops) {
             ALOGI("Video playback jumped %d ms forward in content time (%d -> %d) ",
                 int((contentTimeUs - mLastContentTimeUs) / 1000), int(mLastContentTimeUs / 1000),
@@ -475,9 +484,9 @@
     int64_t judderScore = computePreviousJudderScore(mActualFrameDurationUs,
                                                      mContentFrameDurationUs,
                                                      mConfiguration);
-    int64_t judderTimeUs = actualRenderTimeUs - mActualFrameDurationUs[0] -
-                           mActualFrameDurationUs[1];
     if (judderScore != 0) {
+        int64_t judderTimeUs = actualRenderTimeUs - mActualFrameDurationUs[0] -
+                mActualFrameDurationUs[1];
         processJudder(judderScore, judderTimeUs, mLastJudderEndTimeUs, mActualFrameDurationUs,
                       mContentFrameDurationUs, mJudderEvent, mMetrics, mConfiguration);
         mLastJudderEndTimeUs = judderTimeUs + mActualFrameDurationUs[1];
diff --git a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
index db29bb5..7823922 100644
--- a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
+++ b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
@@ -432,6 +432,80 @@
     EXPECT_NEAR(h.getMetrics().actualFrameRate, 60.0, 0.5);
 }
 
+TEST_F(VideoRenderQualityTrackerTest, handlesSeeking) {
+    Configuration c;
+    c.maxExpectedContentFrameDurationUs = 30;
+    VideoRenderQualityTracker v(c);
+    v.onFrameReleased(0, 0);
+    v.onFrameRendered(0, 0);
+    v.onFrameReleased(20, 20);
+    v.onFrameRendered(20, 20);
+    v.onFrameReleased(40, 40);
+    v.onFrameRendered(40, 40);
+    v.onFrameReleased(60, 60);
+    v.onFrameRendered(60, 60);
+    v.onFrameReleased(80, 80);
+    v.onFrameRendered(80, 80);
+    v.onFrameReleased(7200000000, 100);
+    v.onFrameRendered(7200000000, 100);
+    v.onFrameReleased(7200000020, 120);
+    v.onFrameRendered(7200000020, 120);
+    v.onFrameReleased(7200000040, 140);
+    v.onFrameRendered(7200000040, 140);
+    v.onFrameReleased(7200000060, 160);
+    v.onFrameRendered(7200000060, 160);
+    v.onFrameReleased(7200000080, 180);
+    v.onFrameRendered(7200000080, 180);
+    v.onFrameReleased(0, 200);
+    v.onFrameRendered(0, 200);
+    v.onFrameReleased(20, 220);
+    v.onFrameRendered(20, 220);
+    v.onFrameReleased(40, 240);
+    v.onFrameRendered(40, 240);
+    v.onFrameReleased(60, 260);
+    v.onFrameRendered(60, 260);
+    const VideoRenderQualityMetrics &m = v.getMetrics();
+    EXPECT_EQ(m.judderRate, 0); // frame durations can get messed up during discontinuities so if
+                                // the discontinuity is not detected, judder is expected
+    EXPECT_NE(m.contentFrameRate, FRAME_RATE_UNDETERMINED);
+}
+
+TEST_F(VideoRenderQualityTrackerTest, withSkipping_handlesSeeking) {
+    Configuration c;
+    c.maxExpectedContentFrameDurationUs = 30;
+    VideoRenderQualityTracker v(c);
+    v.onFrameReleased(0, 0);
+    v.onFrameRendered(0, 0);
+    v.onFrameReleased(20, 20);
+    v.onFrameRendered(20, 20);
+    v.onFrameReleased(40, 40);
+    v.onFrameRendered(40, 40);
+    v.onFrameReleased(60, 60);
+    v.onFrameRendered(60, 60);
+    v.onFrameReleased(80, 80);
+    v.onFrameRendered(80, 80);
+    v.onFrameSkipped(7200000000);
+    v.onFrameSkipped(7200000020);
+    v.onFrameReleased(7200000040, 100);
+    v.onFrameRendered(7200000040, 100);
+    v.onFrameReleased(7200000060, 120);
+    v.onFrameRendered(7200000060, 120);
+    v.onFrameReleased(7200000080, 140);
+    v.onFrameSkipped(0);
+    v.onFrameRendered(7200000080, 140);
+    v.onFrameSkipped(20);
+    v.onFrameReleased(40, 160);
+    v.onFrameRendered(40, 160);
+    v.onFrameReleased(60, 180);
+    v.onFrameRendered(60, 180);
+    v.onFrameReleased(80, 200);
+    v.onFrameRendered(80, 200);
+    const VideoRenderQualityMetrics &m = v.getMetrics();
+    EXPECT_EQ(m.judderRate, 0); // frame durations can get messed up during discontinuities so if
+                                // the discontinuity is not detected, judder is expected
+    EXPECT_NE(m.contentFrameRate, FRAME_RATE_UNDETERMINED);
+}
+
 TEST_F(VideoRenderQualityTrackerTest, whenLowTolerance_doesntDetectFrameRate) {
     Configuration c;
     c.frameRateDetectionToleranceUs = 0;