blob: b3c8769fb4d940365e4e2fa4f82cf1cde8b62af4 [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.content.pm.PackageManager;
import android.cts.util.MediaUtils;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo.CodecProfileLevel;
import android.media.MediaCodecInfo.VideoCapabilities;
import static android.media.MediaCodecInfo.CodecProfileLevel.*;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import static android.media.MediaFormat.MIMETYPE_VIDEO_AVC;
import static android.media.MediaFormat.MIMETYPE_VIDEO_H263;
import static android.media.MediaFormat.MIMETYPE_VIDEO_HEVC;
import static android.media.MediaFormat.MIMETYPE_VIDEO_MPEG4;
import static android.media.MediaFormat.MIMETYPE_VIDEO_VP8;
import static android.media.MediaFormat.MIMETYPE_VIDEO_VP9;
import android.media.MediaPlayer;
import android.os.Build;
import android.util.Log;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.Arrays;
/**
* Basic sanity test of data returned by MediaCodeCapabilities.
*/
public class MediaCodecCapabilitiesTest extends MediaPlayerTestBase {
private static final String TAG = "MediaCodecCapabilitiesTest";
private static final int PLAY_TIME_MS = 30000;
private static final int TIMEOUT_US = 1000000; // 1 sec
private final MediaCodecList mRegularCodecs =
new MediaCodecList(MediaCodecList.REGULAR_CODECS);
private final MediaCodecList mAllCodecs =
new MediaCodecList(MediaCodecList.ALL_CODECS);
private final MediaCodecInfo[] mRegularInfos =
mRegularCodecs.getCodecInfos();
private final MediaCodecInfo[] mAllInfos =
mAllCodecs.getCodecInfos();
// Android device implementations with H.264 encoders, MUST support Baseline Profile Level 3.
// SHOULD support Main Profile/ Level 4, if supported the device must also support Main
// Profile/Level 4 decoding.
public void testH264EncoderProfileAndLevel() throws Exception {
if (!MediaUtils.checkEncoder(MIMETYPE_VIDEO_AVC)) {
return; // skip
}
assertTrue(
"H.264 must support Baseline Profile Level 3",
hasEncoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3));
if (hasEncoder(MIMETYPE_VIDEO_AVC, AVCProfileMain, AVCLevel4)) {
assertTrue(
"H.264 decoder must support Main Profile Level 4 if it can encode it",
hasDecoder(MIMETYPE_VIDEO_AVC, AVCProfileMain, AVCLevel4));
}
}
// Android device implementations with H.264 decoders, MUST support Baseline Profile Level 3.
// Android Television Devices MUST support High Profile Level 4.2.
public void testH264DecoderProfileAndLevel() throws Exception {
if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_AVC)) {
return; // skip
}
assertTrue(
"H.264 must support Baseline Profile Level 3",
hasDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3));
if (isTv()) {
assertTrue(
"H.264 must support High Profile Level 4.2 on TV",
checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel42));
}
}
// Android device implementations with H.263 encoders, MUST support Level 45.
public void testH263EncoderProfileAndLevel() throws Exception {
if (!MediaUtils.checkEncoder(MIMETYPE_VIDEO_H263)) {
return; // skip
}
assertTrue(
"H.263 must support Level 45",
hasEncoder(MIMETYPE_VIDEO_H263, MPEG4ProfileSimple, H263Level45));
}
// Android device implementations with H.263 decoders, MUST support Level 30.
public void testH263DecoderProfileAndLevel() throws Exception {
if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_H263)) {
return; // skip
}
assertTrue(
"H.263 must support Level 30",
hasDecoder(MIMETYPE_VIDEO_H263, MPEG4ProfileSimple, H263Level30));
}
// Android device implementations with MPEG-4 decoders, MUST support Simple Profile Level 3.
public void testMpeg4DecoderProfileAndLevel() throws Exception {
if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_MPEG4)) {
return; // skip
}
assertTrue(
"MPEG-4 must support Simple Profile Level 3",
hasDecoder(MIMETYPE_VIDEO_MPEG4, MPEG4ProfileSimple, MPEG4Level3));
}
// Android device implementations, when supporting H.265 codec MUST support the Main Profile
// Level 3 Main tier.
// Android Television Devices MUST support the Main Profile Level 4.1 Main tier.
// When the UHD video decoding profile is supported, it MUST support Main10 Level 5 Main
// Tier profile.
public void testH265DecoderProfileAndLevel() throws Exception {
if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_HEVC)) {
return; // skip
}
assertTrue(
"H.265 must support Main Profile Main Tier Level 3",
hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel3));
if (isTv()) {
assertTrue(
"H.265 must support Main Profile Main Tier Level 4.1 on TV",
hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel41));
}
if (isTv() && MediaUtils.canDecodeVideo(MIMETYPE_VIDEO_HEVC, 3840, 2160, 30)) {
assertTrue(
"H.265 must support Main10 Profile Main Tier Level 5 if UHD is supported",
hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain10, HEVCMainTierLevel5));
}
}
public void testAvcBaseline1() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel1)) {
return; // skip
}
// TODO: add a test stream
MediaUtils.skipTest(TAG, "no test stream");
}
public void testAvcBaseline12() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel12)) {
return; // skip
}
playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ "&itag=160&source=youtube&user=android-device-test"
+ "&sparams=ip,ipbits,expire,id,itag,source,user"
+ "&ip=0.0.0.0&ipbits=0&expire=999999999999999999"
+ "&signature=341692D20FACCAE25B90EA2C131EA6ADCD8E2384."
+ "9EB08C174BE401AAD20FB85EE4DBA51A2882BB60"
+ "&key=test_key1", 256, 144, PLAY_TIME_MS);
}
public void testAvcBaseline30() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3)) {
return; // skip
}
playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ "&itag=18&source=youtube&user=android-device-test"
+ "&sparams=ip,ipbits,expire,id,itag,source,user"
+ "&ip=0.0.0.0&ipbits=0&expire=999999999999999999"
+ "&signature=8701A45F6422229D46ABB25A22E2C00C94024606."
+ "08BCDF16C3F744C49D4C8A8AD1C38B3DC1810918"
+ "&key=test_key1", 640, 360, PLAY_TIME_MS);
}
public void testAvcHigh31() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel31)) {
return; // skip
}
playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ "&itag=22&source=youtube&user=android-device-test"
+ "&sparams=ip,ipbits,expire,id,itag,source,user"
+ "&ip=0.0.0.0&ipbits=0&expire=999999999999999999"
+ "&signature=42969CA8F7FFAE432B7135BC811F96F7C4172C3F."
+ "1A8A92EA714C1B7C98A05DDF2DE90854CDD7638B"
+ "&key=test_key1", 1280, 720, PLAY_TIME_MS);
}
public void testAvcHigh40() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel4)) {
return; // skip
}
if (Build.VERSION.SDK_INT < 18) {
MediaUtils.skipTest(TAG, "fragmented mp4 not supported");
return;
}
playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ "&itag=137&source=youtube&user=android-device-test"
+ "&sparams=ip,ipbits,expire,id,itag,source,user"
+ "&ip=0.0.0.0&ipbits=0&expire=999999999999999999"
+ "&signature=2C836E04C4DDC98649CD44C8B91813D98342D1D1."
+ "870A848D54CA08C197E5FDC34ED45E6ED7DB5CDA"
+ "&key=test_key1", 1920, 1080, PLAY_TIME_MS);
}
public void testHevcMain1() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel1)) {
return; // skip
}
// TODO: add a test stream
MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain2() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel2)) {
return; // skip
}
// TODO: add a test stream
MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain21() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel21)) {
return; // skip
}
// TODO: add a test stream
MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain3() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel3)) {
return; // skip
}
// TODO: add a test stream
MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain31() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel31)) {
return; // skip
}
// TODO: add a test stream
MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain4() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel4)) {
return; // skip
}
// TODO: add a test stream
MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain41() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel41)) {
return; // skip
}
// TODO: add a test stream
MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain5() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel5)) {
return; // skip
}
// TODO: add a test stream
MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain51() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel51)) {
return; // skip
}
// TODO: add a test stream
MediaUtils.skipTest(TAG, "no test stream");
}
private boolean checkDecoder(String mime, int profile, int level) {
if (!hasDecoder(mime, profile, level)) {
MediaUtils.skipTest(TAG, "no " + mime + " decoder for profile "
+ profile + " and level " + level);
return false;
}
return true;
}
private boolean hasDecoder(String mime, int profile, int level) {
return supports(mime, false /* isEncoder */, profile, level);
}
private boolean hasEncoder(String mime, int profile, int level) {
return supports(mime, true /* isEncoder */, profile, level);
}
private boolean supports(
String mime, boolean isEncoder, int profile, int level) {
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
for (MediaCodecInfo info : mcl.getCodecInfos()) {
if (isEncoder != info.isEncoder()) {
continue;
}
try {
CodecCapabilities caps = info.getCapabilitiesForType(mime);
for (CodecProfileLevel pl : caps.profileLevels) {
if (pl.profile != profile) {
continue;
}
// H.263 levels are not completely ordered:
// Level45 support only implies Level10 support
if (mime.equalsIgnoreCase(MIMETYPE_VIDEO_H263)) {
if (pl.level != level && pl.level == H263Level45 && level > H263Level10) {
continue;
}
}
if (pl.level >= level) {
return true;
}
}
} catch (IllegalArgumentException e) {
}
}
return false;
}
private boolean isVideoMime(String mime) {
return mime.toLowerCase().startsWith("video/");
}
private Set<String> requiredAdaptiveFormats() {
Set<String> adaptiveFormats = new HashSet<String>();
adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_AVC);
adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_VP8);
adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_VP9);
return adaptiveFormats;
}
public void testHaveAdaptiveVideoDecoderForAllSupportedFormats() {
Set<String> supportedFormats = new HashSet<String>();
boolean skipped = true;
// gather all supported video formats
for (MediaCodecInfo info : mAllInfos) {
if (info.isEncoder()) {
continue;
}
for (String mime : info.getSupportedTypes()) {
if (isVideoMime(mime)) {
supportedFormats.add(mime);
}
}
}
// limit to CDD-required formats for now
supportedFormats.retainAll(requiredAdaptiveFormats());
// check if there is an adaptive decoder for each
for (String mime : supportedFormats) {
skipped = false;
// implicit assumption that QVGA video is always valid.
MediaFormat format = MediaFormat.createVideoFormat(mime, 176, 144);
format.setFeatureEnabled(CodecCapabilities.FEATURE_AdaptivePlayback, true);
String codec = mAllCodecs.findDecoderForFormat(format);
assertTrue(
"could not find adaptive decoder for " + mime, codec != null);
}
if (skipped) {
MediaUtils.skipTest("no video decoders that are required to be adaptive found");
}
}
public void testAllVideoDecodersAreAdaptive() {
Set<String> adaptiveFormats = requiredAdaptiveFormats();
boolean skipped = true;
for (MediaCodecInfo info : mAllInfos) {
if (info.isEncoder()) {
continue;
}
for (String mime : info.getSupportedTypes()) {
if (!isVideoMime(mime)
// limit to CDD-required formats for now
|| !adaptiveFormats.contains(mime)) {
continue;
}
skipped = false;
CodecCapabilities caps = info.getCapabilitiesForType(mime);
assertTrue(
info.getName() + " is not adaptive for " + mime,
caps.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback));
}
}
if (skipped) {
MediaUtils.skipTest("no video decoders that are required to be adaptive found");
}
}
private MediaFormat createReasonableVideoFormat(
CodecCapabilities caps, String mime, boolean encoder, int width, int height) {
VideoCapabilities vidCaps = caps.getVideoCapabilities();
MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
if (encoder) {
// bitrate
int maxWidth = vidCaps.getSupportedWidths().getUpper();
int maxHeight = vidCaps.getSupportedHeightsFor(width).getUpper();
int maxRate = vidCaps.getSupportedFrameRatesFor(width, height).getUpper().intValue();
int bitrate = vidCaps.getBitrateRange().clamp(
(int)(vidCaps.getBitrateRange().getUpper()
/ Math.sqrt((double)maxWidth * maxHeight / width / height)));
Log.i(TAG, "reasonable bitrate for " + width + "x" + height + "@" + maxRate
+ " " + mime + " = " + bitrate);
format.setInteger(format.KEY_BIT_RATE, bitrate);
format.setInteger(format.KEY_FRAME_RATE, maxRate);
}
return format;
}
public void testSecureCodecsAdvertiseSecurePlayback() throws IOException {
boolean skipped = true;
for (MediaCodecInfo info : mAllInfos) {
boolean isEncoder = info.isEncoder();
if (isEncoder || !info.getName().endsWith(".secure")) {
continue;
}
for (String mime : info.getSupportedTypes()) {
if (!isVideoMime(mime)) {
continue;
}
skipped = false;
CodecCapabilities caps = info.getCapabilitiesForType(mime);
assertTrue(
info.getName() + " does not advertise secure playback",
caps.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback));
}
}
if (skipped) {
MediaUtils.skipTest("no video decoders found ending in .secure");
}
}
public void testAllNonTunneledVideoCodecsSupportFlexibleYUV() throws IOException {
boolean skipped = true;
for (MediaCodecInfo info : mAllInfos) {
boolean isEncoder = info.isEncoder();
for (String mime: info.getSupportedTypes()) {
if (!isVideoMime(mime)) {
continue;
}
CodecCapabilities caps = info.getCapabilitiesForType(mime);
if (caps.isFeatureRequired(CodecCapabilities.FEATURE_TunneledPlayback)
|| caps.isFeatureRequired(CodecCapabilities.FEATURE_SecurePlayback)) {
continue;
}
skipped = false;
assertTrue(
info.getName() + " does not advertise COLOR_FormatYUV420Flexible",
Arrays.asList(caps.colorFormats).contains(
caps.COLOR_FormatYUV420Flexible));
MediaCodec codec = null;
MediaFormat format = null;
try {
codec = MediaCodec.createByCodecName(info.getName());
// implicit assumption that QVGA video is always valid.
format = createReasonableVideoFormat(caps, mime, isEncoder, 176, 144);
format.setInteger(
MediaFormat.KEY_COLOR_FORMAT,
caps.COLOR_FormatYUV420Flexible);
codec.configure(format, null /* surface */, null /* crypto */,
isEncoder ? codec.CONFIGURE_FLAG_ENCODE : 0);
MediaFormat configuredFormat =
isEncoder ? codec.getInputFormat() : codec.getOutputFormat();
Log.d(TAG, "color format is " + configuredFormat.getInteger(
MediaFormat.KEY_COLOR_FORMAT));
if (isEncoder) {
codec.start();
int ix = codec.dequeueInputBuffer(TIMEOUT_US);
assertNotNull(
info.getName() + " encoder has non-flexYUV input buffer #" + ix,
codec.getInputImage(ix));
} else {
// TODO: test these on various decoders (need test streams)
}
} finally {
if (codec != null) {
codec.release();
}
}
}
}
if (skipped) {
MediaUtils.skipTest("no non-tunneled/non-secure video decoders found");
}
}
}