blob: afa34e80641e88fd866006d10cb00e733dacac77 [file] [log] [blame]
/*
* Copyright (C) 2013 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 android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.opengl.GLES20;
import android.test.AndroidTestCase;
import android.util.Log;
import android.view.Surface;
/**
* General MediaCodec tests.
*
* In particular, check various API edge cases.
*/
public class MediaCodecTest extends AndroidTestCase {
private static final String TAG = "MediaCodecTest";
private static final boolean VERBOSE = false; // lots of logging
// parameters for the encoder
private static final String MIME_TYPE = "video/avc"; // H.264 Advanced Video Coding
private static final int BIT_RATE = 2000000; // 2Mbps
private static final int FRAME_RATE = 15; // 15fps
private static final int IFRAME_INTERVAL = 10; // 10 seconds between I-frames
private static final int WIDTH = 1280;
private static final int HEIGHT = 720;
/**
* Tests:
* <br> calling createInputSurface() before configure() throws exception
* <br> calling createInputSurface() after start() throws exception
* <br> calling createInputSurface() with a non-Surface color format throws exception
*/
public void testCreateInputSurfaceErrors() {
MediaFormat format = createMediaFormat();
MediaCodec encoder = null;
Surface surface = null;
// Replace color format with something that isn't COLOR_FormatSurface.
MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);
int colorFormat = findNonSurfaceColorFormat(codecInfo, MIME_TYPE);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
try {
encoder = MediaCodec.createByCodecName(codecInfo.getName());;
try {
surface = encoder.createInputSurface();
fail("createInputSurface should not work pre-configure");
} catch (IllegalStateException ise) {
// good
}
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
try {
surface = encoder.createInputSurface();
fail("createInputSurface should require COLOR_FormatSurface");
} catch (IllegalStateException ise) {
// good
}
encoder.start();
try {
surface = encoder.createInputSurface();
fail("createInputSurface should not work post-start");
} catch (IllegalStateException ise) {
// good
}
} finally {
if (encoder != null) {
encoder.stop();
encoder.release();
}
}
assertNull(surface);
}
/**
* Tests:
* <br> signaling end-of-stream before any data is sent works
* <br> signaling EOS twice throws exception
* <br> submitting a frame after EOS throws exception [TODO]
*/
public void testSignalSurfaceEOS() {
MediaFormat format = createMediaFormat();
MediaCodec encoder = null;
InputSurface inputSurface = null;
try {
encoder = MediaCodec.createEncoderByType(MIME_TYPE);
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
inputSurface = new InputSurface(encoder.createInputSurface());
inputSurface.makeCurrent();
encoder.start();
// send an immediate EOS
encoder.signalEndOfInputStream();
try {
encoder.signalEndOfInputStream();
fail("should not be able to signal EOS twice");
} catch (IllegalStateException ise) {
// good
}
// submit a frame post-EOS
GLES20.glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
try {
inputSurface.swapBuffers();
if (false) { // TODO
fail("should not be able to submit frame after EOS");
}
} catch (Exception ex) {
// good
}
} finally {
if (encoder != null) {
encoder.stop();
encoder.release();
}
if (inputSurface != null) {
inputSurface.release();
}
}
}
/**
* Tests:
* <br> dequeueInputBuffer() fails when encoder configured with an input Surface
*/
public void testDequeueSurface() {
MediaFormat format = createMediaFormat();
MediaCodec encoder = null;
Surface surface = null;
try {
encoder = MediaCodec.createEncoderByType(MIME_TYPE);
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
surface = encoder.createInputSurface();
encoder.start();
try {
encoder.dequeueInputBuffer(-1);
fail("dequeueInputBuffer should fail on encoder with input surface");
} catch (IllegalStateException ise) {
// good
}
} finally {
if (encoder != null) {
encoder.stop();
encoder.release();
}
if (surface != null) {
surface.release();
}
}
}
/**
* Tests:
* <br> configure() encoder with Surface, re-configure() without Surface works
* <br> sending EOS with signalEndOfInputStream on non-Surface encoder fails
*/
public void testReconfigureWithoutSurface() {
MediaFormat format = createMediaFormat();
MediaCodec encoder = null;
Surface surface = null;
try {
encoder = MediaCodec.createEncoderByType(MIME_TYPE);
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
surface = encoder.createInputSurface();
encoder.start();
encoder.getOutputBuffers();
// re-configure, this time without an input surface
if (VERBOSE) Log.d(TAG, "reconfiguring");
encoder.stop();
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
encoder.start();
if (VERBOSE) Log.d(TAG, "reconfigured");
encoder.getOutputBuffers();
encoder.dequeueInputBuffer(-1);
try {
encoder.signalEndOfInputStream();
fail("signalEndOfInputStream only works on surface input");
} catch (IllegalStateException ise) {
// good
}
} finally {
if (encoder != null) {
encoder.stop();
encoder.release();
}
if (surface != null) {
surface.release();
}
}
}
/**
* Creates a MediaFormat with the basic set of values.
*/
private static MediaFormat createMediaFormat() {
MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
return format;
}
/**
* Returns the first codec capable of encoding the specified MIME type, or null if no
* match was found.
*/
private static MediaCodecInfo selectCodec(String mimeType) {
int numCodecs = MediaCodecList.getCodecCount();
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)) {
return codecInfo;
}
}
}
return null;
}
/**
* Returns a color format that is supported by the codec and isn't COLOR_FormatSurface. Throws
* an exception if none found.
*/
private static int findNonSurfaceColorFormat(MediaCodecInfo codecInfo, String mimeType) {
MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
for (int i = 0; i < capabilities.colorFormats.length; i++) {
int colorFormat = capabilities.colorFormats[i];
if (colorFormat != MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface) {
return colorFormat;
}
}
fail("couldn't find a good color format for " + codecInfo.getName() + " / " + MIME_TYPE);
return 0; // not reached
}
}