Merge "GTS/youtube performance tests check claimed performance" into qt-dev am: 258e7e2b69 am: fe69054f3d
am: ac7d0befc6

Change-Id: I419ea17d6670cd66e63569d05231f9718a6128df
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaPerfUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaPerfUtils.java
index 469e99a..7e02b85 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaPerfUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaPerfUtils.java
@@ -173,4 +173,41 @@
         return "Expected " + kind + ": " + reported + ".\n"
                 + "Measured frame rate: " + Arrays.toString(measuredFps) + ".\n";
     }
+
+    /** Verifies |requestedFps| does not exceed reported achievable rates.
+     *  Returns null if *ALL* requested rates are claimed to be achievable.
+     *  Otherwise, returns a diagnostic explaining why it's not achievable.
+     *  (one of the rates was too fast, we don't have achievability information, etc).
+     *
+     *  we're looking for 90% confidence, which is documented as being:
+     *  "higher than half of the lower limit at least 90% of the time in tested configurations"
+     *
+     *  NB: we only invoke this for the SW codecs; we use performance point info for the
+     *  hardware codecs.
+     *  */
+    public static String areAchievableFrameRates(
+            String name, String mime, int w, int h, double... requestedFps) {
+        Range<Double> reported =
+            MediaUtils.getVideoCapabilities(name, mime).getAchievableFrameRatesFor(w, h);
+        String kind = "achievable frame rates for " + name + " " + mime + " " + w + "x" + h;
+        if (reported == null) {
+            return "Failed to get " + kind;
+        }
+
+        double confidence90 = reported.getLower() / 2.0;
+
+        Log.d(TAG, name + " " + mime + " " + w + "x" + h +
+                " lower " + reported.getLower() + " 90% confidence " + confidence90 +
+                " requested " + Arrays.toString(requestedFps));
+
+        // if *any* of them are too fast, we say no.
+        for (double requested : requestedFps) {
+            if (requested > confidence90) {
+                return "Expected " + kind + ": " + reported + ", 90% confidence: " + confidence90
+                       + ".\n"
+                       + "Requested frame rate: " + Arrays.toString(requestedFps) + ".\n";
+            }
+        }
+        return null;
+    }
 }
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
index c01efd2..53da03a 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
@@ -300,11 +300,79 @@
     }
 
     public static boolean canDecode(MediaFormat format) {
-        if (sMCL.findDecoderForFormat(format) == null) {
+        return canDecode(format, 0.0);
+    }
+
+    // this is "do we claim to decode"; caller is on the hook to determine
+    // if we actually meet that claim, specifically around speed.
+    public static boolean canDecode(MediaFormat format, double rate ) {
+        String decoder = sMCL.findDecoderForFormat(format);
+
+        if (decoder == null) {
             Log.i(TAG, "no decoder for " + format);
             return false;
         }
-        return true;
+
+	if (rate == 0.0) {
+            return true;
+	}
+
+	// we care about speed of decoding
+        Log.d(TAG, "checking for decoding " + format + " at " +
+                   rate + " fps with " + decoder);
+
+        String mime = format.getString(MediaFormat.KEY_MIME);
+        int width = format.getInteger(MediaFormat.KEY_WIDTH);
+        int height = format.getInteger(MediaFormat.KEY_HEIGHT);
+
+        MediaCodecInfo[] mciList = sMCL.getCodecInfos();
+
+        if (mciList == null) {
+            Log.d(TAG, "did not get list of MediaCodecInfo");
+            return false;
+        }
+
+        MediaCodecInfo mci = null;
+        for (MediaCodecInfo mci2 : mciList) {
+            if (mci2.getName().equals(decoder)) {
+                mci = mci2;
+                break;
+            }
+        }
+        if (mci == null) {
+            return false;
+        }
+        if (!mci.getName().equals(decoder)) {
+            Log.e(TAG, "did not find expected " + decoder);
+            return false;
+        }
+
+        if (mci.isSoftwareOnly()) {
+            String verified = MediaPerfUtils.areAchievableFrameRates(
+                              decoder, mime, width, height, rate);
+            if (verified == null) {
+                Log.d(TAG, "claims to decode content at " + rate + " fps");
+                return true;
+            }
+            Log.d(TAG, "achieveable framerates says: " + verified);
+            return false;
+        } else {
+            MediaCodecInfo.VideoCapabilities caps =
+                            mci.getCapabilitiesForType(mime).getVideoCapabilities();
+            List<MediaCodecInfo.VideoCapabilities.PerformancePoint> pp =
+                            caps.getSupportedPerformancePoints();
+            VideoCapabilities.PerformancePoint target =
+                            new VideoCapabilities.PerformancePoint(width, height, (int) rate);
+            for (MediaCodecInfo.VideoCapabilities.PerformancePoint point : pp) {
+                if (point.covers(target)) {
+                    Log.i(TAG, "target " + target.toString() +
+                               " covered by point " + point.toString());
+                    return true;
+                }
+            }
+            Log.i(TAG, "NOT covered by any hardware performance point");
+            return false;
+        }
     }
 
     public static boolean supports(String codecName, String mime, int w, int h) {
@@ -555,15 +623,27 @@
         return check(hasCodecForMimes(true /* encoder */, mimes), "no encoder found");
     }
 
+    // checks format, does not address actual speed of decoding
     public static boolean canDecodeVideo(String mime, int width, int height, float rate) {
+	return canDecodeVideo(mime, width, height, rate, (float)0.0);
+    }
+
+    // format + decode rate
+    public static boolean canDecodeVideo(String mime, int width, int height, float rate, float decodeRate) {
         MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
         format.setFloat(MediaFormat.KEY_FRAME_RATE, rate);
-        return canDecode(format);
+        return canDecode(format, decodeRate);
     }
 
     public static boolean canDecodeVideo(
             String mime, int width, int height, float rate,
             Integer profile, Integer level, Integer bitrate) {
+        return canDecodeVideo(mime, width, height, rate, profile, level, bitrate, (float)0.0);
+    }
+
+    public static boolean canDecodeVideo(
+            String mime, int width, int height, float rate,
+            Integer profile, Integer level, Integer bitrate, float decodeRate) {
         MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
         format.setFloat(MediaFormat.KEY_FRAME_RATE, rate);
         if (profile != null) {
@@ -575,7 +655,7 @@
         if (bitrate != null) {
             format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
         }
-        return canDecode(format);
+        return canDecode(format, decodeRate);
     }
 
     public static boolean checkEncoderForFormat(MediaFormat format) {