1/*
2 * Copyright (C) 2012 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
17package android.media;
18
19import android.util.Log;
20import android.util.Pair;
21import android.util.Range;
22import android.util.Rational;
23import android.util.Size;
24
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.Comparator;
28import java.util.HashMap;
29import java.util.Map;
30import java.util.Set;
31
32import static android.media.Utils.intersectSortedDistinctRanges;
33import static android.media.Utils.sortDistinctRanges;
34import static com.android.internal.util.Preconditions.checkArgumentPositive;
35import static com.android.internal.util.Preconditions.checkNotNull;
36
37/**
38 * Provides information about a given media codec available on the device. You can
39 * iterate through all codecs available by querying {@link MediaCodecList}. For example,
40 * here's how to find an encoder that supports a given MIME type:
41 * <pre>
42 * private static MediaCodecInfo selectCodec(String mimeType) {
43 *     int numCodecs = MediaCodecList.getCodecCount();
44 *     for (int i = 0; i &lt; numCodecs; i++) {
45 *         MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
46 *
47 *         if (!codecInfo.isEncoder()) {
48 *             continue;
49 *         }
50 *
51 *         String[] types = codecInfo.getSupportedTypes();
52 *         for (int j = 0; j &lt; types.length; j++) {
53 *             if (types[j].equalsIgnoreCase(mimeType)) {
54 *                 return codecInfo;
55 *             }
56 *         }
57 *     }
58 *     return null;
59 * }</pre>
60 *
61 */
62public final class MediaCodecInfo {
63    private boolean mIsEncoder;
64    private String mName;
65    private Map<String, CodecCapabilities> mCaps;
66
67    /* package private */ MediaCodecInfo(
68            String name, boolean isEncoder, CodecCapabilities[] caps) {
69        mName = name;
70        mIsEncoder = isEncoder;
71        mCaps = new HashMap<String, CodecCapabilities>();
72        for (CodecCapabilities c: caps) {
73            mCaps.put(c.getMimeType(), c);
74        }
75    }
76
77    /**
78     * Retrieve the codec name.
79     */
80    public final String getName() {
81        return mName;
82    }
83
84    /**
85     * Query if the codec is an encoder.
86     */
87    public final boolean isEncoder() {
88        return mIsEncoder;
89    }
90
91    /**
92     * Query the media types supported by the codec.
93     */
94    public final String[] getSupportedTypes() {
95        Set<String> typeSet = mCaps.keySet();
96        String[] types = typeSet.toArray(new String[typeSet.size()]);
97        Arrays.sort(types);
98        return types;
99    }
100
101    private static int checkPowerOfTwo(int value, String message) {
102        if ((value & (value - 1)) != 0) {
103            throw new IllegalArgumentException(message);
104        }
105        return value;
106    }
107
108    private static class Feature {
109        public String mName;
110        public int mValue;
111        public boolean mDefault;
112        public Feature(String name, int value, boolean def) {
113            mName = name;
114            mValue = value;
115            mDefault = def;
116        }
117    }
118
119    // COMMON CONSTANTS
120    private static final Range<Integer> POSITIVE_INTEGERS =
121        Range.create(1, Integer.MAX_VALUE);
122    private static final Range<Long> POSITIVE_LONGS =
123        Range.create(1l, Long.MAX_VALUE);
124    private static final Range<Rational> POSITIVE_RATIONALS =
125        Range.create(new Rational(1, Integer.MAX_VALUE),
126                     new Rational(Integer.MAX_VALUE, 1));
127    private static final Range<Integer> SIZE_RANGE = Range.create(1, 32768);
128    private static final Range<Integer> FRAME_RATE_RANGE = Range.create(0, 960);
129    private static final Range<Integer> BITRATE_RANGE = Range.create(0, 500000000);
130
131    // found stuff that is not supported by framework (=> this should not happen)
132    private static final int ERROR_UNRECOGNIZED   = (1 << 0);
133    // found profile/level for which we don't have capability estimates
134    private static final int ERROR_UNSUPPORTED    = (1 << 1);
135    // have not found any profile/level for which we don't have capability estimate
136    private static final int ERROR_NONE_SUPPORTED = (1 << 2);
137
138
139    /**
140     * Encapsulates the capabilities of a given codec component.
141     * For example, what profile/level combinations it supports and what colorspaces
142     * it is capable of providing the decoded data in, as well as some
143     * codec-type specific capability flags.
144     * <p>You can get an instance for a given {@link MediaCodecInfo} object with
145     * {@link MediaCodecInfo#getCapabilitiesForType getCapabilitiesForType()}, passing a MIME type.
146     */
147    public static final class CodecCapabilities {
148        public CodecCapabilities() {
149        }
150
151        // CLASSIFICATION
152        private String mMime;
153
154        // LEGACY FIELDS
155
156        // Enumerates supported profile/level combinations as defined
157        // by the type of encoded data. These combinations impose restrictions
158        // on video resolution, bitrate... and limit the available encoder tools
159        // such as B-frame support, arithmetic coding...
160        public CodecProfileLevel[] profileLevels;  // NOTE this array is modifiable by user
161
162        // from OMX_COLOR_FORMATTYPE
163        public static final int COLOR_FormatMonochrome              = 1;
164        public static final int COLOR_Format8bitRGB332              = 2;
165        public static final int COLOR_Format12bitRGB444             = 3;
166        public static final int COLOR_Format16bitARGB4444           = 4;
167        public static final int COLOR_Format16bitARGB1555           = 5;
168        public static final int COLOR_Format16bitRGB565             = 6;
169        public static final int COLOR_Format16bitBGR565             = 7;
170        public static final int COLOR_Format18bitRGB666             = 8;
171        public static final int COLOR_Format18bitARGB1665           = 9;
172        public static final int COLOR_Format19bitARGB1666           = 10;
173        public static final int COLOR_Format24bitRGB888             = 11;
174        public static final int COLOR_Format24bitBGR888             = 12;
175        public static final int COLOR_Format24bitARGB1887           = 13;
176        public static final int COLOR_Format25bitARGB1888           = 14;
177        public static final int COLOR_Format32bitBGRA8888           = 15;
178        public static final int COLOR_Format32bitARGB8888           = 16;
179        public static final int COLOR_FormatYUV411Planar            = 17;
180        public static final int COLOR_FormatYUV411PackedPlanar      = 18;
181        public static final int COLOR_FormatYUV420Planar            = 19;
182        public static final int COLOR_FormatYUV420PackedPlanar      = 20;
183        public static final int COLOR_FormatYUV420SemiPlanar        = 21;
184        public static final int COLOR_FormatYUV422Planar            = 22;
185        public static final int COLOR_FormatYUV422PackedPlanar      = 23;
186        public static final int COLOR_FormatYUV422SemiPlanar        = 24;
187        public static final int COLOR_FormatYCbYCr                  = 25;
188        public static final int COLOR_FormatYCrYCb                  = 26;
189        public static final int COLOR_FormatCbYCrY                  = 27;
190        public static final int COLOR_FormatCrYCbY                  = 28;
191        public static final int COLOR_FormatYUV444Interleaved       = 29;
192        public static final int COLOR_FormatRawBayer8bit            = 30;
193        public static final int COLOR_FormatRawBayer10bit           = 31;
194        public static final int COLOR_FormatRawBayer8bitcompressed  = 32;
195        public static final int COLOR_FormatL2                      = 33;
196        public static final int COLOR_FormatL4                      = 34;
197        public static final int COLOR_FormatL8                      = 35;
198        public static final int COLOR_FormatL16                     = 36;
199        public static final int COLOR_FormatL24                     = 37;
200        public static final int COLOR_FormatL32                     = 38;
201        public static final int COLOR_FormatYUV420PackedSemiPlanar  = 39;
202        public static final int COLOR_FormatYUV422PackedSemiPlanar  = 40;
203        public static final int COLOR_Format18BitBGR666             = 41;
204        public static final int COLOR_Format24BitARGB6666           = 42;
205        public static final int COLOR_Format24BitABGR6666           = 43;
206
207        public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100;
208        // COLOR_FormatSurface indicates that the data will be a GraphicBuffer metadata reference.
209        // In OMX this is called OMX_COLOR_FormatAndroidOpaque.
210        public static final int COLOR_FormatSurface                   = 0x7F000789;
211        // This corresponds to YUV_420_888 format
212        public static final int COLOR_FormatYUV420Flexible            = 0x7F420888;
213        public static final int COLOR_QCOM_FormatYUV420SemiPlanar     = 0x7fa30c00;
214
215        /**
216         * Defined in the OpenMAX IL specs, color format values are drawn from
217         * OMX_COLOR_FORMATTYPE.
218         */
219        public int[] colorFormats; // NOTE this array is modifiable by user
220
221        // FEATURES
222
223        private int mFlagsSupported;
224        private int mFlagsRequired;
225        private int mFlagsVerified;
226
227        /**
228         * <b>video decoder only</b>: codec supports seamless resolution changes.
229         */
230        public static final String FEATURE_AdaptivePlayback       = "adaptive-playback";
231
232        /**
233         * <b>video decoder only</b>: codec supports secure decryption.
234         */
235        public static final String FEATURE_SecurePlayback         = "secure-playback";
236
237        /**
238         * <b>video or audio decoder only</b>: codec supports tunneled playback.
239         */
240        public static final String FEATURE_TunneledPlayback       = "tunneled-playback";
241
242        /**
243         * Query codec feature capabilities.
244         * <p>
245         * These features are supported to be used by the codec.  These
246         * include optional features that can be turned on, as well as
247         * features that are always on.
248         */
249        public final boolean isFeatureSupported(String name) {
250            return checkFeature(name, mFlagsSupported);
251        }
252
253        /**
254         * Query codec feature requirements.
255         * <p>
256         * These features are required to be used by the codec, and as such,
257         * they are always turned on.
258         */
259        public final boolean isFeatureRequired(String name) {
260            return checkFeature(name, mFlagsRequired);
261        }
262
263        private static final Feature[] decoderFeatures = {
264            new Feature(FEATURE_AdaptivePlayback, (1 << 0), true),
265            new Feature(FEATURE_SecurePlayback,   (1 << 1), false),
266            new Feature(FEATURE_TunneledPlayback, (1 << 2), false),
267        };
268
269        /** @hide */
270        public String[] validFeatures() {
271            Feature[] features = getValidFeatures();
272            String[] res = new String[features.length];
273            for (int i = 0; i < res.length; i++) {
274                res[i] = features[i].mName;
275            }
276            return res;
277        }
278
279        private Feature[] getValidFeatures() {
280            if (!isEncoder()) {
281                return decoderFeatures;
282            }
283            return new Feature[] {};
284        }
285
286        private boolean checkFeature(String name, int flags) {
287            for (Feature feat: getValidFeatures()) {
288                if (feat.mName.equals(name)) {
289                    return (flags & feat.mValue) != 0;
290                }
291            }
292            return false;
293        }
294
295        /** @hide */
296        public boolean isRegular() {
297            // regular codecs only require default features
298            for (Feature feat: getValidFeatures()) {
299                if (!feat.mDefault && isFeatureRequired(feat.mName)) {
300                    return false;
301                }
302            }
303            return true;
304        }
305
306        /**
307         * Query whether codec supports a given {@link MediaFormat}.
308         * @param format media format with optional feature directives.
309         * @throws IllegalArgumentException if format is not a valid media format.
310         * @return whether the codec capabilities support the given format
311         *         and feature requests.
312         */
313        public final boolean isFormatSupported(MediaFormat format) {
314            final Map<String, Object> map = format.getMap();
315            final String mime = (String)map.get(MediaFormat.KEY_MIME);
316
317            // mime must match if present
318            if (mime != null && !mMime.equalsIgnoreCase(mime)) {
319                return false;
320            }
321
322            // check feature support
323            for (Feature feat: getValidFeatures()) {
324                Integer yesNo = (Integer)map.get(MediaFormat.KEY_FEATURE_ + feat.mName);
325                if (yesNo == null) {
326                    continue;
327                }
328                if ((yesNo == 1 && !isFeatureSupported(feat.mName)) ||
329                        (yesNo == 0 && isFeatureRequired(feat.mName))) {
330                    return false;
331                }
332            }
333            if (mAudioCaps != null && !mAudioCaps.supportsFormat(format)) {
334                return false;
335            }
336            if (mVideoCaps != null && !mVideoCaps.supportsFormat(format)) {
337                return false;
338            }
339            if (mEncoderCaps != null && !mEncoderCaps.supportsFormat(format)) {
340                return false;
341            }
342            return true;
343        }
344
345        // errors while reading profile levels - accessed from sister capabilities
346        int mError;
347
348        private static final String TAG = "CodecCapabilities";
349
350        // NEW-STYLE CAPABILITIES
351        private AudioCapabilities mAudioCaps;
352        private VideoCapabilities mVideoCaps;
353        private EncoderCapabilities mEncoderCaps;
354        private MediaFormat mDefaultFormat;
355
356        /**
357         * Returns a MediaFormat object with default values for configurations that have
358         * defaults.
359         */
360        public MediaFormat getDefaultFormat() {
361            return mDefaultFormat;
362        }
363
364        /**
365         * Returns the mime type for which this codec-capability object was created.
366         */
367        public String getMimeType() {
368            return mMime;
369        }
370
371        private boolean isAudio() {
372            return mAudioCaps != null;
373        }
374
375        /**
376         * Returns the audio capabilities or {@code null} if this is not an audio codec.
377         */
378        public AudioCapabilities getAudioCapabilities() {
379            return mAudioCaps;
380        }
381
382        private boolean isEncoder() {
383            return mEncoderCaps != null;
384        }
385
386        /**
387         * Returns the encoding capabilities or {@code null} if this is not an encoder.
388         */
389        public EncoderCapabilities getEncoderCapabilities() {
390            return mEncoderCaps;
391        }
392
393        private boolean isVideo() {
394            return mVideoCaps != null;
395        }
396
397        /**
398         * Returns the video capabilities or {@code null} if this is not a video codec.
399         */
400        public VideoCapabilities getVideoCapabilities() {
401            return mVideoCaps;
402        }
403
404        /** @hide */
405        public CodecCapabilities dup() {
406            return new CodecCapabilities(
407                // clone writable arrays
408                Arrays.copyOf(profileLevels, profileLevels.length),
409                Arrays.copyOf(colorFormats, colorFormats.length),
410                isEncoder(),
411                mFlagsVerified,
412                mDefaultFormat,
413                mCapabilitiesInfo);
414        }
415
416        /**
417         * Retrieve the codec capabilities for a certain {@code mime type}, {@code
418         * profile} and {@code level}.  If the type, or profile-level combination
419         * is not understood by the framework, it returns null.
420         */
421        public static CodecCapabilities createFromProfileLevel(
422                String mime, int profile, int level) {
423            CodecProfileLevel pl = new CodecProfileLevel();
424            pl.profile = profile;
425            pl.level = level;
426            MediaFormat defaultFormat = new MediaFormat();
427            defaultFormat.setString(MediaFormat.KEY_MIME, mime);
428
429            CodecCapabilities ret = new CodecCapabilities(
430                new CodecProfileLevel[] { pl }, new int[0], true /* encoder */,
431                0 /* flags */, defaultFormat, new MediaFormat() /* info */);
432            if (ret.mError != 0) {
433                return null;
434            }
435            return ret;
436        }
437
438        /* package private */ CodecCapabilities(
439                CodecProfileLevel[] profLevs, int[] colFmts,
440                boolean encoder, int flags,
441                Map<String, Object>defaultFormatMap,
442                Map<String, Object>capabilitiesMap) {
443            this(profLevs, colFmts, encoder, flags,
444                    new MediaFormat(defaultFormatMap),
445                    new MediaFormat(capabilitiesMap));
446        }
447
448        private MediaFormat mCapabilitiesInfo;
449
450        /* package private */ CodecCapabilities(
451                CodecProfileLevel[] profLevs, int[] colFmts, boolean encoder, int flags,
452                MediaFormat defaultFormat, MediaFormat info) {
453            final Map<String, Object> map = info.getMap();
454            profileLevels = profLevs;
455            colorFormats = colFmts;
456            mFlagsVerified = flags;
457            mDefaultFormat = defaultFormat;
458            mCapabilitiesInfo = info;
459            mMime = mDefaultFormat.getString(MediaFormat.KEY_MIME);
460
461            if (mMime.toLowerCase().startsWith("audio/")) {
462                mAudioCaps = AudioCapabilities.create(info, this);
463                mAudioCaps.setDefaultFormat(mDefaultFormat);
464            } else if (mMime.toLowerCase().startsWith("video/")) {
465                mVideoCaps = VideoCapabilities.create(info, this);
466            }
467            if (encoder) {
468                mEncoderCaps = EncoderCapabilities.create(info, this);
469                mEncoderCaps.setDefaultFormat(mDefaultFormat);
470            }
471
472            for (Feature feat: getValidFeatures()) {
473                String key = MediaFormat.KEY_FEATURE_ + feat.mName;
474                Integer yesNo = (Integer)map.get(key);
475                if (yesNo == null) {
476                    continue;
477                }
478                if (yesNo > 0) {
479                    mFlagsRequired |= feat.mValue;
480                }
481                mFlagsSupported |= feat.mValue;
482                mDefaultFormat.setInteger(key, 1);
483                // TODO restrict features by mFlagsVerified once all codecs reliably verify them
484            }
485        }
486    }
487
488    /**
489     * A class that supports querying the audio capabilities of a codec.
490     */
491    public static final class AudioCapabilities {
492        private static final String TAG = "AudioCapabilities";
493        private CodecCapabilities mParent;
494        private Range<Integer> mBitrateRange;
495
496        private int[] mSampleRates;
497        private Range<Integer>[] mSampleRateRanges;
498        private int mMaxInputChannelCount;
499
500        private static final int MAX_INPUT_CHANNEL_COUNT = 30;
501
502        /**
503         * Returns the range of supported bitrates in bits/second.
504         */
505        public Range<Integer> getBitrateRange() {
506            return mBitrateRange;
507        }
508
509        /**
510         * Returns the array of supported sample rates if the codec
511         * supports only discrete values.  Otherwise, it returns
512         * {@code null}.  The array is sorted in ascending order.
513         */
514        public int[] getSupportedSampleRates() {
515            return Arrays.copyOf(mSampleRates, mSampleRates.length);
516        }
517
518        /**
519         * Returns the array of supported sample rate ranges.  The
520         * array is sorted in ascending order, and the ranges are
521         * distinct.
522         */
523        public Range<Integer>[] getSupportedSampleRateRanges() {
524            return Arrays.copyOf(mSampleRateRanges, mSampleRateRanges.length);
525        }
526
527        /**
528         * Returns the maximum number of input channels supported.  The codec
529         * supports any number of channels between 1 and this maximum value.
530         */
531        public int getMaxInputChannelCount() {
532            return mMaxInputChannelCount;
533        }
534
535        /* no public constructor */
536        private AudioCapabilities() { }
537
538        /** @hide */
539        public static AudioCapabilities create(
540                MediaFormat info, CodecCapabilities parent) {
541            AudioCapabilities caps = new AudioCapabilities();
542            caps.init(info, parent);
543            return caps;
544        }
545
546        /** @hide */
547        public void init(MediaFormat info, CodecCapabilities parent) {
548            mParent = parent;
549            initWithPlatformLimits();
550            applyLevelLimits();
551            parseFromInfo(info);
552        }
553
554        private void initWithPlatformLimits() {
555            mBitrateRange = Range.create(0, Integer.MAX_VALUE);
556            mMaxInputChannelCount = MAX_INPUT_CHANNEL_COUNT;
557            // mBitrateRange = Range.create(1, 320000);
558            mSampleRateRanges = new Range[] { Range.create(8000, 96000) };
559            mSampleRates = null;
560        }
561
562        private boolean supports(Integer sampleRate, Integer inputChannels) {
563            // channels and sample rates are checked orthogonally
564            if (inputChannels != null &&
565                    (inputChannels < 1 || inputChannels > mMaxInputChannelCount)) {
566                return false;
567            }
568            if (sampleRate != null) {
569                int ix = Utils.binarySearchDistinctRanges(
570                        mSampleRateRanges, sampleRate);
571                if (ix < 0) {
572                    return false;
573                }
574            }
575            return true;
576        }
577
578        /**
579         * Query whether the sample rate is supported by the codec.
580         */
581        public boolean isSampleRateSupported(int sampleRate) {
582            return supports(sampleRate, null);
583        }
584
585        /** modifies rates */
586        private void limitSampleRates(int[] rates) {
587            Arrays.sort(rates);
588            ArrayList<Range<Integer>> ranges = new ArrayList<Range<Integer>>();
589            for (int rate: rates) {
590                if (supports(rate, null /* channels */)) {
591                    ranges.add(Range.create(rate, rate));
592                }
593            }
594            mSampleRateRanges = ranges.toArray(new Range[ranges.size()]);
595            createDiscreteSampleRates();
596        }
597
598        private void createDiscreteSampleRates() {
599            mSampleRates = new int[mSampleRateRanges.length];
600            for (int i = 0; i < mSampleRateRanges.length; i++) {
601                mSampleRates[i] = mSampleRateRanges[i].getLower();
602            }
603        }
604
605        /** modifies rateRanges */
606        private void limitSampleRates(Range<Integer>[] rateRanges) {
607            sortDistinctRanges(rateRanges);
608            mSampleRateRanges = intersectSortedDistinctRanges(mSampleRateRanges, rateRanges);
609
610            // check if all values are discrete
611            for (Range<Integer> range: mSampleRateRanges) {
612                if (!range.getLower().equals(range.getUpper())) {
613                    mSampleRates = null;
614                    return;
615                }
616            }
617            createDiscreteSampleRates();
618        }
619
620        private void applyLevelLimits() {
621            int[] sampleRates = null;
622            Range<Integer> sampleRateRange = null, bitRates = null;
623            int maxChannels = 0;
624            String mime = mParent.getMimeType();
625
626            if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MPEG)) {
627                sampleRates = new int[] {
628                        8000, 11025, 12000,
629                        16000, 22050, 24000,
630                        32000, 44100, 48000 };
631                bitRates = Range.create(8000, 320000);
632                maxChannels = 2;
633            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
634                sampleRates = new int[] { 8000 };
635                bitRates = Range.create(4750, 12200);
636                maxChannels = 1;
637            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_WB)) {
638                sampleRates = new int[] { 16000 };
639                bitRates = Range.create(6600, 23850);
640                maxChannels = 1;
641            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AAC)) {
642                sampleRates = new int[] {
643                        7350, 8000,
644                        11025, 12000, 16000,
645                        22050, 24000, 32000,
646                        44100, 48000, 64000,
647                        88200, 96000 };
648                bitRates = Range.create(8000, 510000);
649                maxChannels = 48;
650            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_VORBIS)) {
651                bitRates = Range.create(32000, 500000);
652                sampleRateRange = Range.create(8000, 192000);
653                maxChannels = 255;
654            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_OPUS)) {
655                bitRates = Range.create(6000, 510000);
656                sampleRates = new int[] { 8000, 12000, 16000, 24000, 48000 };
657                maxChannels = 255;
658            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) {
659                sampleRateRange = Range.create(1, 96000);
660                bitRates = Range.create(1, 10000000);
661                maxChannels = 8;
662            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
663                sampleRateRange = Range.create(1, 655350);
664                // lossless codec, so bitrate is ignored
665                maxChannels = 255;
666            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_ALAW)
667                    || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_MLAW)) {
668                sampleRates = new int[] { 8000 };
669                bitRates = Range.create(64000, 64000);
670                // platform allows multiple channels for this format
671            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MSGSM)) {
672                sampleRates = new int[] { 8000 };
673                bitRates = Range.create(13000, 13000);
674                maxChannels = 1;
675            } else {
676                Log.w(TAG, "Unsupported mime " + mime);
677                mParent.mError |= ERROR_UNSUPPORTED;
678            }
679
680            // restrict ranges
681            if (sampleRates != null) {
682                limitSampleRates(sampleRates);
683            } else if (sampleRateRange != null) {
684                limitSampleRates(new Range[] { sampleRateRange });
685            }
686            applyLimits(maxChannels, bitRates);
687        }
688
689        private void applyLimits(int maxInputChannels, Range<Integer> bitRates) {
690            mMaxInputChannelCount = Range.create(1, mMaxInputChannelCount)
691                    .clamp(maxInputChannels);
692            if (bitRates != null) {
693                mBitrateRange = mBitrateRange.intersect(bitRates);
694            }
695        }
696
697        private void parseFromInfo(MediaFormat info) {
698            int maxInputChannels = MAX_INPUT_CHANNEL_COUNT;
699            Range<Integer> bitRates = POSITIVE_INTEGERS;
700
701            if (info.containsKey("sample-rate-ranges")) {
702                String[] rateStrings = info.getString("sample-rate-ranges").split(",");
703                Range<Integer>[] rateRanges = new Range[rateStrings.length];
704                for (int i = 0; i < rateStrings.length; i++) {
705                    rateRanges[i] = Utils.parseIntRange(rateStrings[i], null);
706                }
707                limitSampleRates(rateRanges);
708            }
709            if (info.containsKey("max-channel-count")) {
710                maxInputChannels = Utils.parseIntSafely(
711                        info.getString("max-channel-count"), maxInputChannels);
712            }
713            if (info.containsKey("bitrate-range")) {
714                bitRates = bitRates.intersect(
715                        Utils.parseIntRange(info.getString("bitrate-range"), bitRates));
716            }
717            applyLimits(maxInputChannels, bitRates);
718        }
719
720        /** @hide */
721        public void setDefaultFormat(MediaFormat format) {
722            // report settings that have only a single choice
723            if (mBitrateRange.getLower().equals(mBitrateRange.getUpper())) {
724                format.setInteger(MediaFormat.KEY_BIT_RATE, mBitrateRange.getLower());
725            }
726            if (mMaxInputChannelCount == 1) {
727                // mono-only format
728                format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
729            }
730            if (mSampleRates != null && mSampleRates.length == 1) {
731                format.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRates[0]);
732            }
733        }
734
735        /** @hide */
736        public boolean supportsFormat(MediaFormat format) {
737            Map<String, Object> map = format.getMap();
738            Integer sampleRate = (Integer)map.get(MediaFormat.KEY_SAMPLE_RATE);
739            Integer channels = (Integer)map.get(MediaFormat.KEY_CHANNEL_COUNT);
740            if (!supports(sampleRate, channels)) {
741                return false;
742            }
743
744            // nothing to do for:
745            // KEY_CHANNEL_MASK: codecs don't get this
746            // KEY_IS_ADTS:      required feature for all AAC decoders
747            return true;
748        }
749    }
750
751    /**
752     * A class that supports querying the video capabilities of a codec.
753     */
754    public static final class VideoCapabilities {
755        private static final String TAG = "VideoCapabilities";
756        private CodecCapabilities mParent;
757        private Range<Integer> mBitrateRange;
758
759        private Range<Integer> mHeightRange;
760        private Range<Integer> mWidthRange;
761        private Range<Integer> mBlockCountRange;
762        private Range<Integer> mHorizontalBlockRange;
763        private Range<Integer> mVerticalBlockRange;
764        private Range<Rational> mAspectRatioRange;
765        private Range<Rational> mBlockAspectRatioRange;
766        private Range<Long> mBlocksPerSecondRange;
767        private Range<Integer> mFrameRateRange;
768
769        private int mBlockWidth;
770        private int mBlockHeight;
771        private int mWidthAlignment;
772        private int mHeightAlignment;
773        private int mSmallerDimensionUpperLimit;
774
775        /**
776         * Returns the range of supported bitrates in bits/second.
777         */
778        public Range<Integer> getBitrateRange() {
779            return mBitrateRange;
780        }
781
782        /**
783         * Returns the range of supported video widths.
784         */
785        public Range<Integer> getSupportedWidths() {
786            return mWidthRange;
787        }
788
789        /**
790         * Returns the range of supported video heights.
791         */
792        public Range<Integer> getSupportedHeights() {
793            return mHeightRange;
794        }
795
796        /**
797         * Returns the alignment requirement for video width (in pixels).
798         *
799         * This is a power-of-2 value that video width must be a
800         * multiple of.
801         */
802        public int getWidthAlignment() {
803            return mWidthAlignment;
804        }
805
806        /**
807         * Returns the alignment requirement for video height (in pixels).
808         *
809         * This is a power-of-2 value that video height must be a
810         * multiple of.
811         */
812        public int getHeightAlignment() {
813            return mHeightAlignment;
814        }
815
816        /**
817         * Return the upper limit on the smaller dimension of width or height.
818         * <p></p>
819         * Some codecs have a limit on the smaller dimension, whether it be
820         * the width or the height.  E.g. a codec may only be able to handle
821         * up to 1920x1080 both in landscape and portrait mode (1080x1920).
822         * In this case the maximum width and height are both 1920, but the
823         * smaller dimension limit will be 1080. For other codecs, this is
824         * {@code Math.min(getSupportedWidths().getUpper(),
825         * getSupportedHeights().getUpper())}.
826         *
827         * @hide
828         */
829        public int getSmallerDimensionUpperLimit() {
830            return mSmallerDimensionUpperLimit;
831        }
832
833        /**
834         * Returns the range of supported frame rates.
835         * <p>
836         * This is not a performance indicator.  Rather, it expresses the
837         * limits specified in the coding standard, based on the complexities
838         * of encoding material for later playback at a certain frame rate,
839         * or the decoding of such material in non-realtime.
840         */
841        public Range<Integer> getSupportedFrameRates() {
842            return mFrameRateRange;
843        }
844
845        /**
846         * Returns the range of supported video widths for a video height.
847         * @param height the height of the video
848         */
849        public Range<Integer> getSupportedWidthsFor(int height) {
850            try {
851                Range<Integer> range = mWidthRange;
852                if (!mHeightRange.contains(height)
853                        || (height % mHeightAlignment) != 0) {
854                    throw new IllegalArgumentException("unsupported height");
855                }
856                final int heightInBlocks = Utils.divUp(height, mBlockHeight);
857
858                // constrain by block count and by block aspect ratio
859                final int minWidthInBlocks = Math.max(
860                        Utils.divUp(mBlockCountRange.getLower(), heightInBlocks),
861                        (int)Math.ceil(mBlockAspectRatioRange.getLower().doubleValue()
862                                * heightInBlocks));
863                final int maxWidthInBlocks = Math.min(
864                        mBlockCountRange.getUpper() / heightInBlocks,
865                        (int)(mBlockAspectRatioRange.getUpper().doubleValue()
866                                * heightInBlocks));
867                range = range.intersect(
868                        (minWidthInBlocks - 1) * mBlockWidth + mWidthAlignment,
869                        maxWidthInBlocks * mBlockWidth);
870
871                // constrain by smaller dimension limit
872                if (height > mSmallerDimensionUpperLimit) {
873                    range = range.intersect(1, mSmallerDimensionUpperLimit);
874                }
875
876                // constrain by aspect ratio
877                range = range.intersect(
878                        (int)Math.ceil(mAspectRatioRange.getLower().doubleValue()
879                                * height),
880                        (int)(mAspectRatioRange.getUpper().doubleValue() * height));
881                return range;
882            } catch (IllegalArgumentException e) {
883                // should not be here
884                Log.w(TAG, "could not get supported widths for " + height , e);
885                throw new IllegalArgumentException("unsupported height");
886            }
887        }
888
889        /**
890         * Returns the range of supported video heights for a video width
891         * @param width the width of the video
892         */
893        public Range<Integer> getSupportedHeightsFor(int width) {
894            try {
895                Range<Integer> range = mHeightRange;
896                if (!mWidthRange.contains(width)
897                        || (width % mWidthAlignment) != 0) {
898                    throw new IllegalArgumentException("unsupported width");
899                }
900                final int widthInBlocks = Utils.divUp(width, mBlockWidth);
901
902                // constrain by block count and by block aspect ratio
903                final int minHeightInBlocks = Math.max(
904                        Utils.divUp(mBlockCountRange.getLower(), widthInBlocks),
905                        (int)Math.ceil(widthInBlocks /
906                                mBlockAspectRatioRange.getUpper().doubleValue()));
907                final int maxHeightInBlocks = Math.min(
908                        mBlockCountRange.getUpper() / widthInBlocks,
909                        (int)(widthInBlocks /
910                                mBlockAspectRatioRange.getLower().doubleValue()));
911                range = range.intersect(
912                        (minHeightInBlocks - 1) * mBlockHeight + mHeightAlignment,
913                        maxHeightInBlocks * mBlockHeight);
914
915                // constrain by smaller dimension limit
916                if (width > mSmallerDimensionUpperLimit) {
917                    range = range.intersect(1, mSmallerDimensionUpperLimit);
918                }
919
920                // constrain by aspect ratio
921                range = range.intersect(
922                        (int)Math.ceil(width /
923                                mAspectRatioRange.getUpper().doubleValue()),
924                        (int)(width / mAspectRatioRange.getLower().doubleValue()));
925                return range;
926            } catch (IllegalArgumentException e) {
927                // should not be here
928                Log.w(TAG, "could not get supported heights for " + width , e);
929                throw new IllegalArgumentException("unsupported width");
930            }
931        }
932
933        /**
934         * Returns the range of supported video frame rates for a video size.
935         * <p>
936         * This is not a performance indicator.  Rather, it expresses the limits specified in
937         * the coding standard, based on the complexities of encoding material of a given
938         * size for later playback at a certain frame rate, or the decoding of such material
939         * in non-realtime.
940
941         * @param width the width of the video
942         * @param height the height of the video
943         */
944        public Range<Double> getSupportedFrameRatesFor(int width, int height) {
945            Range<Integer> range = mHeightRange;
946            if (!supports(width, height, null)) {
947                throw new IllegalArgumentException("unsupported size");
948            }
949            final int blockCount =
950                Utils.divUp(width, mBlockWidth) * Utils.divUp(height, mBlockHeight);
951
952            return Range.create(
953                    Math.max(mBlocksPerSecondRange.getLower() / (double) blockCount,
954                            (double) mFrameRateRange.getLower()),
955                    Math.min(mBlocksPerSecondRange.getUpper() / (double) blockCount,
956                            (double) mFrameRateRange.getUpper()));
957        }
958
959        /**
960         * Returns whether a given video size ({@code width} and
961         * {@code height}) and {@code frameRate} combination is supported.
962         */
963        public boolean areSizeAndRateSupported(
964                int width, int height, double frameRate) {
965            return supports(width, height, frameRate);
966        }
967
968        /**
969         * Returns whether a given video size ({@code width} and
970         * {@code height}) is supported.
971         */
972        public boolean isSizeSupported(int width, int height) {
973            return supports(width, height, null);
974        }
975
976        private boolean supports(
977                Integer width, Integer height, Number rate) {
978            boolean ok = true;
979
980            if (ok && width != null) {
981                ok = mWidthRange.contains(width)
982                        && (width % mWidthAlignment == 0);
983            }
984            if (ok && height != null) {
985                ok = mHeightRange.contains(height)
986                        && (height % mHeightAlignment == 0);
987            }
988            if (ok && rate != null) {
989                ok = mFrameRateRange.contains(Utils.intRangeFor(rate.doubleValue()));
990            }
991            if (ok && height != null && width != null) {
992                ok = Math.min(height, width) <= mSmallerDimensionUpperLimit;
993
994                final int widthInBlocks = Utils.divUp(width, mBlockWidth);
995                final int heightInBlocks = Utils.divUp(height, mBlockHeight);
996                final int blockCount = widthInBlocks * heightInBlocks;
997                ok = ok && mBlockCountRange.contains(blockCount)
998                        && mBlockAspectRatioRange.contains(
999                                new Rational(widthInBlocks, heightInBlocks))
1000                        && mAspectRatioRange.contains(new Rational(width, height));
1001                if (ok && rate != null) {
1002                    double blocksPerSec = blockCount * rate.doubleValue();
1003                    ok = mBlocksPerSecondRange.contains(
1004                            Utils.longRangeFor(blocksPerSec));
1005                }
1006            }
1007            return ok;
1008        }
1009
1010        /**
1011         * @hide
1012         * @throws java.lang.ClassCastException */
1013        public boolean supportsFormat(MediaFormat format) {
1014            final Map<String, Object> map = format.getMap();
1015            Integer width = (Integer)map.get(MediaFormat.KEY_WIDTH);
1016            Integer height = (Integer)map.get(MediaFormat.KEY_HEIGHT);
1017            Number rate = (Number)map.get(MediaFormat.KEY_FRAME_RATE);
1018
1019            // we ignore color-format for now as it is not reliably reported by codec
1020
1021            return supports(width, height, rate);
1022        }
1023
1024        /* no public constructor */
1025        private VideoCapabilities() { }
1026
1027        /** @hide */
1028        public static VideoCapabilities create(
1029                MediaFormat info, CodecCapabilities parent) {
1030            VideoCapabilities caps = new VideoCapabilities();
1031            caps.init(info, parent);
1032            return caps;
1033        }
1034
1035        /** @hide */
1036        public void init(MediaFormat info, CodecCapabilities parent) {
1037            mParent = parent;
1038            initWithPlatformLimits();
1039            applyLevelLimits();
1040            parseFromInfo(info);
1041            updateLimits();
1042        }
1043
1044        /** @hide */
1045        public Size getBlockSize() {
1046            return new Size(mBlockWidth, mBlockHeight);
1047        }
1048
1049        /** @hide */
1050        public Range<Integer> getBlockCountRange() {
1051            return mBlockCountRange;
1052        }
1053
1054        /** @hide */
1055        public Range<Long> getBlocksPerSecondRange() {
1056            return mBlocksPerSecondRange;
1057        }
1058
1059        /** @hide */
1060        public Range<Rational> getAspectRatioRange(boolean blocks) {
1061            return blocks ? mBlockAspectRatioRange : mAspectRatioRange;
1062        }
1063
1064        private void initWithPlatformLimits() {
1065            mBitrateRange = BITRATE_RANGE;
1066
1067            mWidthRange  = SIZE_RANGE;
1068            mHeightRange = SIZE_RANGE;
1069            mFrameRateRange = FRAME_RATE_RANGE;
1070
1071            mHorizontalBlockRange = SIZE_RANGE;
1072            mVerticalBlockRange   = SIZE_RANGE;
1073
1074            // full positive ranges are supported as these get calculated
1075            mBlockCountRange      = POSITIVE_INTEGERS;
1076            mBlocksPerSecondRange = POSITIVE_LONGS;
1077
1078            mBlockAspectRatioRange = POSITIVE_RATIONALS;
1079            mAspectRatioRange      = POSITIVE_RATIONALS;
1080
1081            // YUV 4:2:0 requires 2:2 alignment
1082            mWidthAlignment = 2;
1083            mHeightAlignment = 2;
1084            mBlockWidth = 2;
1085            mBlockHeight = 2;
1086            mSmallerDimensionUpperLimit = SIZE_RANGE.getUpper();
1087        }
1088
1089        private void parseFromInfo(MediaFormat info) {
1090            final Map<String, Object> map = info.getMap();
1091            Size blockSize = new Size(mBlockWidth, mBlockHeight);
1092            Size alignment = new Size(mWidthAlignment, mHeightAlignment);
1093            Range<Integer> counts = null, widths = null, heights = null;
1094            Range<Integer> frameRates = null, bitRates = null;
1095            Range<Long> blockRates = null;
1096            Range<Rational> ratios = null, blockRatios = null;
1097
1098            blockSize = Utils.parseSize(map.get("block-size"), blockSize);
1099            alignment = Utils.parseSize(map.get("alignment"), alignment);
1100            counts = Utils.parseIntRange(map.get("block-count-range"), null);
1101            blockRates =
1102                Utils.parseLongRange(map.get("blocks-per-second-range"), null);
1103            {
1104                Object o = map.get("size-range");
1105                Pair<Size, Size> sizeRange = Utils.parseSizeRange(o);
1106                if (sizeRange != null) {
1107                    try {
1108                        widths = Range.create(
1109                                sizeRange.first.getWidth(),
1110                                sizeRange.second.getWidth());
1111                        heights = Range.create(
1112                                sizeRange.first.getHeight(),
1113                                sizeRange.second.getHeight());
1114                    } catch (IllegalArgumentException e) {
1115                        Log.w(TAG, "could not parse size range '" + o + "'");
1116                        widths = null;
1117                        heights = null;
1118                    }
1119                }
1120            }
1121            // for now this just means using the smaller max size as 2nd
1122            // upper limit.
1123            // for now we are keeping the profile specific "width/height
1124            // in macroblocks" limits.
1125            if (Integer.valueOf(1).equals(map.get("feature-can-swap-width-height"))) {
1126                if (widths != null) {
1127                    mSmallerDimensionUpperLimit =
1128                        Math.min(widths.getUpper(), heights.getUpper());
1129                    widths = heights = widths.extend(heights);
1130                } else {
1131                    Log.w(TAG, "feature can-swap-width-height is best used with size-range");
1132                    mSmallerDimensionUpperLimit =
1133                        Math.min(mWidthRange.getUpper(), mHeightRange.getUpper());
1134                    mWidthRange = mHeightRange = mWidthRange.extend(mHeightRange);
1135                }
1136            }
1137
1138            ratios = Utils.parseRationalRange(
1139                    map.get("block-aspect-ratio-range"), null);
1140            blockRatios = Utils.parseRationalRange(
1141                    map.get("pixel-aspect-ratio-range"), null);
1142            frameRates = Utils.parseIntRange(map.get("frame-rate-range"), null);
1143            if (frameRates != null) {
1144                try {
1145                    frameRates = frameRates.intersect(FRAME_RATE_RANGE);
1146                } catch (IllegalArgumentException e) {
1147                    Log.w(TAG, "frame rate range (" + frameRates
1148                            + ") is out of limits: " + FRAME_RATE_RANGE);
1149                    frameRates = null;
1150                }
1151            }
1152            bitRates = Utils.parseIntRange(map.get("bitrate-range"), null);
1153            if (bitRates != null) {
1154                try {
1155                    bitRates = bitRates.intersect(BITRATE_RANGE);
1156                } catch (IllegalArgumentException e) {
1157                    Log.w(TAG,  "bitrate range (" + bitRates
1158                            + ") is out of limits: " + BITRATE_RANGE);
1159                    bitRates = null;
1160                }
1161            }
1162
1163            checkPowerOfTwo(
1164                    blockSize.getWidth(), "block-size width must be power of two");
1165            checkPowerOfTwo(
1166                    blockSize.getHeight(), "block-size height must be power of two");
1167
1168            checkPowerOfTwo(
1169                    alignment.getWidth(), "alignment width must be power of two");
1170            checkPowerOfTwo(
1171                    alignment.getHeight(), "alignment height must be power of two");
1172
1173            // update block-size and alignment
1174            applyMacroBlockLimits(
1175                    Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE,
1176                    Long.MAX_VALUE, blockSize.getWidth(), blockSize.getHeight(),
1177                    alignment.getWidth(), alignment.getHeight());
1178
1179            if ((mParent.mError & ERROR_UNSUPPORTED) != 0) {
1180                // codec supports profiles that we don't know.
1181                // Use supplied values clipped to platform limits
1182                if (widths != null) {
1183                    mWidthRange = SIZE_RANGE.intersect(widths);
1184                }
1185                if (heights != null) {
1186                    mHeightRange = SIZE_RANGE.intersect(heights);
1187                }
1188                if (counts != null) {
1189                    mBlockCountRange = POSITIVE_INTEGERS.intersect(
1190                            Utils.factorRange(counts, mBlockWidth * mBlockHeight
1191                                    / blockSize.getWidth() / blockSize.getHeight()));
1192                }
1193                if (blockRates != null) {
1194                    mBlocksPerSecondRange = POSITIVE_LONGS.intersect(
1195                            Utils.factorRange(blockRates, mBlockWidth * mBlockHeight
1196                                    / blockSize.getWidth() / blockSize.getHeight()));
1197                }
1198                if (blockRatios != null) {
1199                    mBlockAspectRatioRange = POSITIVE_RATIONALS.intersect(
1200                            Utils.scaleRange(blockRatios,
1201                                    mBlockHeight / blockSize.getHeight(),
1202                                    mBlockWidth / blockSize.getWidth()));
1203                }
1204                if (ratios != null) {
1205                    mAspectRatioRange = POSITIVE_RATIONALS.intersect(ratios);
1206                }
1207                if (frameRates != null) {
1208                    mFrameRateRange = FRAME_RATE_RANGE.intersect(frameRates);
1209                }
1210                if (bitRates != null) {
1211                    mBitrateRange = BITRATE_RANGE.intersect(bitRates);
1212                }
1213            } else {
1214                // no unsupported profile/levels, so restrict values to known limits
1215                if (widths != null) {
1216                    mWidthRange = mWidthRange.intersect(widths);
1217                }
1218                if (heights != null) {
1219                    mHeightRange = mHeightRange.intersect(heights);
1220                }
1221                if (counts != null) {
1222                    mBlockCountRange = mBlockCountRange.intersect(
1223                            Utils.factorRange(counts, mBlockWidth * mBlockHeight
1224                                    / blockSize.getWidth() / blockSize.getHeight()));
1225                }
1226                if (blockRates != null) {
1227                    mBlocksPerSecondRange = mBlocksPerSecondRange.intersect(
1228                            Utils.factorRange(blockRates, mBlockWidth * mBlockHeight
1229                                    / blockSize.getWidth() / blockSize.getHeight()));
1230                }
1231                if (blockRatios != null) {
1232                    mBlockAspectRatioRange = mBlockAspectRatioRange.intersect(
1233                            Utils.scaleRange(blockRatios,
1234                                    mBlockHeight / blockSize.getHeight(),
1235                                    mBlockWidth / blockSize.getWidth()));
1236                }
1237                if (ratios != null) {
1238                    mAspectRatioRange = mAspectRatioRange.intersect(ratios);
1239                }
1240                if (frameRates != null) {
1241                    mFrameRateRange = mFrameRateRange.intersect(frameRates);
1242                }
1243                if (bitRates != null) {
1244                    mBitrateRange = mBitrateRange.intersect(bitRates);
1245                }
1246            }
1247            updateLimits();
1248        }
1249
1250        private void applyBlockLimits(
1251                int blockWidth, int blockHeight,
1252                Range<Integer> counts, Range<Long> rates, Range<Rational> ratios) {
1253            checkPowerOfTwo(blockWidth, "blockWidth must be a power of two");
1254            checkPowerOfTwo(blockHeight, "blockHeight must be a power of two");
1255
1256            final int newBlockWidth = Math.max(blockWidth, mBlockWidth);
1257            final int newBlockHeight = Math.max(blockHeight, mBlockHeight);
1258
1259            // factor will always be a power-of-2
1260            int factor =
1261                newBlockWidth * newBlockHeight / mBlockWidth / mBlockHeight;
1262            if (factor != 1) {
1263                mBlockCountRange = Utils.factorRange(mBlockCountRange, factor);
1264                mBlocksPerSecondRange = Utils.factorRange(
1265                        mBlocksPerSecondRange, factor);
1266                mBlockAspectRatioRange = Utils.scaleRange(
1267                        mBlockAspectRatioRange,
1268                        newBlockHeight / mBlockHeight,
1269                        newBlockWidth / mBlockWidth);
1270                mHorizontalBlockRange = Utils.factorRange(
1271                        mHorizontalBlockRange, newBlockWidth / mBlockWidth);
1272                mVerticalBlockRange = Utils.factorRange(
1273                        mVerticalBlockRange, newBlockHeight / mBlockHeight);
1274            }
1275            factor = newBlockWidth * newBlockHeight / blockWidth / blockHeight;
1276            if (factor != 1) {
1277                counts = Utils.factorRange(counts, factor);
1278                rates = Utils.factorRange(rates, factor);
1279                ratios = Utils.scaleRange(
1280                        ratios, newBlockHeight / blockHeight,
1281                        newBlockWidth / blockWidth);
1282            }
1283            mBlockCountRange = mBlockCountRange.intersect(counts);
1284            mBlocksPerSecondRange = mBlocksPerSecondRange.intersect(rates);
1285            mBlockAspectRatioRange = mBlockAspectRatioRange.intersect(ratios);
1286            mBlockWidth = newBlockWidth;
1287            mBlockHeight = newBlockHeight;
1288        }
1289
1290        private void applyAlignment(int widthAlignment, int heightAlignment) {
1291            checkPowerOfTwo(widthAlignment, "widthAlignment must be a power of two");
1292            checkPowerOfTwo(heightAlignment, "heightAlignment must be a power of two");
1293
1294            if (widthAlignment > mBlockWidth || heightAlignment > mBlockHeight) {
1295                // maintain assumption that 0 < alignment <= block-size
1296                applyBlockLimits(
1297                        Math.max(widthAlignment, mBlockWidth),
1298                        Math.max(heightAlignment, mBlockHeight),
1299                        POSITIVE_INTEGERS, POSITIVE_LONGS, POSITIVE_RATIONALS);
1300            }
1301
1302            mWidthAlignment = Math.max(widthAlignment, mWidthAlignment);
1303            mHeightAlignment = Math.max(heightAlignment, mHeightAlignment);
1304
1305            mWidthRange = Utils.alignRange(mWidthRange, mWidthAlignment);
1306            mHeightRange = Utils.alignRange(mHeightRange, mHeightAlignment);
1307        }
1308
1309        private void updateLimits() {
1310            // pixels -> blocks <- counts
1311            mHorizontalBlockRange = mHorizontalBlockRange.intersect(
1312                    Utils.factorRange(mWidthRange, mBlockWidth));
1313            mHorizontalBlockRange = mHorizontalBlockRange.intersect(
1314                    Range.create(
1315                            mBlockCountRange.getLower() / mVerticalBlockRange.getUpper(),
1316                            mBlockCountRange.getUpper() / mVerticalBlockRange.getLower()));
1317            mVerticalBlockRange = mVerticalBlockRange.intersect(
1318                    Utils.factorRange(mHeightRange, mBlockHeight));
1319            mVerticalBlockRange = mVerticalBlockRange.intersect(
1320                    Range.create(
1321                            mBlockCountRange.getLower() / mHorizontalBlockRange.getUpper(),
1322                            mBlockCountRange.getUpper() / mHorizontalBlockRange.getLower()));
1323            mBlockCountRange = mBlockCountRange.intersect(
1324                    Range.create(
1325                            mHorizontalBlockRange.getLower()
1326                                    * mVerticalBlockRange.getLower(),
1327                            mHorizontalBlockRange.getUpper()
1328                                    * mVerticalBlockRange.getUpper()));
1329            mBlockAspectRatioRange = mBlockAspectRatioRange.intersect(
1330                    new Rational(
1331                            mHorizontalBlockRange.getLower(), mVerticalBlockRange.getUpper()),
1332                    new Rational(
1333                            mHorizontalBlockRange.getUpper(), mVerticalBlockRange.getLower()));
1334
1335            // blocks -> pixels
1336            mWidthRange = mWidthRange.intersect(
1337                    (mHorizontalBlockRange.getLower() - 1) * mBlockWidth + mWidthAlignment,
1338                    mHorizontalBlockRange.getUpper() * mBlockWidth);
1339            mHeightRange = mHeightRange.intersect(
1340                    (mVerticalBlockRange.getLower() - 1) * mBlockHeight + mHeightAlignment,
1341                    mVerticalBlockRange.getUpper() * mBlockHeight);
1342            mAspectRatioRange = mAspectRatioRange.intersect(
1343                    new Rational(mWidthRange.getLower(), mHeightRange.getUpper()),
1344                    new Rational(mWidthRange.getUpper(), mHeightRange.getLower()));
1345
1346            mSmallerDimensionUpperLimit = Math.min(
1347                    mSmallerDimensionUpperLimit,
1348                    Math.min(mWidthRange.getUpper(), mHeightRange.getUpper()));
1349
1350            // blocks -> rate
1351            mBlocksPerSecondRange = mBlocksPerSecondRange.intersect(
1352                    mBlockCountRange.getLower() * (long)mFrameRateRange.getLower(),
1353                    mBlockCountRange.getUpper() * (long)mFrameRateRange.getUpper());
1354            mFrameRateRange = mFrameRateRange.intersect(
1355                    (int)(mBlocksPerSecondRange.getLower()
1356                            / mBlockCountRange.getUpper()),
1357                    (int)(mBlocksPerSecondRange.getUpper()
1358                            / (double)mBlockCountRange.getLower()));
1359        }
1360
1361        private void applyMacroBlockLimits(
1362                int maxHorizontalBlocks, int maxVerticalBlocks,
1363                int maxBlocks, long maxBlocksPerSecond,
1364                int blockWidth, int blockHeight,
1365                int widthAlignment, int heightAlignment) {
1366            applyAlignment(widthAlignment, heightAlignment);
1367            applyBlockLimits(
1368                    blockWidth, blockHeight, Range.create(1, maxBlocks),
1369                    Range.create(1L, maxBlocksPerSecond),
1370                    Range.create(
1371                            new Rational(1, maxVerticalBlocks),
1372                            new Rational(maxHorizontalBlocks, 1)));
1373            mHorizontalBlockRange =
1374                    mHorizontalBlockRange.intersect(
1375                            1, maxHorizontalBlocks / (mBlockWidth / blockWidth));
1376            mVerticalBlockRange =
1377                    mVerticalBlockRange.intersect(
1378                            1, maxVerticalBlocks / (mBlockHeight / blockHeight));
1379        }
1380
1381        private void applyLevelLimits() {
1382            int maxBlocksPerSecond = 0;
1383            int maxBlocks = 0;
1384            int maxBps = 0;
1385            int maxDPBBlocks = 0;
1386
1387            int errors = ERROR_NONE_SUPPORTED;
1388            CodecProfileLevel[] profileLevels = mParent.profileLevels;
1389            String mime = mParent.getMimeType();
1390
1391            if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC)) {
1392                maxBlocks = 99;
1393                maxBlocksPerSecond = 1485;
1394                maxBps = 64000;
1395                maxDPBBlocks = 396;
1396                for (CodecProfileLevel profileLevel: profileLevels) {
1397                    int MBPS = 0, FS = 0, BR = 0, DPB = 0;
1398                    boolean supported = true;
1399                    switch (profileLevel.level) {
1400                        case CodecProfileLevel.AVCLevel1:
1401                            MBPS =    1485; FS =    99; BR =     64; DPB =    396; break;
1402                        case CodecProfileLevel.AVCLevel1b:
1403                            MBPS =    1485; FS =    99; BR =    128; DPB =    396; break;
1404                        case CodecProfileLevel.AVCLevel11:
1405                            MBPS =    3000; FS =   396; BR =    192; DPB =    900; break;
1406                        case CodecProfileLevel.AVCLevel12:
1407                            MBPS =    6000; FS =   396; BR =    384; DPB =   2376; break;
1408                        case CodecProfileLevel.AVCLevel13:
1409                            MBPS =   11880; FS =   396; BR =    768; DPB =   2376; break;
1410                        case CodecProfileLevel.AVCLevel2:
1411                            MBPS =   11880; FS =   396; BR =   2000; DPB =   2376; break;
1412                        case CodecProfileLevel.AVCLevel21:
1413                            MBPS =   19800; FS =   792; BR =   4000; DPB =   4752; break;
1414                        case CodecProfileLevel.AVCLevel22:
1415                            MBPS =   20250; FS =  1620; BR =   4000; DPB =   8100; break;
1416                        case CodecProfileLevel.AVCLevel3:
1417                            MBPS =   40500; FS =  1620; BR =  10000; DPB =   8100; break;
1418                        case CodecProfileLevel.AVCLevel31:
1419                            MBPS =  108000; FS =  3600; BR =  14000; DPB =  18000; break;
1420                        case CodecProfileLevel.AVCLevel32:
1421                            MBPS =  216000; FS =  5120; BR =  20000; DPB =  20480; break;
1422                        case CodecProfileLevel.AVCLevel4:
1423                            MBPS =  245760; FS =  8192; BR =  20000; DPB =  32768; break;
1424                        case CodecProfileLevel.AVCLevel41:
1425                            MBPS =  245760; FS =  8192; BR =  50000; DPB =  32768; break;
1426                        case CodecProfileLevel.AVCLevel42:
1427                            MBPS =  522240; FS =  8704; BR =  50000; DPB =  34816; break;
1428                        case CodecProfileLevel.AVCLevel5:
1429                            MBPS =  589824; FS = 22080; BR = 135000; DPB = 110400; break;
1430                        case CodecProfileLevel.AVCLevel51:
1431                            MBPS =  983040; FS = 36864; BR = 240000; DPB = 184320; break;
1432                        case CodecProfileLevel.AVCLevel52:
1433                            MBPS = 2073600; FS = 36864; BR = 240000; DPB = 184320; break;
1434                        default:
1435                            Log.w(TAG, "Unrecognized level "
1436                                    + profileLevel.level + " for " + mime);
1437                            errors |= ERROR_UNRECOGNIZED;
1438                    }
1439                    switch (profileLevel.profile) {
1440                        case CodecProfileLevel.AVCProfileHigh:
1441                            BR *= 1250; break;
1442                        case CodecProfileLevel.AVCProfileHigh10:
1443                            BR *= 3000; break;
1444                        case CodecProfileLevel.AVCProfileExtended:
1445                        case CodecProfileLevel.AVCProfileHigh422:
1446                        case CodecProfileLevel.AVCProfileHigh444:
1447                            Log.w(TAG, "Unsupported profile "
1448                                    + profileLevel.profile + " for " + mime);
1449                            errors |= ERROR_UNSUPPORTED;
1450                            supported = false;
1451                            // fall through - treat as base profile
1452                        case CodecProfileLevel.AVCProfileBaseline:
1453                        case CodecProfileLevel.AVCProfileMain:
1454                            BR *= 1000; break;
1455                        default:
1456                            Log.w(TAG, "Unrecognized profile "
1457                                    + profileLevel.profile + " for " + mime);
1458                            errors |= ERROR_UNRECOGNIZED;
1459                            BR *= 1000;
1460                    }
1461                    if (supported) {
1462                        errors &= ~ERROR_NONE_SUPPORTED;
1463                    }
1464                    maxBlocksPerSecond = Math.max(MBPS, maxBlocksPerSecond);
1465                    maxBlocks = Math.max(FS, maxBlocks);
1466                    maxBps = Math.max(BR, maxBps);
1467                    maxDPBBlocks = Math.max(maxDPBBlocks, DPB);
1468                }
1469
1470                int maxLengthInBlocks = (int)(Math.sqrt(maxBlocks * 8));
1471                applyMacroBlockLimits(
1472                        maxLengthInBlocks, maxLengthInBlocks,
1473                        maxBlocks, maxBlocksPerSecond,
1474                        16 /* blockWidth */, 16 /* blockHeight */,
1475                        1 /* widthAlignment */, 1 /* heightAlignment */);
1476            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
1477                int maxWidth = 11, maxHeight = 9, maxRate = 15;
1478                maxBlocks = 99;
1479                maxBlocksPerSecond = 1485;
1480                maxBps = 64000;
1481                for (CodecProfileLevel profileLevel: profileLevels) {
1482                    int MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0;
1483                    boolean supported = true;
1484                    switch (profileLevel.profile) {
1485                        case CodecProfileLevel.MPEG4ProfileSimple:
1486                            switch (profileLevel.level) {
1487                                case CodecProfileLevel.MPEG4Level0:
1488                                    FR = 15; W = 11; H =  9; MBPS =  1485; FS =  99; BR =  64; break;
1489                                case CodecProfileLevel.MPEG4Level1:
1490                                    FR = 30; W = 11; H =  9; MBPS =  1485; FS =  99; BR =  64; break;
1491                                case CodecProfileLevel.MPEG4Level0b:
1492                                    FR = 30; W = 11; H =  9; MBPS =  1485; FS =  99; BR = 128; break;
1493                                case CodecProfileLevel.MPEG4Level2:
1494                                    FR = 30; W = 22; H = 18; MBPS =  5940; FS = 396; BR = 128; break;
1495                                case CodecProfileLevel.MPEG4Level3:
1496                                    FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 384; break;
1497                                case CodecProfileLevel.MPEG4Level4:
1498                                case CodecProfileLevel.MPEG4Level4a:
1499                                case CodecProfileLevel.MPEG4Level5:
1500                                    // While MPEG4 SP does not have level 4 or 5, some vendors
1501                                    // report it. Use the same limits as level 3, but mark as
1502                                    // unsupported.
1503                                    FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 384;
1504                                    supported = false;
1505                                    break;
1506                                default:
1507                                    Log.w(TAG, "Unrecognized profile/level "
1508                                            + profileLevel.profile + "/"
1509                                            + profileLevel.level + " for " + mime);
1510                                    errors |= ERROR_UNRECOGNIZED;
1511                            }
1512                            break;
1513                        case CodecProfileLevel.MPEG4ProfileAdvancedSimple:
1514                            switch (profileLevel.level) {
1515                                case CodecProfileLevel.MPEG4Level0:
1516                                case CodecProfileLevel.MPEG4Level1:
1517                                    FR = 30; W = 11; H =  9; MBPS =  2970; FS =   99; BR =  128; break;
1518                                case CodecProfileLevel.MPEG4Level2:
1519                                    FR = 30; W = 22; H = 18; MBPS =  5940; FS =  396; BR =  384; break;
1520                                case CodecProfileLevel.MPEG4Level3:
1521                                    FR = 30; W = 22; H = 18; MBPS = 11880; FS =  396; BR =  768; break;
1522                                // case CodecProfileLevel.MPEG4Level3b:
1523                                // TODO: MPEG4 level 3b is not defined in OMX
1524                                //  MBPS = 11880; FS =  396; BR = 1500; break;
1525                                case CodecProfileLevel.MPEG4Level4:
1526                                case CodecProfileLevel.MPEG4Level4a:
1527                                    // TODO: MPEG4 level 4a is not defined in spec
1528                                    FR = 30; W = 44; H = 36; MBPS = 23760; FS =  792; BR = 3000; break;
1529                                case CodecProfileLevel.MPEG4Level5:
1530                                    FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 8000; break;
1531                                default:
1532                                    Log.w(TAG, "Unrecognized profile/level "
1533                                            + profileLevel.profile + "/"
1534                                            + profileLevel.level + " for " + mime);
1535                                    errors |= ERROR_UNRECOGNIZED;
1536                            }
1537                            break;
1538                        case CodecProfileLevel.MPEG4ProfileMain:             // 2-4
1539                        case CodecProfileLevel.MPEG4ProfileNbit:             // 2
1540                        case CodecProfileLevel.MPEG4ProfileAdvancedRealTime: // 1-4
1541                        case CodecProfileLevel.MPEG4ProfileCoreScalable:     // 1-3
1542                        case CodecProfileLevel.MPEG4ProfileAdvancedCoding:   // 1-4
1543                        case CodecProfileLevel.MPEG4ProfileCore:             // 1-2
1544                        case CodecProfileLevel.MPEG4ProfileAdvancedCore:     // 1-4
1545                        case CodecProfileLevel.MPEG4ProfileSimpleScalable:   // 0-2
1546                        case CodecProfileLevel.MPEG4ProfileAdvancedScalable: // 1-3
1547                        case CodecProfileLevel.MPEG4ProfileHybrid:           // 1-2
1548                        case CodecProfileLevel.MPEG4ProfileBasicAnimated:    // 1-2
1549                        case CodecProfileLevel.MPEG4ProfileScalableTexture:  // 1
1550                        case CodecProfileLevel.MPEG4ProfileSimpleFace:       // 1-2
1551                        case CodecProfileLevel.MPEG4ProfileSimpleFBA:        // 1-2
1552                            Log.i(TAG, "Unsupported profile "
1553                                    + profileLevel.profile + " for " + mime);
1554                            errors |= ERROR_UNSUPPORTED;
1555                            supported = false;
1556                            break;
1557                        default:
1558                            Log.w(TAG, "Unrecognized profile "
1559                                    + profileLevel.profile + " for " + mime);
1560                            errors |= ERROR_UNRECOGNIZED;
1561                    }
1562                    if (supported) {
1563                        errors &= ~ERROR_NONE_SUPPORTED;
1564                    }
1565                    maxBlocksPerSecond = Math.max(MBPS, maxBlocksPerSecond);
1566                    maxBlocks = Math.max(FS, maxBlocks);
1567                    maxBps = Math.max(BR * 1000, maxBps);
1568                    maxWidth = Math.max(W, maxWidth);
1569                    maxHeight = Math.max(H, maxHeight);
1570                    maxRate = Math.max(FR, maxRate);
1571                }
1572                applyMacroBlockLimits(maxWidth, maxHeight,
1573                        maxBlocks, maxBlocksPerSecond,
1574                        16 /* blockWidth */, 16 /* blockHeight */,
1575                        1 /* widthAlignment */, 1 /* heightAlignment */);
1576                mFrameRateRange = mFrameRateRange.intersect(12, maxRate);
1577            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263)) {
1578                int maxWidth = 11, maxHeight = 9, maxRate = 15;
1579                maxBlocks = 99;
1580                maxBlocksPerSecond = 1485;
1581                maxBps = 64000;
1582                for (CodecProfileLevel profileLevel: profileLevels) {
1583                    int MBPS = 0, BR = 0, FR = 0, W = 0, H = 0;
1584                    switch (profileLevel.level) {
1585                        case CodecProfileLevel.H263Level10:
1586                            FR = 15; W = 11; H =  9; BR =   1; MBPS =  W * H * FR; break;
1587                        case CodecProfileLevel.H263Level20:
1588                            // only supports CIF, 0..QCIF
1589                            FR = 30; W = 22; H = 18; BR =   2; MBPS =  W * H * FR; break;
1590                        case CodecProfileLevel.H263Level30:
1591                            // only supports CIF, 0..QCIF
1592                            FR = 30; W = 22; H = 18; BR =   6; MBPS =  W * H * FR; break;
1593                        case CodecProfileLevel.H263Level40:
1594                            // only supports CIF, 0..QCIF
1595                            FR = 30; W = 22; H = 18; BR =  32; MBPS =  W * H * FR; break;
1596                        case CodecProfileLevel.H263Level45:
1597                            // only implies level 10 support
1598                            FR = 30; W = 11; H =  9; BR =   2; MBPS =  W * H * FR; break;
1599                        case CodecProfileLevel.H263Level50:
1600                            // only supports 50fps for H > 15
1601                            FR = 60; W = 22; H = 18; BR =  64; MBPS =  W * H * 50; break;
1602                        case CodecProfileLevel.H263Level60:
1603                            // only supports 50fps for H > 15
1604                            FR = 60; W = 45; H = 18; BR = 128; MBPS =  W * H * 50; break;
1605                        case CodecProfileLevel.H263Level70:
1606                            // only supports 50fps for H > 30
1607                            FR = 60; W = 45; H = 36; BR = 256; MBPS =  W * H * 50; break;
1608                        default:
1609                            Log.w(TAG, "Unrecognized profile/level " + profileLevel.profile
1610                                    + "/" + profileLevel.level + " for " + mime);
1611                            errors |= ERROR_UNRECOGNIZED;
1612                    }
1613                    switch (profileLevel.profile) {
1614                        case CodecProfileLevel.H263ProfileBackwardCompatible:
1615                        case CodecProfileLevel.H263ProfileBaseline:
1616                        case CodecProfileLevel.H263ProfileH320Coding:
1617                        case CodecProfileLevel.H263ProfileHighCompression:
1618                        case CodecProfileLevel.H263ProfileHighLatency:
1619                        case CodecProfileLevel.H263ProfileInterlace:
1620                        case CodecProfileLevel.H263ProfileInternet:
1621                        case CodecProfileLevel.H263ProfileISWV2:
1622                        case CodecProfileLevel.H263ProfileISWV3:
1623                            break;
1624                        default:
1625                            Log.w(TAG, "Unrecognized profile "
1626                                    + profileLevel.profile + " for " + mime);
1627                            errors |= ERROR_UNRECOGNIZED;
1628                    }
1629                    errors &= ~ERROR_NONE_SUPPORTED;
1630                    maxBlocksPerSecond = Math.max(MBPS, maxBlocksPerSecond);
1631                    maxBlocks = Math.max(W * H, maxBlocks);
1632                    maxBps = Math.max(BR * 64000, maxBps);
1633                    maxWidth = Math.max(W, maxWidth);
1634                    maxHeight = Math.max(H, maxHeight);
1635                    maxRate = Math.max(FR, maxRate);
1636                }
1637                applyMacroBlockLimits(maxWidth, maxHeight,
1638                        maxBlocks, maxBlocksPerSecond,
1639                        16 /* blockWidth */, 16 /* blockHeight */,
1640                        1 /* widthAlignment */, 1 /* heightAlignment */);
1641                mFrameRateRange = Range.create(1, maxRate);
1642            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP8) ||
1643                    mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9)) {
1644                maxBlocks = maxBlocksPerSecond = Integer.MAX_VALUE;
1645
1646                // TODO: set to 100Mbps for now, need a number for VPX
1647                maxBps = 100000000;
1648
1649                // profile levels are not indicative for VPx, but verify
1650                // them nonetheless
1651                for (CodecProfileLevel profileLevel: profileLevels) {
1652                    switch (profileLevel.level) {
1653                        case CodecProfileLevel.VP8Level_Version0:
1654                        case CodecProfileLevel.VP8Level_Version1:
1655                        case CodecProfileLevel.VP8Level_Version2:
1656                        case CodecProfileLevel.VP8Level_Version3:
1657                            break;
1658                        default:
1659                            Log.w(TAG, "Unrecognized level "
1660                                    + profileLevel.level + " for " + mime);
1661                            errors |= ERROR_UNRECOGNIZED;
1662                    }
1663                    switch (profileLevel.profile) {
1664                        case CodecProfileLevel.VP8ProfileMain:
1665                            break;
1666                        default:
1667                            Log.w(TAG, "Unrecognized profile "
1668                                    + profileLevel.profile + " for " + mime);
1669                            errors |= ERROR_UNRECOGNIZED;
1670                    }
1671                    errors &= ~ERROR_NONE_SUPPORTED;
1672                }
1673
1674                final int blockSize =
1675                    mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP8) ? 16 : 8;
1676                applyMacroBlockLimits(Short.MAX_VALUE, Short.MAX_VALUE,
1677                        maxBlocks, maxBlocksPerSecond, blockSize, blockSize,
1678                        1 /* widthAlignment */, 1 /* heightAlignment */);
1679            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
1680                maxBlocks = 36864;
1681                maxBlocksPerSecond = maxBlocks * 15;
1682                maxBps = 128000;
1683                for (CodecProfileLevel profileLevel: profileLevels) {
1684                    double FR = 0;
1685                    int FS = 0;
1686                    int BR = 0;
1687                    switch (profileLevel.level) {
1688                        case CodecProfileLevel.HEVCMainTierLevel1:
1689                        case CodecProfileLevel.HEVCHighTierLevel1:
1690                            FR =    15; FS =    36864; BR =    128; break;
1691                        case CodecProfileLevel.HEVCMainTierLevel2:
1692                        case CodecProfileLevel.HEVCHighTierLevel2:
1693                            FR =    30; FS =   122880; BR =   1500; break;
1694                        case CodecProfileLevel.HEVCMainTierLevel21:
1695                        case CodecProfileLevel.HEVCHighTierLevel21:
1696                            FR =    30; FS =   245760; BR =   3000; break;
1697                        case CodecProfileLevel.HEVCMainTierLevel3:
1698                        case CodecProfileLevel.HEVCHighTierLevel3:
1699                            FR =    30; FS =   552960; BR =   6000; break;
1700                        case CodecProfileLevel.HEVCMainTierLevel31:
1701                        case CodecProfileLevel.HEVCHighTierLevel31:
1702                            FR = 33.75; FS =   983040; BR =  10000; break;
1703                        case CodecProfileLevel.HEVCMainTierLevel4:
1704                            FR =    30; FS =  2228224; BR =  12000; break;
1705                        case CodecProfileLevel.HEVCHighTierLevel4:
1706                            FR =    30; FS =  2228224; BR =  30000; break;
1707                        case CodecProfileLevel.HEVCMainTierLevel41:
1708                            FR =    60; FS =  2228224; BR =  20000; break;
1709                        case CodecProfileLevel.HEVCHighTierLevel41:
1710                            FR =    60; FS =  2228224; BR =  50000; break;
1711                        case CodecProfileLevel.HEVCMainTierLevel5:
1712                            FR =    30; FS =  8912896; BR =  25000; break;
1713                        case CodecProfileLevel.HEVCHighTierLevel5:
1714                            FR =    30; FS =  8912896; BR = 100000; break;
1715                        case CodecProfileLevel.HEVCMainTierLevel51:
1716                            FR =    60; FS =  8912896; BR =  40000; break;
1717                        case CodecProfileLevel.HEVCHighTierLevel51:
1718                            FR =    60; FS =  8912896; BR = 160000; break;
1719                        case CodecProfileLevel.HEVCMainTierLevel52:
1720                            FR =   120; FS =  8912896; BR =  60000; break;
1721                        case CodecProfileLevel.HEVCHighTierLevel52:
1722                            FR =   120; FS =  8912896; BR = 240000; break;
1723                        case CodecProfileLevel.HEVCMainTierLevel6:
1724                            FR =    30; FS = 35651584; BR =  60000; break;
1725                        case CodecProfileLevel.HEVCHighTierLevel6:
1726                            FR =    30; FS = 35651584; BR = 240000; break;
1727                        case CodecProfileLevel.HEVCMainTierLevel61:
1728                            FR =    60; FS = 35651584; BR = 120000; break;
1729                        case CodecProfileLevel.HEVCHighTierLevel61:
1730                            FR =    60; FS = 35651584; BR = 480000; break;
1731                        case CodecProfileLevel.HEVCMainTierLevel62:
1732                            FR =   120; FS = 35651584; BR = 240000; break;
1733                        case CodecProfileLevel.HEVCHighTierLevel62:
1734                            FR =   120; FS = 35651584; BR = 800000; break;
1735                        default:
1736                            Log.w(TAG, "Unrecognized level "
1737                                    + profileLevel.level + " for " + mime);
1738                            errors |= ERROR_UNRECOGNIZED;
1739                    }
1740                    switch (profileLevel.profile) {
1741                        case CodecProfileLevel.HEVCProfileMain:
1742                        case CodecProfileLevel.HEVCProfileMain10:
1743                            break;
1744                        default:
1745                            Log.w(TAG, "Unrecognized profile "
1746                                    + profileLevel.profile + " for " + mime);
1747                            errors |= ERROR_UNRECOGNIZED;
1748                    }
1749
1750                    /* DPB logic:
1751                    if      (width * height <= FS / 4)    DPB = 16;
1752                    else if (width * height <= FS / 2)    DPB = 12;
1753                    else if (width * height <= FS * 0.75) DPB = 8;
1754                    else                                  DPB = 6;
1755                    */
1756
1757                    errors &= ~ERROR_NONE_SUPPORTED;
1758                    maxBlocksPerSecond = Math.max((int)(FR * FS), maxBlocksPerSecond);
1759                    maxBlocks = Math.max(FS, maxBlocks);
1760                    maxBps = Math.max(BR * 1000, maxBps);
1761                }
1762
1763                int maxLengthInBlocks = (int)(Math.sqrt(maxBlocks * 8));
1764                // CTBs are at least 8x8
1765                maxBlocks = Utils.divUp(maxBlocks, 8 * 8);
1766                maxBlocksPerSecond = Utils.divUp(maxBlocksPerSecond, 8 * 8);
1767                maxLengthInBlocks = Utils.divUp(maxLengthInBlocks, 8);
1768
1769                applyMacroBlockLimits(
1770                        maxLengthInBlocks, maxLengthInBlocks,
1771                        maxBlocks, maxBlocksPerSecond,
1772                        8 /* blockWidth */, 8 /* blockHeight */,
1773                        1 /* widthAlignment */, 1 /* heightAlignment */);
1774            } else {
1775                Log.w(TAG, "Unsupported mime " + mime);
1776                // using minimal bitrate here.  should be overriden by
1777                // info from media_codecs.xml
1778                maxBps = 64000;
1779                errors |= ERROR_UNSUPPORTED;
1780            }
1781            mBitrateRange = Range.create(1, maxBps);
1782            mParent.mError |= errors;
1783        }
1784    }
1785
1786    /**
1787     * A class that supports querying the encoding capabilities of a codec.
1788     */
1789    public static final class EncoderCapabilities {
1790        /**
1791         * Returns the supported range of quality values.
1792         *
1793         * @hide
1794         */
1795        public Range<Integer> getQualityRange() {
1796            return mQualityRange;
1797        }
1798
1799        /**
1800         * Returns the supported range of encoder complexity values.
1801         * <p>
1802         * Some codecs may support multiple complexity levels, where higher
1803         * complexity values use more encoder tools (e.g. perform more
1804         * intensive calculations) to improve the quality or the compression
1805         * ratio.  Use a lower value to save power and/or time.
1806         */
1807        public Range<Integer> getComplexityRange() {
1808            return mComplexityRange;
1809        }
1810
1811        /** Constant quality mode */
1812        public static final int BITRATE_MODE_CQ = 0;
1813        /** Variable bitrate mode */
1814        public static final int BITRATE_MODE_VBR = 1;
1815        /** Constant bitrate mode */
1816        public static final int BITRATE_MODE_CBR = 2;
1817
1818        private static final Feature[] bitrates = new Feature[] {
1819            new Feature("VBR", BITRATE_MODE_VBR, true),
1820            new Feature("CBR", BITRATE_MODE_CBR, false),
1821            new Feature("CQ",  BITRATE_MODE_CQ,  false)
1822        };
1823
1824        private static int parseBitrateMode(String mode) {
1825            for (Feature feat: bitrates) {
1826                if (feat.mName.equalsIgnoreCase(mode)) {
1827                    return feat.mValue;
1828                }
1829            }
1830            return 0;
1831        }
1832
1833        /**
1834         * Query whether a bitrate mode is supported.
1835         */
1836        public boolean isBitrateModeSupported(int mode) {
1837            for (Feature feat: bitrates) {
1838                if (mode == feat.mValue) {
1839                    return (mBitControl & (1 << mode)) != 0;
1840                }
1841            }
1842            return false;
1843        }
1844
1845        private Range<Integer> mQualityRange;
1846        private Range<Integer> mComplexityRange;
1847        private CodecCapabilities mParent;
1848
1849        /* no public constructor */
1850        private EncoderCapabilities() { }
1851
1852        /** @hide */
1853        public static EncoderCapabilities create(
1854                MediaFormat info, CodecCapabilities parent) {
1855            EncoderCapabilities caps = new EncoderCapabilities();
1856            caps.init(info, parent);
1857            return caps;
1858        }
1859
1860        /** @hide */
1861        public void init(MediaFormat info, CodecCapabilities parent) {
1862            // no support for complexity or quality yet
1863            mParent = parent;
1864            mComplexityRange = Range.create(0, 0);
1865            mQualityRange = Range.create(0, 0);
1866            mBitControl = (1 << BITRATE_MODE_VBR);
1867
1868            applyLevelLimits();
1869            parseFromInfo(info);
1870        }
1871
1872        private void applyLevelLimits() {
1873            String mime = mParent.getMimeType();
1874            if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
1875                mComplexityRange = Range.create(0, 8);
1876                mBitControl = (1 << BITRATE_MODE_CQ);
1877            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_NB)
1878                    || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_WB)
1879                    || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_ALAW)
1880                    || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_MLAW)
1881                    || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MSGSM)) {
1882                mBitControl = (1 << BITRATE_MODE_CBR);
1883            }
1884        }
1885
1886        private int mBitControl;
1887        private Integer mDefaultComplexity;
1888        private Integer mDefaultQuality;
1889        private String mQualityScale;
1890
1891        private void parseFromInfo(MediaFormat info) {
1892            Map<String, Object> map = info.getMap();
1893
1894            if (info.containsKey("complexity-range")) {
1895                mComplexityRange = Utils
1896                        .parseIntRange(info.getString("complexity-range"), mComplexityRange);
1897                // TODO should we limit this to level limits?
1898            }
1899            if (info.containsKey("quality-range")) {
1900                mQualityRange = Utils
1901                        .parseIntRange(info.getString("quality-range"), mQualityRange);
1902            }
1903            if (info.containsKey("feature-bitrate-control")) {
1904                for (String mode: info.getString("feature-bitrate-control").split(",")) {
1905                    mBitControl |= parseBitrateMode(mode);
1906                }
1907            }
1908
1909            try {
1910                mDefaultComplexity = Integer.parseInt((String)map.get("complexity-default"));
1911            } catch (NumberFormatException e) { }
1912
1913            try {
1914                mDefaultQuality = Integer.parseInt((String)map.get("quality-default"));
1915            } catch (NumberFormatException e) { }
1916
1917            mQualityScale = (String)map.get("quality-scale");
1918        }
1919
1920        private boolean supports(
1921                Integer complexity, Integer quality, Integer profile) {
1922            boolean ok = true;
1923            if (ok && complexity != null) {
1924                ok = mComplexityRange.contains(complexity);
1925            }
1926            if (ok && quality != null) {
1927                ok = mQualityRange.contains(quality);
1928            }
1929            if (ok && profile != null) {
1930                for (CodecProfileLevel pl: mParent.profileLevels) {
1931                    if (pl.profile == profile) {
1932                        profile = null;
1933                        break;
1934                    }
1935                }
1936                ok = profile == null;
1937            }
1938            return ok;
1939        }
1940
1941        /** @hide */
1942        public void setDefaultFormat(MediaFormat format) {
1943            // don't list trivial quality/complexity as default for now
1944            if (!mQualityRange.getUpper().equals(mQualityRange.getLower())
1945                    && mDefaultQuality != null) {
1946                format.setInteger(MediaFormat.KEY_QUALITY, mDefaultQuality);
1947            }
1948            if (!mComplexityRange.getUpper().equals(mComplexityRange.getLower())
1949                    && mDefaultComplexity != null) {
1950                format.setInteger(MediaFormat.KEY_COMPLEXITY, mDefaultComplexity);
1951            }
1952            // bitrates are listed in order of preference
1953            for (Feature feat: bitrates) {
1954                if ((mBitControl & (1 << feat.mValue)) != 0) {
1955                    format.setInteger(MediaFormat.KEY_BITRATE_MODE, feat.mValue);
1956                    break;
1957                }
1958            }
1959        }
1960
1961        /** @hide */
1962        public boolean supportsFormat(MediaFormat format) {
1963            final Map<String, Object> map = format.getMap();
1964            final String mime = mParent.getMimeType();
1965
1966            Integer mode = (Integer)map.get(MediaFormat.KEY_BITRATE_MODE);
1967            if (mode != null && !isBitrateModeSupported(mode)) {
1968                return false;
1969            }
1970
1971            Integer complexity = (Integer)map.get(MediaFormat.KEY_COMPLEXITY);
1972            if (MediaFormat.MIMETYPE_AUDIO_FLAC.equalsIgnoreCase(mime)) {
1973                Integer flacComplexity =
1974                    (Integer)map.get(MediaFormat.KEY_FLAC_COMPRESSION_LEVEL);
1975                if (complexity == null) {
1976                    complexity = flacComplexity;
1977                } else if (flacComplexity != null && complexity != flacComplexity) {
1978                    throw new IllegalArgumentException(
1979                            "conflicting values for complexity and " +
1980                            "flac-compression-level");
1981                }
1982            }
1983
1984            // other audio parameters
1985            Integer profile = (Integer)map.get(MediaFormat.KEY_PROFILE);
1986            if (MediaFormat.MIMETYPE_AUDIO_AAC.equalsIgnoreCase(mime)) {
1987                Integer aacProfile = (Integer)map.get(MediaFormat.KEY_AAC_PROFILE);
1988                if (profile == null) {
1989                    profile = aacProfile;
1990                } else if (aacProfile != null && aacProfile != profile) {
1991                    throw new IllegalArgumentException(
1992                            "conflicting values for profile and aac-profile");
1993                }
1994            }
1995
1996            Integer quality = (Integer)map.get(MediaFormat.KEY_QUALITY);
1997
1998            return supports(complexity, quality, profile);
1999        }
2000    };
2001
2002    /**
2003     * Encapsulates the profiles available for a codec component.
2004     * <p>You can get a set of {@link MediaCodecInfo.CodecProfileLevel} objects for a given
2005     * {@link MediaCodecInfo} object from the
2006     * {@link MediaCodecInfo.CodecCapabilities#profileLevels} field.
2007     */
2008    public static final class CodecProfileLevel {
2009        // from OMX_VIDEO_AVCPROFILETYPE
2010        public static final int AVCProfileBaseline = 0x01;
2011        public static final int AVCProfileMain     = 0x02;
2012        public static final int AVCProfileExtended = 0x04;
2013        public static final int AVCProfileHigh     = 0x08;
2014        public static final int AVCProfileHigh10   = 0x10;
2015        public static final int AVCProfileHigh422  = 0x20;
2016        public static final int AVCProfileHigh444  = 0x40;
2017
2018        // from OMX_VIDEO_AVCLEVELTYPE
2019        public static final int AVCLevel1       = 0x01;
2020        public static final int AVCLevel1b      = 0x02;
2021        public static final int AVCLevel11      = 0x04;
2022        public static final int AVCLevel12      = 0x08;
2023        public static final int AVCLevel13      = 0x10;
2024        public static final int AVCLevel2       = 0x20;
2025        public static final int AVCLevel21      = 0x40;
2026        public static final int AVCLevel22      = 0x80;
2027        public static final int AVCLevel3       = 0x100;
2028        public static final int AVCLevel31      = 0x200;
2029        public static final int AVCLevel32      = 0x400;
2030        public static final int AVCLevel4       = 0x800;
2031        public static final int AVCLevel41      = 0x1000;
2032        public static final int AVCLevel42      = 0x2000;
2033        public static final int AVCLevel5       = 0x4000;
2034        public static final int AVCLevel51      = 0x8000;
2035        public static final int AVCLevel52      = 0x10000;
2036
2037        // from OMX_VIDEO_H263PROFILETYPE
2038        public static final int H263ProfileBaseline             = 0x01;
2039        public static final int H263ProfileH320Coding           = 0x02;
2040        public static final int H263ProfileBackwardCompatible   = 0x04;
2041        public static final int H263ProfileISWV2                = 0x08;
2042        public static final int H263ProfileISWV3                = 0x10;
2043        public static final int H263ProfileHighCompression      = 0x20;
2044        public static final int H263ProfileInternet             = 0x40;
2045        public static final int H263ProfileInterlace            = 0x80;
2046        public static final int H263ProfileHighLatency          = 0x100;
2047
2048        // from OMX_VIDEO_H263LEVELTYPE
2049        public static final int H263Level10      = 0x01;
2050        public static final int H263Level20      = 0x02;
2051        public static final int H263Level30      = 0x04;
2052        public static final int H263Level40      = 0x08;
2053        public static final int H263Level45      = 0x10;
2054        public static final int H263Level50      = 0x20;
2055        public static final int H263Level60      = 0x40;
2056        public static final int H263Level70      = 0x80;
2057
2058        // from OMX_VIDEO_MPEG4PROFILETYPE
2059        public static final int MPEG4ProfileSimple              = 0x01;
2060        public static final int MPEG4ProfileSimpleScalable      = 0x02;
2061        public static final int MPEG4ProfileCore                = 0x04;
2062        public static final int MPEG4ProfileMain                = 0x08;
2063        public static final int MPEG4ProfileNbit                = 0x10;
2064        public static final int MPEG4ProfileScalableTexture     = 0x20;
2065        public static final int MPEG4ProfileSimpleFace          = 0x40;
2066        public static final int MPEG4ProfileSimpleFBA           = 0x80;
2067        public static final int MPEG4ProfileBasicAnimated       = 0x100;
2068        public static final int MPEG4ProfileHybrid              = 0x200;
2069        public static final int MPEG4ProfileAdvancedRealTime    = 0x400;
2070        public static final int MPEG4ProfileCoreScalable        = 0x800;
2071        public static final int MPEG4ProfileAdvancedCoding      = 0x1000;
2072        public static final int MPEG4ProfileAdvancedCore        = 0x2000;
2073        public static final int MPEG4ProfileAdvancedScalable    = 0x4000;
2074        public static final int MPEG4ProfileAdvancedSimple      = 0x8000;
2075
2076        // from OMX_VIDEO_MPEG4LEVELTYPE
2077        public static final int MPEG4Level0      = 0x01;
2078        public static final int MPEG4Level0b     = 0x02;
2079        public static final int MPEG4Level1      = 0x04;
2080        public static final int MPEG4Level2      = 0x08;
2081        public static final int MPEG4Level3      = 0x10;
2082        public static final int MPEG4Level4      = 0x20;
2083        public static final int MPEG4Level4a     = 0x40;
2084        public static final int MPEG4Level5      = 0x80;
2085
2086        // from OMX_AUDIO_AACPROFILETYPE
2087        public static final int AACObjectMain       = 1;
2088        public static final int AACObjectLC         = 2;
2089        public static final int AACObjectSSR        = 3;
2090        public static final int AACObjectLTP        = 4;
2091        public static final int AACObjectHE         = 5;
2092        public static final int AACObjectScalable   = 6;
2093        public static final int AACObjectERLC       = 17;
2094        public static final int AACObjectLD         = 23;
2095        public static final int AACObjectHE_PS      = 29;
2096        public static final int AACObjectELD        = 39;
2097
2098        // from OMX_VIDEO_VP8LEVELTYPE
2099        public static final int VP8Level_Version0 = 0x01;
2100        public static final int VP8Level_Version1 = 0x02;
2101        public static final int VP8Level_Version2 = 0x04;
2102        public static final int VP8Level_Version3 = 0x08;
2103
2104        // from OMX_VIDEO_VP8PROFILETYPE
2105        public static final int VP8ProfileMain = 0x01;
2106
2107        // from OMX_VIDEO_HEVCPROFILETYPE
2108        public static final int HEVCProfileMain   = 0x01;
2109        public static final int HEVCProfileMain10 = 0x02;
2110
2111        // from OMX_VIDEO_HEVCLEVELTYPE
2112        public static final int HEVCMainTierLevel1  = 0x1;
2113        public static final int HEVCHighTierLevel1  = 0x2;
2114        public static final int HEVCMainTierLevel2  = 0x4;
2115        public static final int HEVCHighTierLevel2  = 0x8;
2116        public static final int HEVCMainTierLevel21 = 0x10;
2117        public static final int HEVCHighTierLevel21 = 0x20;
2118        public static final int HEVCMainTierLevel3  = 0x40;
2119        public static final int HEVCHighTierLevel3  = 0x80;
2120        public static final int HEVCMainTierLevel31 = 0x100;
2121        public static final int HEVCHighTierLevel31 = 0x200;
2122        public static final int HEVCMainTierLevel4  = 0x400;
2123        public static final int HEVCHighTierLevel4  = 0x800;
2124        public static final int HEVCMainTierLevel41 = 0x1000;
2125        public static final int HEVCHighTierLevel41 = 0x2000;
2126        public static final int HEVCMainTierLevel5  = 0x4000;
2127        public static final int HEVCHighTierLevel5  = 0x8000;
2128        public static final int HEVCMainTierLevel51 = 0x10000;
2129        public static final int HEVCHighTierLevel51 = 0x20000;
2130        public static final int HEVCMainTierLevel52 = 0x40000;
2131        public static final int HEVCHighTierLevel52 = 0x80000;
2132        public static final int HEVCMainTierLevel6  = 0x100000;
2133        public static final int HEVCHighTierLevel6  = 0x200000;
2134        public static final int HEVCMainTierLevel61 = 0x400000;
2135        public static final int HEVCHighTierLevel61 = 0x800000;
2136        public static final int HEVCMainTierLevel62 = 0x1000000;
2137        public static final int HEVCHighTierLevel62 = 0x2000000;
2138
2139        /**
2140         * Defined in the OpenMAX IL specs, depending on the type of media
2141         * this can be OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263PROFILETYPE,
2142         * OMX_VIDEO_MPEG4PROFILETYPE or OMX_VIDEO_VP8PROFILETYPE.
2143         */
2144        public int profile;
2145
2146        /**
2147         * Defined in the OpenMAX IL specs, depending on the type of media
2148         * this can be OMX_VIDEO_AVCLEVELTYPE, OMX_VIDEO_H263LEVELTYPE
2149         * OMX_VIDEO_MPEG4LEVELTYPE or OMX_VIDEO_VP8LEVELTYPE.
2150         */
2151        public int level;
2152    };
2153
2154    /**
2155     * Enumerates the capabilities of the codec component. Since a single
2156     * component can support data of a variety of types, the type has to be
2157     * specified to yield a meaningful result.
2158     * @param type The MIME type to query
2159     */
2160    public final CodecCapabilities getCapabilitiesForType(
2161            String type) {
2162        CodecCapabilities caps = mCaps.get(type);
2163        if (caps == null) {
2164            throw new IllegalArgumentException("codec does not support type");
2165        }
2166        // clone writable object
2167        return caps.dup();
2168    }
2169
2170    /** @hide */
2171    public MediaCodecInfo makeRegular() {
2172        ArrayList<CodecCapabilities> caps = new ArrayList<CodecCapabilities>();
2173        for (CodecCapabilities c: mCaps.values()) {
2174            if (c.isRegular()) {
2175                caps.add(c);
2176            }
2177        }
2178        if (caps.size() == 0) {
2179            return null;
2180        } else if (caps.size() == mCaps.size()) {
2181            return this;
2182        }
2183
2184        return new MediaCodecInfo(
2185                mName, mIsEncoder,
2186                caps.toArray(new CodecCapabilities[caps.size()]));
2187    }
2188}
2189