videoperf: video codecs performance measurements on common video sizes.
Bug: 20507129
Change-Id: Ia717cae7452a36a390e03014315a480c45ce45d8
diff --git a/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/CodecInfo.java b/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/CodecInfo.java
index 6459c86..9ea8482 100644
--- a/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/CodecInfo.java
+++ b/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/CodecInfo.java
@@ -16,6 +16,7 @@
package com.android.cts.videoperf;
+import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo.CodecProfileLevel;
@@ -24,6 +25,7 @@
import android.media.MediaFormat;
import android.util.Log;
+import java.io.IOException;
/**
* Utility class for getting codec information like bit rate, fps, and etc.
@@ -43,30 +45,22 @@
private static final String VIDEO_AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
/**
* Check if given codec with given (w,h) is supported.
+ * @param codecName codec name
* @param mimeType codec type in mime format like MediaFormat.MIMETYPE_VIDEO_AVC
* @param w video width
* @param h video height
- * @param isEncoder whether the codec is encoder or decoder
* @return null if the configuration is not supported.
*/
public static CodecInfo getSupportedFormatInfo(
- String mimeType, int w, int h, boolean isEncoder) {
- MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- MediaFormat format = MediaFormat.createVideoFormat(mimeType, w, h);
- String codec = isEncoder
- ? mcl.findEncoderForFormat(format)
- : mcl.findDecoderForFormat(format);
- if (codec == null) { // not supported
+ String codecName, String mimeType, int w, int h) {
+ MediaCodec codec;
+ try {
+ codec = MediaCodec.createByCodecName(codecName);
+ } catch (IOException e) {
return null;
}
- CodecCapabilities cap = null;
- for (MediaCodecInfo info : mcl.getCodecInfos()) {
- if (info.getName().equals(codec)) {
- cap = info.getCapabilitiesForType(mimeType);
- break;
- }
- }
+ CodecCapabilities cap = codec.getCodecInfo().getCapabilitiesForType(mimeType);
if (cap.colorFormats.length == 0) {
Log.w(TAG, "no supported color format");
return null;
@@ -84,11 +78,15 @@
printIntArray("supported colors", cap.colorFormats);
VideoCapabilities vidCap = cap.getVideoCapabilities();
- if (mimeType.equals(VIDEO_AVC)) {
+ try {
info.mFps = vidCap.getSupportedFrameRatesFor(w, h).getUpper().intValue();
- info.mBitRate = vidCap.getBitrateRange().getUpper();
- Log.i(TAG, "AVC bit rate " + info.mBitRate + " fps " + info.mFps);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "unsupported size");
+ return null;
}
+ info.mBitRate = vidCap.getBitrateRange().getUpper();
+ Log.i(TAG, "test bit rate " + info.mBitRate + " fps " + info.mFps);
+ codec.release();
return info;
}
diff --git a/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/VideoEncoderDecoderTest.java b/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/VideoEncoderDecoderTest.java
index bf02d9c..6bd631b 100644
--- a/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/VideoEncoderDecoderTest.java
+++ b/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/VideoEncoderDecoderTest.java
@@ -23,10 +23,12 @@
import android.media.Image;
import android.media.Image.Plane;
import android.media.MediaCodec;
-import android.media.MediaCodecList;
+import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.util.Log;
+import android.util.Size;
import android.cts.util.CtsAndroidTestCase;
import com.android.cts.util.ResultType;
@@ -57,6 +59,9 @@
private static final long VIDEO_CODEC_WAIT_TIME_US = 5000;
private static final boolean VERBOSE = false;
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_H263 = MediaFormat.MIMETYPE_VIDEO_H263;
+ private static final String VIDEO_MPEG4 = MediaFormat.MIMETYPE_VIDEO_MPEG4;
private static final int TOTAL_FRAMES = 300;
private static final int NUMBER_OF_REPEAT = 10;
// i frame interval for encoder
@@ -81,6 +86,7 @@
private static final int PIXEL_CHECK_PER_FRAME = 1000;
// RMS error in pixel values above this will be treated as error.
private static final double PIXEL_RMS_ERROR_MARGAIN = 20.0;
+ private double mRmsErrorMargain = PIXEL_RMS_ERROR_MARGAIN;
private Random mRandom;
@Override
@@ -104,20 +110,71 @@
super.tearDown();
}
- public void testAvc0176x0144() throws Exception {
- doTest(VIDEO_AVC, 176, 144, NUMBER_OF_REPEAT);
+ private String getEncoderName(String mime, boolean isGoog) {
+ return getCodecName(mime, isGoog, true /* isEncoder */);
}
- public void testAvc0352x0288() throws Exception {
- doTest(VIDEO_AVC, 352, 288, NUMBER_OF_REPEAT);
+ private String getDecoderName(String mime, boolean isGoog) {
+ return getCodecName(mime, isGoog, false /* isEncoder */);
}
- public void testAvc0720x0480() throws Exception {
- doTest(VIDEO_AVC, 720, 480, NUMBER_OF_REPEAT);
+ private String getCodecName(String mime, boolean isGoog, boolean isEncoder) {
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ for (MediaCodecInfo info : mcl.getCodecInfos()) {
+ if (info.isEncoder() != isEncoder
+ || info.getName().toLowerCase().startsWith("omx.google.") != isGoog) {
+ continue;
+ }
+ CodecCapabilities caps = null;
+ try {
+ caps = info.getCapabilitiesForType(mime);
+ } catch (IllegalArgumentException e) { // mime is not supported
+ continue;
+ }
+ return info.getName();
+ }
+ return null;
}
- public void testAvc1280x0720() throws Exception {
- doTest(VIDEO_AVC, 1280, 720, NUMBER_OF_REPEAT);
+ // Avc tests
+ public void testAvc0176x0144Other() throws Exception {
+ doTestOther(VIDEO_AVC, 176, 144);
+ }
+
+ public void testAvc0176x0144Goog() throws Exception {
+ doTestGoog(VIDEO_AVC, 176, 144);
+ }
+
+ public void testAvc0320x0240Other() throws Exception {
+ doTestOther(VIDEO_AVC, 320, 240);
+ }
+
+ public void testAvc0320x0240Goog() throws Exception {
+ doTestGoog(VIDEO_AVC, 320, 240);
+ }
+
+ public void testAvc0352x0288Other() throws Exception {
+ doTestOther(VIDEO_AVC, 352, 288);
+ }
+
+ public void testAvc0352x0288Goog() throws Exception {
+ doTestGoog(VIDEO_AVC, 352, 288);
+ }
+
+ public void testAvc0720x0480Other() throws Exception {
+ doTestOther(VIDEO_AVC, 720, 480);
+ }
+
+ public void testAvc0720x0480Goog() throws Exception {
+ doTestGoog(VIDEO_AVC, 720, 480);
+ }
+
+ public void testAvc1280x0720Other() throws Exception {
+ doTestOther(VIDEO_AVC, 1280, 720);
+ }
+
+ public void testAvc1280x0720Goog() throws Exception {
+ doTestGoog(VIDEO_AVC, 1280, 720);
}
/**
@@ -125,8 +182,95 @@
* as 1080 is not multiple of 16, and it requires additional setting like stride
* which is not specified in API documentation.
*/
- public void testAvc1920x1072() throws Exception {
- doTest(VIDEO_AVC, 1920, 1072, NUMBER_OF_REPEAT);
+ public void testAvc1920x1072Other() throws Exception {
+ doTestOther(VIDEO_AVC, 1920, 1072);
+ }
+
+ public void testAvc1920x1072Goog() throws Exception {
+ doTestGoog(VIDEO_AVC, 1920, 1072);
+ }
+
+ // Vp8 tests
+ public void testVp80320x0180Other() throws Exception {
+ doTestOther(VIDEO_VP8, 320, 180);
+ }
+
+ public void testVp80320x0180Goog() throws Exception {
+ doTestGoog(VIDEO_VP8, 320, 180);
+ }
+
+ public void testVp80640x0360Other() throws Exception {
+ doTestOther(VIDEO_VP8, 640, 360);
+ }
+
+ public void testVp80640x0360Goog() throws Exception {
+ doTestGoog(VIDEO_VP8, 640, 360);
+ }
+
+ public void testVp81280x0720Other() throws Exception {
+ doTestOther(VIDEO_VP8, 1280, 720);
+ }
+
+ public void testVp81280x0720Goog() throws Exception {
+ doTestGoog(VIDEO_VP8, 1280, 720);
+ }
+
+ public void testVp81920x1080Other() throws Exception {
+ doTestOther(VIDEO_VP8, 1920, 1080);
+ }
+
+ public void testVp81920x1080Goog() throws Exception {
+ doTestGoog(VIDEO_VP8, 1920, 1080);
+ }
+
+ // H263 tests
+ public void testH2630176x0144Other() throws Exception {
+ doTestOther(VIDEO_H263, 176, 144);
+ }
+
+ public void testH2630176x0144Goog() throws Exception {
+ doTestGoog(VIDEO_H263, 176, 144);
+ }
+
+ public void testH2630352x0288Other() throws Exception {
+ doTestOther(VIDEO_H263, 352, 288);
+ }
+
+ public void testH2630352x0288Goog() throws Exception {
+ doTestGoog(VIDEO_H263, 352, 288);
+ }
+
+ // Mpeg4 tests
+ public void testMpeg40176x0144Other() throws Exception {
+ doTestOther(VIDEO_MPEG4, 176, 144);
+ }
+
+ public void testMpeg40176x0144Goog() throws Exception {
+ doTestGoog(VIDEO_MPEG4, 176, 144);
+ }
+
+ public void testMpeg40352x0288Other() throws Exception {
+ doTestOther(VIDEO_MPEG4, 352, 288);
+ }
+
+ public void testMpeg40352x0288Goog() throws Exception {
+ doTestGoog(VIDEO_MPEG4, 352, 288);
+ }
+
+ public void testMpeg40640x0480Other() throws Exception {
+ doTestOther(VIDEO_MPEG4, 640, 480);
+ }
+
+ public void testMpeg40640x0480Goog() throws Exception {
+ doTestGoog(VIDEO_MPEG4, 640, 480);
+ }
+
+ public void testMpeg41280x0720Other() throws Exception {
+ doTestOther(VIDEO_MPEG4, 1280, 720);
+ }
+
+ public void testMpeg41280x0720Goog() throws Exception {
+ doTestGoog(VIDEO_MPEG4, 1280, 720);
}
private boolean isSrcSemiPlanar() {
@@ -156,20 +300,44 @@
}
}
+ private void doTestGoog(String mimeType, int w, int h) throws Exception {
+ doTest(true /* isGoog */, mimeType, w, h, NUMBER_OF_REPEAT);
+ }
+
+ private void doTestOther(String mimeType, int w, int h) throws Exception {
+ doTest(false /* isGoog */, mimeType, w, h, NUMBER_OF_REPEAT);
+ }
+
/**
* Run encoding / decoding test for given mimeType of codec
+ * @param isGoog test google or non-google codec.
* @param mimeType like video/avc
* @param w video width
* @param h video height
* @param numberRepeat how many times to repeat the encoding / decoding process
*/
- private void doTest(String mimeType, int w, int h, int numberRepeat) throws Exception {
- CodecInfo infoEnc = CodecInfo.getSupportedFormatInfo(mimeType, w, h, true /* encoder */);
+ private void doTest(boolean isGoog, String mimeType, int w, int h, int numberRepeat)
+ throws Exception {
+ String encoderName = getEncoderName(mimeType, isGoog);
+ if (encoderName == null) {
+ Log.i(TAG, isGoog ? "Google " : "Non-google "
+ + "encoder for " + mimeType + " not found");
+ return;
+ }
+
+ String decoderName = getDecoderName(mimeType, isGoog);
+ if (decoderName == null) {
+ Log.i(TAG, isGoog ? "Google " : "Non-google "
+ + "decoder for " + mimeType + " not found");
+ return;
+ }
+
+ CodecInfo infoEnc = CodecInfo.getSupportedFormatInfo(encoderName, mimeType, w, h);
if (infoEnc == null) {
Log.i(TAG, "Encoder " + mimeType + " with " + w + "," + h + " not supported");
return;
}
- CodecInfo infoDec = CodecInfo.getSupportedFormatInfo(mimeType, w, h, false /* encoder */);
+ CodecInfo infoDec = CodecInfo.getSupportedFormatInfo(decoderName, mimeType, w, h);
assertNotNull(infoDec);
mVideoWidth = w;
mVideoHeight = h;
@@ -196,14 +364,15 @@
format.setInteger(MediaFormat.KEY_FRAME_RATE, infoEnc.mFps);
mFrameRate = infoEnc.mFps;
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, KEY_I_FRAME_INTERVAL);
- double encodingTime = runEncoder(VIDEO_AVC, format, TOTAL_FRAMES);
+
+ double encodingTime = runEncoder(encoderName, format, TOTAL_FRAMES);
// re-initialize format for decoder
format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, mimeType);
format.setInteger(MediaFormat.KEY_WIDTH, w);
format.setInteger(MediaFormat.KEY_HEIGHT, h);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, mDstColorFormat);
- double[] decoderResult = runDecoder(VIDEO_AVC, format);
+ double[] decoderResult = runDecoder(decoderName, format);
if (decoderResult == null) {
success = false;
} else {
@@ -227,26 +396,31 @@
ResultUnit.FPS);
getReportLog().printArray("encoder decoder", totalFpsResults, ResultType.HIGHER_BETTER,
ResultUnit.FPS);
+ getReportLog().printValue(mimeType + " encoder average fps for " + w + "x" + h,
+ Stat.getAverage(encoderFpsResults), ResultType.HIGHER_BETTER, ResultUnit.FPS);
+ getReportLog().printValue(mimeType + " decoder average fps for " + w + "x" + h,
+ Stat.getAverage(decoderFpsResults), ResultType.HIGHER_BETTER, ResultUnit.FPS);
getReportLog().printSummary("encoder decoder", Stat.getAverage(totalFpsResults),
ResultType.HIGHER_BETTER, ResultUnit.FPS);
// make sure that rms error is not too big.
for (int i = 0; i < numberRepeat; i++) {
- assertTrue(decoderRmsErrorResults[i] < PIXEL_RMS_ERROR_MARGAIN);
+ if (decoderRmsErrorResults[i] >= mRmsErrorMargain) {
+ fail("rms error is bigger than the limit "
+ + decoderRmsErrorResults[i] + " vs " + mRmsErrorMargain);
+ }
}
}
/**
* run encoder benchmarking
- * @param mimeType encoder type like video/avc
+ * @param encoderName encoder name
* @param format format of media to encode
* @param totalFrames total number of frames to encode
* @return time taken in ms to encode the frames. This does not include initialization time.
*/
- private double runEncoder(String mimeType, MediaFormat format, int totalFrames) {
+ private double runEncoder(String encoderName, MediaFormat format, int totalFrames) {
MediaCodec codec = null;
try {
- MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- String encoderName = mcl.findEncoderForFormat(format);
codec = MediaCodec.createByCodecName(encoderName);
codec.configure(
format,
@@ -254,9 +428,9 @@
null /* crypto */,
MediaCodec.CONFIGURE_FLAG_ENCODE);
} catch (IllegalStateException e) {
- Log.e(TAG, "codec '" + mimeType + "' failed configuration.");
+ Log.e(TAG, "codec '" + encoderName + "' failed configuration.");
codec.release();
- assertTrue("codec '" + mimeType + "' failed configuration.", false);
+ assertTrue("codec '" + encoderName + "' failed configuration.", false);
} catch (IOException | NullPointerException e) {
Log.i(TAG, "could not find codec for " + format);
return Double.NaN;
@@ -542,18 +716,16 @@
/**
* run encoder benchmarking with encoded stream stored from encoding phase
- * @param mimeType encoder type like video/avc
+ * @param decoderName decoder name
* @param format format of media to decode
* @return returns length-2 array with 0: time for decoding, 1 : rms error of pixels
*/
- private double[] runDecoder(String mimeType, MediaFormat format) {
- MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- String decoderName = mcl.findDecoderForFormat(format);
+ private double[] runDecoder(String decoderName, MediaFormat format) {
MediaCodec codec = null;
try {
codec = MediaCodec.createByCodecName(decoderName);
} catch (IOException | NullPointerException e) {
- Log.i(TAG, "could not find codec for " + format);
+ Log.i(TAG, "could not find decoder for " + format);
return null;
}
codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
@@ -580,6 +752,7 @@
ByteBuffer src = mEncodedOutputBuffer.get(inputBufferCount);
int writeSize = src.capacity();
dstBuf.put(src.array(), 0, writeSize);
+
codec.queueInputBuffer(
inputBufIndex,
0 /* offset */,
@@ -669,7 +842,9 @@
codec.stop();
codec.release();
codec = null;
- assertTrue(outFrameCount >= TOTAL_FRAMES);
+ if (outFrameCount < TOTAL_FRAMES) {
+ fail("Expecting " + TOTAL_FRAMES + " frames but get " + outFrameCount + " instead.");
+ }
// divide by 3 as sum is done for Y, U, V.
double errorRms = Math.sqrt(totalErrorSquared / PIXEL_CHECK_PER_FRAME / outFrameCount / 3);
double[] result = { (double) finish - start, errorRms };