Merge "Fix frame duration computations after discontinuities" into udc-dev
diff --git a/media/libstagefright/VideoRenderQualityTracker.cpp b/media/libstagefright/VideoRenderQualityTracker.cpp
index c9f461e..4f12a37 100644
--- a/media/libstagefright/VideoRenderQualityTracker.cpp
+++ b/media/libstagefright/VideoRenderQualityTracker.cpp
@@ -326,11 +326,8 @@
     if (mConfiguration.freezeDurationMsHistogramToScore.size() ==
         mMetrics.freezeDurationMsHistogram.size()) {
         for (int i = 0; i < mMetrics.freezeDurationMsHistogram.size(); ++i) {
-            int32_t count = 0;
-            for (int j = i; j < mMetrics.freezeDurationMsHistogram.size(); ++j) {
-                count += mMetrics.freezeDurationMsHistogram[j];
-            }
-            mMetrics.freezeScore += count / mConfiguration.freezeDurationMsHistogramToScore[i];
+            mMetrics.freezeScore += mMetrics.freezeDurationMsHistogram[i] *
+                    mConfiguration.freezeDurationMsHistogramToScore[i];
         }
     }
     mMetrics.freezeRate = float(double(mMetrics.freezeDurationMsHistogram.getSum()) /
@@ -339,11 +336,8 @@
     mMetrics.judderScore = 0;
     if (mConfiguration.judderScoreHistogramToScore.size() == mMetrics.judderScoreHistogram.size()) {
         for (int i = 0; i < mMetrics.judderScoreHistogram.size(); ++i) {
-            int32_t count = 0;
-            for (int j = i; j < mMetrics.judderScoreHistogram.size(); ++j) {
-                count += mMetrics.judderScoreHistogram[j];
-            }
-            mMetrics.judderScore += count / mConfiguration.judderScoreHistogramToScore[i];
+            mMetrics.judderScore += mMetrics.judderScoreHistogram[i] *
+                    mConfiguration.judderScoreHistogramToScore[i];
         }
     }
     mMetrics.judderRate = float(double(mMetrics.judderScoreHistogram.getCount()) /
diff --git a/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h b/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
index 2b3dbcb..82ba81c 100644
--- a/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
+++ b/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
@@ -62,7 +62,13 @@
 
     // A histogram of the durations of freezes due to dropped/skipped frames.
     MediaHistogram<int32_t> freezeDurationMsHistogram;
-    // The computed overall freeze score using the above histogram and score conversion table.
+    // The computed overall freeze score using the above histogram and score conversion table. The
+    // score is based on counts in the histogram bucket, multiplied by the value in the score
+    // conversion table for that bucket. For example, the impact of a short freeze may be minimal,
+    // but the impact of long freeze may be disproportionally worse. Therefore, the score
+    // multipliers for each bucket might increase exponentially instead of linearly. A score
+    // multiplier of zero would reflect that small freeze durations have near-zero impact to the
+    // user experience.
     int32_t freezeScore;
     // The computed percentage of total playback duration that was frozen.
     float freezeRate;
@@ -72,9 +78,16 @@
     // A histogram of the durations between each freeze.
     MediaHistogram<int32_t> freezeDistanceMsHistogram;
 
-    // A histogram of the judder scores.
+    // A histogram of the judder scores - based on the error tolerance between actual render
+    // duration of each frame and the ideal render duration.
     MediaHistogram<int32_t> judderScoreHistogram;
-    // The computed overall judder score using the above histogram and score conversion table.
+    // The computed overall judder score using the above histogram and score conversion table. The
+    // score is based on counts in the histogram bucket, multiplied by the value in the score
+    // conversion table for that bucket. For example, the impact of minimal judder may be small,
+    // but the impact of large judder may be disproportionally worse. Therefore, the score
+    // multipliers for each bucket might increase exponentially instead of linearly. A score
+    // multiplier of zero would reflect that small judder errors have near-zero impact to the user
+    // experience.
     int32_t judderScore;
     // The computed percentage of total frames that had judder.
     float judderRate;
@@ -143,15 +156,21 @@
         //
         // The values used to distribute freeze durations across a histogram.
         std::vector<int32_t> freezeDurationMsHistogramBuckets;
-        // The values used to compare against freeze duration counts when determining an overall
-        // score.
+        //
+        // The values used to multiply the counts in the histogram buckets above to compute an
+        // overall score. This allows the score to reflect disproportionate impact as freeze
+        // durations increase.
         std::vector<int64_t> freezeDurationMsHistogramToScore;
+        //
         // The values used to distribute distances between freezes across a histogram.
         std::vector<int32_t> freezeDistanceMsHistogramBuckets;
+        //
         // The maximum number of freeze events to send back to the caller.
         int32_t freezeEventMax;
+        //
         // The maximum number of detail entries tracked per freeze event.
         int32_t freezeEventDetailsMax;
+        //
         // The maximum distance in time between two freeze occurrences such that both will be
         // lumped into the same freeze event.
         int32_t freezeEventDistanceToleranceMs;
@@ -160,15 +179,21 @@
         //
         // A judder error lower than this value is not scored as judder.
         int32_t judderErrorToleranceUs;
+        //
         // The values used to distribute judder scores across a histogram.
         std::vector<int32_t> judderScoreHistogramBuckets;
-        // The values used to compare against judder score histogram counts when determining an
-        // overall score.
+        //
+        // The values used to multiply the counts in the histogram buckets above to compute an
+        // overall score. This allows the score to reflect disproportionate impact as judder scores
+        // increase.
         std::vector<int64_t> judderScoreHistogramToScore;
+        //
         // The maximum number of judder events to send back to the caller.
         int32_t judderEventMax;
+        //
         // The maximum number of detail entries tracked per judder event.
         int32_t judderEventDetailsMax;
+        //
         // The maximum distance in time between two judder occurrences such that both will be
         // lumped into the same judder event.
         int32_t judderEventDistanceToleranceMs;
diff --git a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
index 1d4c581..7823922 100644
--- a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
+++ b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
@@ -986,4 +986,42 @@
     EXPECT_EQ(h.getAndClearJudderEvent().valid, false); // max number of judder events exceeded
 }
 
+TEST_F(VideoRenderQualityTrackerTest, capturesOverallFreezeScore) {
+    Configuration c;
+    // # drops * 20ms + 20ms because current frame is frozen + 1 for bucket threshold
+    c.freezeDurationMsHistogramBuckets = {1 * 20 + 21, 5 * 20 + 21, 10 * 20 + 21};
+    c.freezeDurationMsHistogramToScore = {10, 100, 1000};
+    Helper h(20, c);
+    h.render(5);
+    h.drop(2); // bucket = 0, bucket count = 1, bucket score = 10
+    h.render(5);
+    h.drop(11); // bucket = 2, bucket count = 1, bucket score = 1000
+    h.render(5);
+    h.drop(6); // bucket = 1, bucket count = 1, bucket score = 100
+    h.render(5);
+    h.drop(1); // bucket = null
+    h.render(5);
+    h.drop(3); // bucket = 0, bucket count = 2, bucket score = 20
+    h.render(5);
+    h.drop(10); // bucket = 1, bucket count = 2, bucket score = 200
+    h.render(5);
+    h.drop(7); // bucket = 1, bucket count = 3, bucket score = 300
+    h.render(5);
+    EXPECT_EQ(h.getMetrics().freezeScore, 20 + 300 + 1000);
+}
+
+TEST_F(VideoRenderQualityTrackerTest, capturesOverallJudderScore) {
+    Configuration c;
+    c.judderScoreHistogramBuckets = {0, 6, 10};
+    c.judderScoreHistogramToScore = {10, 100, 1000};
+    Helper h(20, c);
+    h.render({20, 20, 15, 20, 20}); // bucket = 0, bucket count = 1, bucket score = 10
+    h.render({20, 20, 11, 20, 20}); // bucket = 1, bucket count = 1, bucket score = 100
+    h.render({20, 20, 13, 20, 20}); // bucket = 1, bucket count = 2, bucket score = 200
+    h.render({20, 20,  5, 20, 20}); // bucket = 2, bucket count = 1, bucket score = 1000
+    h.render({20, 20, 14, 20, 20}); // bucket = 1, bucket count = 3, bucket score = 300
+    h.render({20, 20, 10, 20, 20}); // bucket = 2, bucket count = 2, bucket score = 2000
+    EXPECT_EQ(h.getMetrics().judderScore, 10 + 300 + 2000);
+}
+
 } // android