Add CTS media tests for FEATURE_VR_MODE_HIGH_PERFORMANCE.
Tests perform sanity checks on H264, HEVC, and VP9 codec capabilities.
If a codec claims to be VR ready, performs a decode test.
Bug: 27948493
Change-Id: Ic75475ff9e773da585bcb6275a2d1095c40bf7fa
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index 79bf8c7..24880e2 100755
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -2497,5 +2497,182 @@
mMediaCodecPlayer.flush();
// mMediaCodecPlayer.reset() handled in TearDown();
}
+
+ /**
+ * Returns list of CodecCapabilities advertising support for the given MIME type.
+ */
+ private static List<CodecCapabilities> getCodecCapabilitiesForMimeType(String mimeType) {
+ int numCodecs = MediaCodecList.getCodecCount();
+ List<CodecCapabilities> caps = new ArrayList<CodecCapabilities>();
+ for (int i = 0; i < numCodecs; i++) {
+ MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+ if (codecInfo.isEncoder()) {
+ continue;
+ }
+
+ String[] types = codecInfo.getSupportedTypes();
+ for (int j = 0; j < types.length; j++) {
+ if (types[j].equalsIgnoreCase(mimeType)) {
+ caps.add(codecInfo.getCapabilitiesForType(mimeType));
+ }
+ }
+ }
+ return caps;
+ }
+
+ /**
+ * Returns true if there exists a codec supporting the given MIME type that meets VR high
+ * performance requirements.
+ *
+ * The requirements are as follows:
+ * - At least 972000 blocks per second (where blocks are defined as 16x16 -- note this
+ * is equivalent to 3840x2160@30fps)
+ * - At least 4 concurrent instances
+ * - Feature adaptive-playback present
+ */
+ private static boolean doesMimeTypeHaveVrReadyCodec(String mimeType) {
+ List<CodecCapabilities> caps = getCodecCapabilitiesForMimeType(mimeType);
+ for (CodecCapabilities c : caps) {
+ if (c.getMaxSupportedInstances() < 4) {
+ continue;
+ }
+
+ if (!c.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) {
+ continue;
+ }
+
+ if (!c.getVideoCapabilities().areSizeAndRateSupported(3840, 2160, 30.0)) {
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private class DecodeRunnable implements Runnable {
+ private int video;
+ private int frames;
+ private long durationMillis;
+
+ public DecodeRunnable(int video) {
+ this.video = video;
+ this.frames = 0;
+ this.durationMillis = 0;
+ }
+
+ @Override
+ public void run() {
+ long start = System.currentTimeMillis();
+ int actual = 0;
+ try {
+ actual = countFrames(this.video, RESET_MODE_NONE, -1, null);
+ } catch (Exception e) {
+ actual = -1;
+ }
+ long durationMillis = System.currentTimeMillis() - start;
+
+ synchronized (this) {
+ this.frames = actual;
+ this.durationMillis = durationMillis;
+ }
+ }
+
+ public synchronized int getFrames() {
+ return this.frames;
+ }
+
+ public synchronized double getMeasuredFps() {
+ return this.frames / (this.durationMillis / 1000.0);
+ }
+ }
+
+ private void decodeInParallel(int video, int frames, int fps, int parallel) throws Exception {
+ DecodeRunnable[] runnables = new DecodeRunnable[parallel];
+ Thread[] threads = new Thread[parallel];
+
+ for (int i = 0; i < parallel; ++i) {
+ runnables[i] = new DecodeRunnable(video);
+ threads[i] = new Thread(runnables[i]);
+ threads[i].start();
+ }
+
+ for (Thread t : threads) {
+ t.join();
+ }
+
+ for (DecodeRunnable dr : runnables) {
+ assertTrue("Expected to decode " + frames + " frames, found " + dr.getFrames(),
+ frames == dr.getFrames());
+ }
+
+ for (DecodeRunnable dr : runnables) {
+ Log.d(TAG, "Decoded at " + dr.getMeasuredFps());
+ assertTrue("Expected to decode at " + fps + " fps, measured " + dr.getMeasuredFps(),
+ fps < dr.getMeasuredFps());
+ }
+ }
+
+ public void testVrHighPerformanceH264() throws Exception {
+ if (!supportsVrHighPerformance()) {
+ MediaUtils.skipTest(TAG, "FEATURE_VR_MODE_HIGH_PERFORMANCE not present");
+ return;
+ }
+
+ boolean h264IsReady = doesMimeTypeHaveVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_AVC);
+ assertTrue("Did not find a VR ready H.264 decoder", h264IsReady);
+
+ // Test throughput by decoding 1920x1080 @ 30fps x 4 instances.
+ decodeInParallel(
+ R.raw.video_1920x1080_mp4_h264_20480kbps_30fps_aac_stereo_128kbps_44100hz, 299, 30,
+ 4);
+
+ // Test throughput by decoding 1920x1080 @ 60fps x 2 instances.
+ decodeInParallel(
+ R.raw.video_1920x1080_mp4_h264_20480kbps_60fps_aac_stereo_128kbps_44100hz, 596, 60,
+ 2);
+ }
+
+ public void testVrHighPerformanceHEVC() throws Exception {
+ if (!supportsVrHighPerformance()) {
+ MediaUtils.skipTest(TAG, "FEATURE_VR_MODE_HIGH_PERFORMANCE not present");
+ return;
+ }
+
+ boolean hevcIsReady = doesMimeTypeHaveVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_HEVC);
+ if (!hevcIsReady) {
+ MediaUtils.skipTest(TAG, "HEVC isn't required to be VR ready");
+ return;
+ }
+
+ // Test throughput by decoding 1920x1080 @ 30fps x 4 instances.
+ decodeInParallel(
+ R.raw.video_1920x1080_mp4_hevc_10240kbps_30fps_aac_stereo_128kbps_44100hz, 299, 30,
+ 4);
+ }
+
+ public void testVrHighPerformanceVP9() throws Exception {
+ if (!supportsVrHighPerformance()) {
+ MediaUtils.skipTest(TAG, "FEATURE_VR_MODE_HIGH_PERFORMANCE not present");
+ return;
+ }
+
+ boolean vp9IsReady = doesMimeTypeHaveVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_VP9);
+ if (!vp9IsReady) {
+ MediaUtils.skipTest(TAG, "VP9 isn't required to be VR ready");
+ return;
+ }
+
+ // Test throughput by decoding 1920x1080 @ 30fps x 4 instances.
+ decodeInParallel(
+ R.raw.video_1920x1080_webm_vp9_10240kbps_30fps_vorbis_stereo_128kbps_48000hz, 249,
+ 30, 4);
+ }
+
+ private boolean supportsVrHighPerformance() {
+ PackageManager pm = mContext.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE);
+ }
}