media: add VideoDecoderPerfTest
Bug: 20507129
Change-Id: Ic450f37ee82c01ee31ee9d87479865d8c4467199
diff --git a/libs/deviceutil/src/android/cts/util/MediaUtils.java b/libs/deviceutil/src/android/cts/util/MediaUtils.java
index 7edde48..4ec84ce 100644
--- a/libs/deviceutil/src/android/cts/util/MediaUtils.java
+++ b/libs/deviceutil/src/android/cts/util/MediaUtils.java
@@ -17,11 +17,15 @@
import android.content.Context;
import android.content.res.AssetFileDescriptor;
+import android.media.MediaCodec;
import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.VideoCapabilities;
import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.net.Uri;
+import android.util.Range;
import java.lang.reflect.Method;
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
@@ -147,6 +151,40 @@
return true;
}
+ public static boolean supports(String codecName, String mime, int w, int h) {
+ MediaCodec codec;
+ try {
+ codec = MediaCodec.createByCodecName(codecName);
+ } catch (IOException e) {
+ return false;
+ }
+
+ CodecCapabilities cap = null;
+ try {
+ cap = codec.getCodecInfo().getCapabilitiesForType(mime);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "not supported mime: " + mime);
+ codec.release();
+ return false;
+ }
+
+ VideoCapabilities vidCap = cap.getVideoCapabilities();
+ if (vidCap == null) {
+ Log.w(TAG, "not a video codec: " + codecName);
+ codec.release();
+ return false;
+ }
+ try {
+ Range<Double> fps = vidCap.getSupportedFrameRatesFor(w, h);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "unsupported size " + w + "x" + h);
+ codec.release();
+ return false;
+ }
+ codec.release();
+ return true;
+ }
+
public static boolean hasCodecForTrack(MediaExtractor ex, int track) {
int count = ex.getTrackCount();
if (track < 0 || track >= count) {
diff --git a/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java b/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
new file mode 100644
index 0000000..54024f7
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import com.android.cts.media.R;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.cts.util.DeviceReportLog;
+import android.cts.util.MediaUtils;
+import android.media.MediaCodec;
+import android.media.MediaCodecList;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.util.Log;
+import android.view.Surface;
+
+import com.android.cts.util.ReportLog;
+import com.android.cts.util.ResultType;
+import com.android.cts.util.ResultUnit;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.ArrayList;
+
+public class VideoDecoderPerfTest extends MediaPlayerTestBase {
+ private static final String TAG = "VideoDecoderPerfTest";
+ private static final int TOTAL_FRAMES = 3000;
+ private static final int NUMBER_OF_REPEAT = 2;
+ private static final String VIDEO_AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
+ private static final String VIDEO_VP8 = MediaFormat.MIMETYPE_VIDEO_VP8;
+ private static final String VIDEO_VP9 = MediaFormat.MIMETYPE_VIDEO_VP9;
+ private static final String VIDEO_HEVC = MediaFormat.MIMETYPE_VIDEO_HEVC;
+ private static final String VIDEO_H263 = MediaFormat.MIMETYPE_VIDEO_H263;
+ private static final String VIDEO_MPEG4 = MediaFormat.MIMETYPE_VIDEO_MPEG4;
+
+ private Resources mResources;
+ private DeviceReportLog mReportLog = new DeviceReportLog();
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mResources = mContext.getResources();
+ }
+
+ private static String[] getDecoderName(String mime) {
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ ArrayList<String> result = new ArrayList<String>();
+ for (MediaCodecInfo info : mcl.getCodecInfos()) {
+ if (info.isEncoder()) {
+ continue;
+ }
+ CodecCapabilities caps = null;
+ try {
+ caps = info.getCapabilitiesForType(mime);
+ } catch (IllegalArgumentException e) { // mime is not supported
+ continue;
+ }
+ result.add(info.getName());
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
+ private void decode(String mime, int video, int width, int height) throws Exception {
+ String[] names = getDecoderName(mime);
+ for (String name: names) {
+ if (!MediaUtils.supports(name, mime, width, height)) {
+ Log.i(TAG, "Codec " + name + " with " + width + "," + height + " not supported");
+ continue;
+ }
+
+ Log.d(TAG, "testing " + name);
+ for (int i = 0; i < NUMBER_OF_REPEAT; ++i) {
+ // Decode to Surface.
+ Log.d(TAG, "round #" + i + " decode to surface");
+ Surface s = getActivity().getSurfaceHolder().getSurface();
+ doDecode(name, video, TOTAL_FRAMES, s);
+
+ // Decode to buffer.
+ Log.d(TAG, "round #" + i + " decode to buffer");
+ doDecode(name, video, TOTAL_FRAMES, null);
+ }
+ }
+ }
+
+ private void doDecode(String name, int video, int stopAtSample, Surface surface)
+ throws Exception {
+ AssetFileDescriptor testFd = mResources.openRawResourceFd(video);
+ MediaExtractor extractor = new MediaExtractor();
+ extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+ testFd.getLength());
+ extractor.selectTrack(0);
+
+ int trackIndex = extractor.getSampleTrackIndex();
+ MediaFormat format = extractor.getTrackFormat(trackIndex);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ ByteBuffer[] codecInputBuffers;
+ ByteBuffer[] codecOutputBuffers;
+
+ MediaCodec codec = MediaCodec.createByCodecName(name);
+ codec.configure(format, surface, null /* crypto */, 0 /* flags */);
+ codec.start();
+ codecInputBuffers = codec.getInputBuffers();
+ codecOutputBuffers = codec.getOutputBuffers();
+
+ // start decode loop
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+
+ final long kTimeOutUs = 5000; // 5ms timeout
+ long[] frameTimeDiff = new long[TOTAL_FRAMES - 1];
+ long lastOutputTimeNs = 0;
+ boolean sawInputEOS = false;
+ boolean sawOutputEOS = false;
+ int inputNum = 0;
+ int outputNum = 0;
+ int width = 0;
+ int height = 0;
+ long start = System.currentTimeMillis();
+ while (!sawOutputEOS) {
+ // handle input
+ if (!sawInputEOS) {
+ int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
+
+ if (inputBufIndex >= 0) {
+ ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
+
+ int sampleSize =
+ extractor.readSampleData(dstBuf, 0 /* offset */);
+ if (sampleSize < 0) {
+ // repeat from beginning.
+ extractor.release();
+ extractor = new MediaExtractor();
+ extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+ testFd.getLength());
+ extractor.selectTrack(0);
+ sampleSize =
+ extractor.readSampleData(dstBuf, 0 /* offset */);
+ }
+ assert sampleSize >= 0:
+ "extractor.readSampleData returns negative.";
+ long presentationTimeUs = extractor.getSampleTime();
+ if (++inputNum == stopAtSample) {
+ Log.d(TAG, "saw input EOS (stop at sample).");
+ sawInputEOS = true; // tag this sample as EOS
+ }
+ codec.queueInputBuffer(
+ inputBufIndex,
+ 0 /* offset */,
+ sampleSize,
+ presentationTimeUs,
+ sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+ } else {
+ assertEquals(
+ "codec.dequeueInputBuffer() unrecognized return value: " + inputBufIndex,
+ MediaCodec.INFO_TRY_AGAIN_LATER, inputBufIndex);
+ }
+ }
+
+ // handle output
+ int outputBufIndex = codec.dequeueOutputBuffer(info, kTimeOutUs);
+
+ if (outputBufIndex >= 0) {
+ if (info.size > 0) { // Disregard 0-sized buffers at the end.
+ if (lastOutputTimeNs > 0) {
+ frameTimeDiff[outputNum - 1] = System.nanoTime() - lastOutputTimeNs;
+ }
+ lastOutputTimeNs = System.nanoTime();
+ outputNum++;
+ }
+ codec.releaseOutputBuffer(outputBufIndex, false /* render */);
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ Log.d(TAG, "saw output EOS.");
+ sawOutputEOS = true;
+ }
+ } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ codecOutputBuffers = codec.getOutputBuffers();
+ Log.d(TAG, "output buffers have changed.");
+ } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ MediaFormat oformat = codec.getOutputFormat();
+ width = oformat.getInteger(MediaFormat.KEY_WIDTH);
+ height = oformat.getInteger(MediaFormat.KEY_HEIGHT);
+ Log.d(TAG, "output resolution " + width + "x" + height);
+ } else {
+ assertEquals(
+ "codec.dequeueOutputBuffer() unrecognized return index: "
+ + outputBufIndex,
+ MediaCodec.INFO_TRY_AGAIN_LATER, outputBufIndex);
+ }
+ }
+ long finish = System.currentTimeMillis();
+
+ codec.stop();
+ codec.release();
+
+ extractor.release();
+ testFd.close();
+
+ Log.d(TAG, "input num " + inputNum + " vs output num " + outputNum);
+
+ String testConfig = "codec=" + name +
+ " decodeto=" + ((surface == null) ? "buffer" : "surface") +
+ " size=" + width + "x" + height;
+
+ String message = "average fps for " + testConfig;
+ double fps = (double)outputNum / ((finish - start) / 1000.0);
+ mReportLog.printValue(message, fps, ResultType.HIGHER_BETTER, ResultUnit.FPS);
+
+ message = "frame time diff for " + testConfig + ": " + Arrays.toString(frameTimeDiff);
+ mReportLog.printValue(message, 0, ResultType.NEUTRAL, ResultUnit.NONE);
+ }
+
+ public void testH264320x240() throws Exception {
+ decode(VIDEO_AVC,
+ R.raw.video_320x240_mp4_h264_800kbps_30fps_aac_stereo_128kbps_44100hz,
+ 320, 240);
+ }
+
+ public void testH264720x480() throws Exception {
+ decode(VIDEO_AVC,
+ R.raw.video_720x480_mp4_h264_2048kbps_30fps_aac_stereo_128kbps_44100hz,
+ 720, 480);
+ }
+
+ public void testH2641280x720() throws Exception {
+ decode(VIDEO_AVC,
+ R.raw.video_1280x720_mp4_h264_8192kbps_30fps_aac_stereo_128kbps_44100hz,
+ 1280, 720);
+ }
+
+ public void testH2641920x1080() throws Exception {
+ decode(VIDEO_AVC,
+ R.raw.video_1920x1080_mp4_h264_20480kbps_30fps_aac_stereo_128kbps_44100hz,
+ 1920, 1080);
+ }
+
+ public void testVP8320x240() throws Exception {
+ decode(VIDEO_VP8,
+ R.raw.video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz,
+ 320, 240);
+ }
+
+ public void testVP8640x360() throws Exception {
+ decode(VIDEO_VP8,
+ R.raw.video_640x360_webm_vp8_2048kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ 640, 360);
+ }
+
+ public void testVP81280x720() throws Exception {
+ decode(VIDEO_VP8,
+ R.raw.video_1280x720_webm_vp8_8192kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ 1280, 720);
+ }
+
+ public void testVP81920x1080() throws Exception {
+ decode(VIDEO_VP8,
+ R.raw.video_1920x1080_webm_vp8_20480kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ 1920, 1080);
+ }
+
+ public void testVP9320x240() throws Exception {
+ decode(VIDEO_VP9,
+ R.raw.video_320x240_webm_vp9_600kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ 320, 240);
+ }
+
+ public void testVP9640x360() throws Exception {
+ decode(VIDEO_VP9,
+ R.raw.video_640x360_webm_vp9_1600kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ 640, 360);
+ }
+
+ public void testVP91280x720() throws Exception {
+ decode(VIDEO_VP9,
+ R.raw.video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_44100hz,
+ 1280, 720);
+ }
+
+ public void testVP91920x1080() throws Exception {
+ decode(VIDEO_VP9,
+ R.raw.video_1920x1080_webm_vp9_10240kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ 1920, 1080);
+ }
+
+ public void testVP93840x2160() throws Exception {
+ decode(VIDEO_VP9,
+ R.raw.video_3840x2160_webm_vp9_20480kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ 3840, 2160);
+ }
+
+ public void testHEVC352x288() throws Exception {
+ decode(VIDEO_HEVC,
+ R.raw.video_352x288_mp4_hevc_600kbps_30fps_aac_stereo_128kbps_44100hz,
+ 352, 288);
+ }
+
+ public void testHEVC720x480() throws Exception {
+ decode(VIDEO_HEVC,
+ R.raw.video_720x480_mp4_hevc_1638kbps_30fps_aac_stereo_128kbps_44100hz,
+ 720, 480);
+ }
+
+ public void testHEVC1280x720() throws Exception {
+ decode(VIDEO_HEVC,
+ R.raw.video_1280x720_mp4_hevc_4096kbps_30fps_aac_stereo_128kbps_44100hz,
+ 1280, 720);
+ }
+
+ public void testHEVC1920x1080() throws Exception {
+ decode(VIDEO_HEVC,
+ R.raw.video_1920x1080_mp4_hevc_10240kbps_30fps_aac_stereo_128kbps_44100hz,
+ 1920, 1080);
+ }
+
+ public void testHEVC3840x2160() throws Exception {
+ decode(VIDEO_HEVC,
+ R.raw.video_3840x2160_mp4_hevc_20480kbps_30fps_aac_stereo_128kbps_44100hz,
+ 3840, 2160);
+ }
+
+ public void testH263176x144() throws Exception {
+ decode(VIDEO_HEVC,
+ R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
+ 176, 144);
+ }
+
+ public void testH263352x288() throws Exception {
+ decode(VIDEO_HEVC,
+ R.raw.video_352x288_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
+ 352, 288);
+ }
+
+ public void testMPEG4480x360() throws Exception {
+ decode(VIDEO_MPEG4,
+ R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz,
+ 480, 360);
+ }
+
+ public void testMPEG41280x720() throws Exception {
+ decode(VIDEO_MPEG4,
+ R.raw.video_1280x720_mp4_mpeg4_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+ 1280, 720);
+ }
+}
+