1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.media.cts;
17 
18 import android.content.pm.PackageManager;
19 import android.cts.util.MediaUtils;
20 import android.media.MediaCodec;
21 import android.media.MediaCodecInfo;
22 import android.media.MediaCodecInfo.AudioCapabilities;
23 import android.media.MediaCodecInfo.CodecCapabilities;
24 import android.media.MediaCodecInfo.CodecProfileLevel;
25 import android.media.MediaCodecInfo.VideoCapabilities;
26 import static android.media.MediaCodecInfo.CodecProfileLevel.*;
27 import android.media.MediaCodecList;
28 import android.media.MediaFormat;
29 import static android.media.MediaFormat.MIMETYPE_VIDEO_AVC;
30 import static android.media.MediaFormat.MIMETYPE_VIDEO_H263;
31 import static android.media.MediaFormat.MIMETYPE_VIDEO_HEVC;
32 import static android.media.MediaFormat.MIMETYPE_VIDEO_MPEG4;
33 import static android.media.MediaFormat.MIMETYPE_VIDEO_VP8;
34 import static android.media.MediaFormat.MIMETYPE_VIDEO_VP9;
35 import android.media.MediaPlayer;
36 import android.os.Build;
37 import android.util.Log;
38 
39 import java.io.IOException;
40 import java.util.HashSet;
41 import java.util.Set;
42 import java.util.Arrays;
43 import java.util.Vector;
44 
45 /**
46  * Basic sanity test of data returned by MediaCodeCapabilities.
47  */
48 public class MediaCodecCapabilitiesTest extends MediaPlayerTestBase {
49 
50     private static final String TAG = "MediaCodecCapabilitiesTest";
51     private static final int PLAY_TIME_MS = 30000;
52     private static final int TIMEOUT_US = 1000000;  // 1 sec
53     private static final int IFRAME_INTERVAL = 10;          // 10 seconds between I-frames
54 
55     private final MediaCodecList mRegularCodecs =
56             new MediaCodecList(MediaCodecList.REGULAR_CODECS);
57     private final MediaCodecList mAllCodecs =
58             new MediaCodecList(MediaCodecList.ALL_CODECS);
59     private final MediaCodecInfo[] mRegularInfos =
60             mRegularCodecs.getCodecInfos();
61     private final MediaCodecInfo[] mAllInfos =
62             mAllCodecs.getCodecInfos();
63 
64     // Android device implementations with H.264 encoders, MUST support Baseline Profile Level 3.
65     // SHOULD support Main Profile/ Level 4, if supported the device must also support Main
66     // Profile/Level 4 decoding.
testH264EncoderProfileAndLevel()67     public void testH264EncoderProfileAndLevel() throws Exception {
68         if (!MediaUtils.checkEncoder(MIMETYPE_VIDEO_AVC)) {
69             return; // skip
70         }
71 
72         assertTrue(
73                 "H.264 must support Baseline Profile Level 3",
74                 hasEncoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3));
75 
76         if (hasEncoder(MIMETYPE_VIDEO_AVC, AVCProfileMain, AVCLevel4)) {
77             assertTrue(
78                     "H.264 decoder must support Main Profile Level 4 if it can encode it",
79                     hasDecoder(MIMETYPE_VIDEO_AVC, AVCProfileMain, AVCLevel4));
80         }
81     }
82 
83     // Android device implementations with H.264 decoders, MUST support Baseline Profile Level 3.
84     // Android Television Devices MUST support High Profile Level 4.2.
testH264DecoderProfileAndLevel()85     public void testH264DecoderProfileAndLevel() throws Exception {
86         if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_AVC)) {
87             return; // skip
88         }
89 
90         assertTrue(
91                 "H.264 must support Baseline Profile Level 3",
92                 hasDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3));
93 
94         if (isTv()) {
95             assertTrue(
96                     "H.264 must support High Profile Level 4.2 on TV",
97                     checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel42));
98         }
99     }
100 
101     // Android device implementations with H.263 encoders, MUST support Level 45.
testH263EncoderProfileAndLevel()102     public void testH263EncoderProfileAndLevel() throws Exception {
103         if (!MediaUtils.checkEncoder(MIMETYPE_VIDEO_H263)) {
104             return; // skip
105         }
106 
107         assertTrue(
108                 "H.263 must support Level 45",
109                 hasEncoder(MIMETYPE_VIDEO_H263, MPEG4ProfileSimple, H263Level45));
110     }
111 
112     // Android device implementations with H.263 decoders, MUST support Level 30.
testH263DecoderProfileAndLevel()113     public void testH263DecoderProfileAndLevel() throws Exception {
114         if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_H263)) {
115             return; // skip
116         }
117 
118         assertTrue(
119                 "H.263 must support Level 30",
120                 hasDecoder(MIMETYPE_VIDEO_H263, MPEG4ProfileSimple, H263Level30));
121     }
122 
123     // Android device implementations with MPEG-4 decoders, MUST support Simple Profile Level 3.
testMpeg4DecoderProfileAndLevel()124     public void testMpeg4DecoderProfileAndLevel() throws Exception {
125         if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_MPEG4)) {
126             return; // skip
127         }
128 
129         assertTrue(
130                 "MPEG-4 must support Simple Profile Level 3",
131                 hasDecoder(MIMETYPE_VIDEO_MPEG4, MPEG4ProfileSimple, MPEG4Level3));
132     }
133 
134     // Android device implementations, when supporting H.265 codec MUST support the Main Profile
135     // Level 3 Main tier.
136     // Android Television Devices MUST support the Main Profile Level 4.1 Main tier.
137     // When the UHD video decoding profile is supported, it MUST support Main10 Level 5 Main
138     // Tier profile.
testH265DecoderProfileAndLevel()139     public void testH265DecoderProfileAndLevel() throws Exception {
140         if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_HEVC)) {
141             return; // skip
142         }
143 
144         assertTrue(
145                 "H.265 must support Main Profile Main Tier Level 3",
146                 hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel3));
147 
148         if (isTv()) {
149             assertTrue(
150                     "H.265 must support Main Profile Main Tier Level 4.1 on TV",
151                     hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel41));
152         }
153 
154         if (isTv() && MediaUtils.canDecodeVideo(MIMETYPE_VIDEO_HEVC, 3840, 2160, 30)) {
155             assertTrue(
156                     "H.265 must support Main10 Profile Main Tier Level 5 if UHD is supported",
157                     hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain10, HEVCMainTierLevel5));
158         }
159     }
160 
testAvcBaseline1()161     public void testAvcBaseline1() throws Exception {
162         if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel1)) {
163             return; // skip
164         }
165 
166         // TODO: add a test stream
167         MediaUtils.skipTest(TAG, "no test stream");
168     }
169 
testAvcBaseline12()170     public void testAvcBaseline12() throws Exception {
171         if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel12)) {
172             return; // skip
173         }
174 
175         playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
176                 + "&itag=160&source=youtube&user=android-device-test"
177                 + "&sparams=ip,ipbits,expire,id,itag,source,user"
178                 + "&ip=0.0.0.0&ipbits=0&expire=19000000000"
179                 + "&signature=9EDCA0B395B8A949C511FD5E59B9F805CFF797FD."
180                 + "702DE9BA7AF96785FD6930AD2DD693A0486C880E"
181                 + "&key=ik0", 256, 144, PLAY_TIME_MS);
182     }
183 
testAvcBaseline30()184     public void testAvcBaseline30() throws Exception {
185         if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3)) {
186             return; // skip
187         }
188         playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
189                 + "&itag=18&source=youtube&user=android-device-test"
190                 + "&sparams=ip,ipbits,expire,id,itag,source,user"
191                 + "&ip=0.0.0.0&ipbits=0&expire=19000000000"
192                 + "&signature=7DCDE3A6594D0B91A27676A3CDC3A87B149F82EA."
193                 + "7A83031734CB1EDCE06766B6228842F954927960"
194                 + "&key=ik0", 640, 360, PLAY_TIME_MS);
195     }
196 
testAvcHigh31()197     public void testAvcHigh31() throws Exception {
198         if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel31)) {
199             return; // skip
200         }
201         playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
202                 + "&itag=22&source=youtube&user=android-device-test"
203                 + "&sparams=ip,ipbits,expire,id,itag,source,user"
204                 + "&ip=0.0.0.0&ipbits=0&expire=19000000000"
205                 + "&signature=179525311196616BD8E1381759B0E5F81A9E91B5."
206                 + "C4A50E44059FEBCC6BBC78E3B3A4E0E0065777"
207                 + "&key=ik0", 1280, 720, PLAY_TIME_MS);
208     }
209 
testAvcHigh40()210     public void testAvcHigh40() throws Exception {
211         if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel4)) {
212             return; // skip
213         }
214         if (Build.VERSION.SDK_INT < 18) {
215             MediaUtils.skipTest(TAG, "fragmented mp4 not supported");
216             return;
217         }
218         playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
219                 + "&itag=137&source=youtube&user=android-device-test"
220                 + "&sparams=ip,ipbits,expire,id,itag,source,user"
221                 + "&ip=0.0.0.0&ipbits=0&expire=19000000000"
222                 + "&signature=B0976085596DD42DEA3F08307F76587241CB132B."
223                 + "043B719C039E8B92F45391ADC0BE3665E2332930"
224                 + "&key=ik0", 1920, 1080, PLAY_TIME_MS);
225     }
226 
testHevcMain1()227     public void testHevcMain1() throws Exception {
228         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel1)) {
229             return; // skip
230         }
231 
232         // TODO: add a test stream
233         MediaUtils.skipTest(TAG, "no test stream");
234     }
235 
testHevcMain2()236     public void testHevcMain2() throws Exception {
237         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel2)) {
238             return; // skip
239         }
240 
241         // TODO: add a test stream
242         MediaUtils.skipTest(TAG, "no test stream");
243     }
244 
testHevcMain21()245     public void testHevcMain21() throws Exception {
246         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel21)) {
247             return; // skip
248         }
249 
250         // TODO: add a test stream
251         MediaUtils.skipTest(TAG, "no test stream");
252     }
253 
testHevcMain3()254     public void testHevcMain3() throws Exception {
255         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel3)) {
256             return; // skip
257         }
258 
259         // TODO: add a test stream
260         MediaUtils.skipTest(TAG, "no test stream");
261     }
262 
testHevcMain31()263     public void testHevcMain31() throws Exception {
264         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel31)) {
265             return; // skip
266         }
267 
268         // TODO: add a test stream
269         MediaUtils.skipTest(TAG, "no test stream");
270     }
271 
testHevcMain4()272     public void testHevcMain4() throws Exception {
273         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel4)) {
274             return; // skip
275         }
276 
277         // TODO: add a test stream
278         MediaUtils.skipTest(TAG, "no test stream");
279     }
280 
testHevcMain41()281     public void testHevcMain41() throws Exception {
282         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel41)) {
283             return; // skip
284         }
285 
286         // TODO: add a test stream
287         MediaUtils.skipTest(TAG, "no test stream");
288     }
289 
testHevcMain5()290     public void testHevcMain5() throws Exception {
291         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel5)) {
292             return; // skip
293         }
294 
295         // TODO: add a test stream
296         MediaUtils.skipTest(TAG, "no test stream");
297     }
298 
testHevcMain51()299     public void testHevcMain51() throws Exception {
300         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel51)) {
301             return; // skip
302         }
303 
304         // TODO: add a test stream
305         MediaUtils.skipTest(TAG, "no test stream");
306     }
307 
checkDecoder(String mime, int profile, int level)308     private boolean checkDecoder(String mime, int profile, int level) {
309         if (!hasDecoder(mime, profile, level)) {
310             MediaUtils.skipTest(TAG, "no " + mime + " decoder for profile "
311                     + profile + " and level " + level);
312             return false;
313         }
314         return true;
315     }
316 
hasDecoder(String mime, int profile, int level)317     private boolean hasDecoder(String mime, int profile, int level) {
318         return supports(mime, false /* isEncoder */, profile, level);
319     }
320 
hasEncoder(String mime, int profile, int level)321     private boolean hasEncoder(String mime, int profile, int level) {
322         return supports(mime, true /* isEncoder */, profile, level);
323     }
324 
supports( String mime, boolean isEncoder, int profile, int level)325     private boolean supports(
326             String mime, boolean isEncoder, int profile, int level) {
327         MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
328         for (MediaCodecInfo info : mcl.getCodecInfos()) {
329             if (isEncoder != info.isEncoder()) {
330                 continue;
331             }
332             try {
333                 CodecCapabilities caps = info.getCapabilitiesForType(mime);
334                 for (CodecProfileLevel pl : caps.profileLevels) {
335                     if (pl.profile != profile) {
336                         continue;
337                     }
338 
339                     // H.263 levels are not completely ordered:
340                     // Level45 support only implies Level10 support
341                     if (mime.equalsIgnoreCase(MIMETYPE_VIDEO_H263)) {
342                         if (pl.level != level && pl.level == H263Level45 && level > H263Level10) {
343                             continue;
344                         }
345                     }
346                     if (pl.level >= level) {
347                         return true;
348                     }
349                 }
350             } catch (IllegalArgumentException e) {
351             }
352         }
353         return false;
354     }
355 
isVideoMime(String mime)356     private boolean isVideoMime(String mime) {
357         return mime.toLowerCase().startsWith("video/");
358     }
359 
requiredAdaptiveFormats()360     private Set<String> requiredAdaptiveFormats() {
361         Set<String> adaptiveFormats = new HashSet<String>();
362         adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_AVC);
363         adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
364         adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_VP8);
365         adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_VP9);
366         return adaptiveFormats;
367     }
368 
testHaveAdaptiveVideoDecoderForAllSupportedFormats()369     public void testHaveAdaptiveVideoDecoderForAllSupportedFormats() {
370         Set<String> supportedFormats = new HashSet<String>();
371         boolean skipped = true;
372 
373         // gather all supported video formats
374         for (MediaCodecInfo info : mAllInfos) {
375             if (info.isEncoder()) {
376                 continue;
377             }
378             for (String mime : info.getSupportedTypes()) {
379                 if (isVideoMime(mime)) {
380                     supportedFormats.add(mime);
381                 }
382             }
383         }
384 
385         // limit to CDD-required formats for now
386         supportedFormats.retainAll(requiredAdaptiveFormats());
387 
388         // check if there is an adaptive decoder for each
389         for (String mime : supportedFormats) {
390             skipped = false;
391             // implicit assumption that QVGA video is always valid.
392             MediaFormat format = MediaFormat.createVideoFormat(mime, 176, 144);
393             format.setFeatureEnabled(CodecCapabilities.FEATURE_AdaptivePlayback, true);
394             String codec = mAllCodecs.findDecoderForFormat(format);
395             assertTrue(
396                     "could not find adaptive decoder for " + mime, codec != null);
397         }
398         if (skipped) {
399             MediaUtils.skipTest("no video decoders that are required to be adaptive found");
400         }
401     }
402 
testAllVideoDecodersAreAdaptive()403     public void testAllVideoDecodersAreAdaptive() {
404         Set<String> adaptiveFormats = requiredAdaptiveFormats();
405         boolean skipped = true;
406         for (MediaCodecInfo info : mAllInfos) {
407             if (info.isEncoder()) {
408                 continue;
409             }
410             for (String mime : info.getSupportedTypes()) {
411                 if (!isVideoMime(mime)
412                         // limit to CDD-required formats for now
413                         || !adaptiveFormats.contains(mime)) {
414                     continue;
415                 }
416                 skipped = false;
417                 CodecCapabilities caps = info.getCapabilitiesForType(mime);
418                 assertTrue(
419                     info.getName() + " is not adaptive for " + mime,
420                     caps.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback));
421             }
422         }
423         if (skipped) {
424             MediaUtils.skipTest("no video decoders that are required to be adaptive found");
425         }
426     }
427 
createReasonableVideoFormat( CodecCapabilities caps, String mime, boolean encoder, int width, int height)428     private MediaFormat createReasonableVideoFormat(
429             CodecCapabilities caps, String mime, boolean encoder, int width, int height) {
430         VideoCapabilities vidCaps = caps.getVideoCapabilities();
431         MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
432         if (encoder) {
433             // bitrate
434             int maxWidth = vidCaps.getSupportedWidths().getUpper();
435             int maxHeight = vidCaps.getSupportedHeightsFor(width).getUpper();
436             int maxRate = vidCaps.getSupportedFrameRatesFor(width, height).getUpper().intValue();
437             int bitrate = vidCaps.getBitrateRange().clamp(
438                     (int)(vidCaps.getBitrateRange().getUpper()
439                             / Math.sqrt((double)maxWidth * maxHeight / width / height)));
440             Log.i(TAG, "reasonable bitrate for " + width + "x" + height + "@" + maxRate
441                     + " " + mime + " = " + bitrate);
442             format.setInteger(format.KEY_BIT_RATE, bitrate);
443             format.setInteger(format.KEY_FRAME_RATE, maxRate);
444             format.setInteger(format.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
445         }
446         return format;
447     }
448 
testSecureCodecsAdvertiseSecurePlayback()449     public void testSecureCodecsAdvertiseSecurePlayback() throws IOException {
450         boolean skipped = true;
451         for (MediaCodecInfo info : mAllInfos) {
452             boolean isEncoder = info.isEncoder();
453             if (isEncoder || !info.getName().endsWith(".secure")) {
454                 continue;
455             }
456             for (String mime : info.getSupportedTypes()) {
457                 if (!isVideoMime(mime)) {
458                     continue;
459                 }
460                 skipped = false;
461                 CodecCapabilities caps = info.getCapabilitiesForType(mime);
462                 assertTrue(
463                         info.getName() + " does not advertise secure playback",
464                         caps.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback));
465             }
466         }
467         if (skipped) {
468             MediaUtils.skipTest("no video decoders found ending in .secure");
469         }
470     }
471 
testAllNonTunneledVideoCodecsSupportFlexibleYUV()472     public void testAllNonTunneledVideoCodecsSupportFlexibleYUV() throws IOException {
473         boolean skipped = true;
474         for (MediaCodecInfo info : mAllInfos) {
475             boolean isEncoder = info.isEncoder();
476             for (String mime: info.getSupportedTypes()) {
477                 if (!isVideoMime(mime)) {
478                     continue;
479                 }
480                 CodecCapabilities caps = info.getCapabilitiesForType(mime);
481                 if (caps.isFeatureRequired(CodecCapabilities.FEATURE_TunneledPlayback)
482                         || caps.isFeatureRequired(CodecCapabilities.FEATURE_SecurePlayback)) {
483                     continue;
484                 }
485                 skipped = false;
486                 boolean found = false;
487                 for (int c : caps.colorFormats) {
488                     if (c == caps.COLOR_FormatYUV420Flexible) {
489                         found = true;
490                         break;
491                     }
492                 }
493                 assertTrue(
494                     info.getName() + " does not advertise COLOR_FormatYUV420Flexible",
495                     found);
496 
497                 MediaCodec codec = null;
498                 MediaFormat format = null;
499                 try {
500                     codec = MediaCodec.createByCodecName(info.getName());
501                     // implicit assumption that QVGA video is always valid.
502                     format = createReasonableVideoFormat(caps, mime, isEncoder, 176, 144);
503                     format.setInteger(
504                             MediaFormat.KEY_COLOR_FORMAT,
505                             caps.COLOR_FormatYUV420Flexible);
506 
507                     codec.configure(format, null /* surface */, null /* crypto */,
508                             isEncoder ? codec.CONFIGURE_FLAG_ENCODE : 0);
509                     MediaFormat configuredFormat =
510                             isEncoder ? codec.getInputFormat() : codec.getOutputFormat();
511                     Log.d(TAG, "color format is " + configuredFormat.getInteger(
512                             MediaFormat.KEY_COLOR_FORMAT));
513                     if (isEncoder) {
514                         codec.start();
515                         int ix = codec.dequeueInputBuffer(TIMEOUT_US);
516                         assertNotNull(
517                                 info.getName() + " encoder has non-flexYUV input buffer #" + ix,
518                                 codec.getInputImage(ix));
519                     } else {
520                         // TODO: test these on various decoders (need test streams)
521                     }
522                 } finally {
523                     if (codec != null) {
524                         codec.release();
525                     }
526                 }
527             }
528         }
529         if (skipped) {
530             MediaUtils.skipTest("no non-tunneled/non-secure video decoders found");
531         }
532     }
533 
createMinFormat(String mime, CodecCapabilities caps)534     private static MediaFormat createMinFormat(String mime, CodecCapabilities caps) {
535         MediaFormat format;
536         if (caps.getVideoCapabilities() != null) {
537             VideoCapabilities vcaps = caps.getVideoCapabilities();
538             int minWidth = vcaps.getSupportedWidths().getLower();
539             int minHeight = vcaps.getSupportedHeightsFor(minWidth).getLower();
540             int minBitrate = vcaps.getBitrateRange().getLower();
541             format = MediaFormat.createVideoFormat(mime, minWidth, minHeight);
542             format.setInteger(MediaFormat.KEY_COLOR_FORMAT, caps.colorFormats[0]);
543             format.setInteger(MediaFormat.KEY_BIT_RATE, minBitrate);
544             format.setInteger(MediaFormat.KEY_FRAME_RATE, 10);
545             format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
546         } else {
547             AudioCapabilities acaps = caps.getAudioCapabilities();
548             int minSampleRate = acaps.getSupportedSampleRateRanges()[0].getLower();
549             int minChannelCount = 1;
550             int minBitrate = acaps.getBitrateRange().getLower();
551             format = MediaFormat.createAudioFormat(mime, minSampleRate, minChannelCount);
552             format.setInteger(MediaFormat.KEY_BIT_RATE, minBitrate);
553         }
554 
555         return format;
556     }
557 
getActualMax( boolean isEncoder, String name, String mime, CodecCapabilities caps, int max)558     private static int getActualMax(
559             boolean isEncoder, String name, String mime, CodecCapabilities caps, int max) {
560         int flag = isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0;
561         MediaFormat format = createMinFormat(mime, caps);
562         Log.d(TAG, "Test format " + format);
563         Vector<MediaCodec> codecs = new Vector<MediaCodec>();
564         MediaCodec codec = null;
565         for (int i = 0; i < max; ++i) {
566             try {
567                 Log.d(TAG, "Create codec " + name + " #" + i);
568                 codec = MediaCodec.createByCodecName(name);
569                 codec.configure(format, null, null, flag);
570                 codec.start();
571                 codecs.add(codec);
572                 codec = null;
573             } catch (IllegalArgumentException e) {
574                 fail("Got unexpected IllegalArgumentException " + e.getMessage());
575             } catch (IOException e) {
576                 fail("Got unexpected IOException " + e.getMessage());
577             } catch (MediaCodec.CodecException e) {
578                 // ERROR_INSUFFICIENT_RESOURCE is expected as the test keep creating codecs.
579                 // But other exception should be treated as failure.
580                 if (e.getErrorCode() == MediaCodec.CodecException.ERROR_INSUFFICIENT_RESOURCE) {
581                     Log.d(TAG, "Got CodecException with ERROR_INSUFFICIENT_RESOURCE.");
582                     break;
583                 } else {
584                     fail("Unexpected CodecException " + e.getDiagnosticInfo());
585                 }
586             } finally {
587                 if (codec != null) {
588                     Log.d(TAG, "release codec");
589                     codec.release();
590                     codec = null;
591                 }
592             }
593         }
594         int actualMax = codecs.size();
595         for (int i = 0; i < codecs.size(); ++i) {
596             Log.d(TAG, "release codec #" + i);
597             codecs.get(i).release();
598         }
599         codecs.clear();
600         return actualMax;
601     }
602 
testGetMaxSupportedInstances()603     public void testGetMaxSupportedInstances() {
604         final int MAX_INSTANCES = 32;
605         StringBuilder xmlOverrides = new StringBuilder();
606         MediaCodecList allCodecs = new MediaCodecList(MediaCodecList.ALL_CODECS);
607         for (MediaCodecInfo info : allCodecs.getCodecInfos()) {
608             Log.d(TAG, "codec: " + info.getName());
609             Log.d(TAG, "  isEncoder = " + info.isEncoder());
610 
611             String[] types = info.getSupportedTypes();
612             for (int j = 0; j < types.length; ++j) {
613                 Log.d(TAG, "calling getCapabilitiesForType " + types[j]);
614                 CodecCapabilities caps = info.getCapabilitiesForType(types[j]);
615                 int max = caps.getMaxSupportedInstances();
616                 Log.d(TAG, "getMaxSupportedInstances returns " + max);
617                 assertTrue(max > 0);
618 
619                 int actualMax = getActualMax(
620                         info.isEncoder(), info.getName(), types[j], caps, MAX_INSTANCES);
621                 Log.d(TAG, "actualMax " + actualMax + " vs reported max " + max);
622                 if (actualMax < (int)(max * 0.9) || actualMax > (int) Math.ceil(max * 1.1)) {
623                     String codec = "<MediaCodec name=\"" + info.getName() +
624                             "\" type=\"" + types[j] + "\" >";
625                     String limit = "    <Limit name=\"concurrent-instances\" max=\"" +
626                             actualMax + "\" />";
627                     xmlOverrides.append(codec);
628                     xmlOverrides.append("\n");
629                     xmlOverrides.append(limit);
630                     xmlOverrides.append("\n");
631                     xmlOverrides.append("</MediaCodec>\n");
632                 }
633             }
634         }
635 
636         if (xmlOverrides.length() > 0) {
637             String failMessage = "In order to pass the test, please publish following " +
638                     "codecs' concurrent instances limit in /etc/media_codecs.xml: \n";
639            fail(failMessage + xmlOverrides.toString());
640         }
641     }
642 }
643