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 
17 package android.media;
18 
19 import android.util.Log;
20 
21 import android.media.MediaCodecInfo;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Map;
25 
26 /**
27  * Allows you to enumerate available codecs, each specified as a {@link MediaCodecInfo} object,
28  * find a codec supporting a given format and query the capabilities
29  * of a given codec.
30  * <p>See {@link MediaCodecInfo} for sample usage.
31  */
32 final public class MediaCodecList {
33     private static final String TAG = "MediaCodecList";
34 
35     /**
36      * Count the number of available (regular) codecs.
37      *
38      * @deprecated Use {@link #getCodecInfos} instead.
39      *
40      * @see #REGULAR_CODECS
41      */
getCodecCount()42     public static final int getCodecCount() {
43         initCodecList();
44         return sRegularCodecInfos.length;
45     }
46 
native_getCodecCount()47     private static native final int native_getCodecCount();
48 
49     /**
50      * Return the {@link MediaCodecInfo} object for the codec at
51      * the given {@code index} in the regular list.
52      *
53      * @deprecated Use {@link #getCodecInfos} instead.
54      *
55      * @see #REGULAR_CODECS
56      */
getCodecInfoAt(int index)57     public static final MediaCodecInfo getCodecInfoAt(int index) {
58         initCodecList();
59         if (index < 0 || index > sRegularCodecInfos.length) {
60             throw new IllegalArgumentException();
61         }
62         return sRegularCodecInfos[index];
63     }
64 
getGlobalSettings()65     /* package private */ static final Map<String, Object> getGlobalSettings() {
66         synchronized (sInitLock) {
67             if (sGlobalSettings == null) {
68                 sGlobalSettings = native_getGlobalSettings();
69             }
70         }
71         return sGlobalSettings;
72     }
73 
74     private static Object sInitLock = new Object();
75     private static MediaCodecInfo[] sAllCodecInfos;
76     private static MediaCodecInfo[] sRegularCodecInfos;
77     private static Map<String, Object> sGlobalSettings;
78 
initCodecList()79     private static final void initCodecList() {
80         synchronized (sInitLock) {
81             if (sRegularCodecInfos == null) {
82                 int count = native_getCodecCount();
83                 ArrayList<MediaCodecInfo> regulars = new ArrayList<MediaCodecInfo>();
84                 ArrayList<MediaCodecInfo> all = new ArrayList<MediaCodecInfo>();
85                 for (int index = 0; index < count; index++) {
86                     try {
87                         MediaCodecInfo info = getNewCodecInfoAt(index);
88                         all.add(info);
89                         info = info.makeRegular();
90                         if (info != null) {
91                             regulars.add(info);
92                         }
93                     } catch (Exception e) {
94                         Log.e(TAG, "Could not get codec capabilities", e);
95                     }
96                 }
97                 sRegularCodecInfos =
98                     regulars.toArray(new MediaCodecInfo[regulars.size()]);
99                 sAllCodecInfos =
100                     all.toArray(new MediaCodecInfo[all.size()]);
101             }
102         }
103     }
104 
getNewCodecInfoAt(int index)105     private static MediaCodecInfo getNewCodecInfoAt(int index) {
106         String[] supportedTypes = getSupportedTypes(index);
107         MediaCodecInfo.CodecCapabilities[] caps =
108             new MediaCodecInfo.CodecCapabilities[supportedTypes.length];
109         int typeIx = 0;
110         for (String type: supportedTypes) {
111             caps[typeIx++] = getCodecCapabilities(index, type);
112         }
113         return new MediaCodecInfo(
114                 getCodecName(index), isEncoder(index), caps);
115     }
116 
getCodecName(int index)117     /* package private */ static native final String getCodecName(int index);
118 
isEncoder(int index)119     /* package private */ static native final boolean isEncoder(int index);
120 
getSupportedTypes(int index)121     /* package private */ static native final String[] getSupportedTypes(int index);
122 
123     /* package private */ static native final MediaCodecInfo.CodecCapabilities
getCodecCapabilities(int index, String type)124         getCodecCapabilities(int index, String type);
125 
native_getGlobalSettings()126     /* package private */ static native final Map<String, Object> native_getGlobalSettings();
127 
findCodecByName(String codec)128     /* package private */ static native final int findCodecByName(String codec);
129 
130     /** @hide */
getInfoFor(String codec)131     public static MediaCodecInfo getInfoFor(String codec) {
132         initCodecList();
133         return sAllCodecInfos[findCodecByName(codec)];
134     }
135 
native_init()136     private static native final void native_init();
137 
138     /**
139      * Use in {@link #MediaCodecList} to enumerate only codecs that are suitable
140      * for regular (buffer-to-buffer) decoding or encoding.
141      *
142      * <em>NOTE:</em> These are the codecs that are returned prior to API 21,
143      * using the now deprecated static methods.
144      */
145     public static final int REGULAR_CODECS = 0;
146 
147     /**
148      * Use in {@link #MediaCodecList} to enumerate all codecs, even ones that are
149      * not suitable for regular (buffer-to-buffer) decoding or encoding.  These
150      * include codecs, for example, that only work with special input or output
151      * surfaces, such as secure-only or tunneled-only codecs.
152      *
153      * @see MediaCodecInfo.CodecCapabilities#isFormatSupported
154      * @see MediaCodecInfo.CodecCapabilities#FEATURE_SecurePlayback
155      * @see MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback
156      */
157     public static final int ALL_CODECS = 1;
158 
MediaCodecList()159     private MediaCodecList() {
160         this(REGULAR_CODECS);
161     }
162 
163     private MediaCodecInfo[] mCodecInfos;
164 
165     /**
166      * Create a list of media-codecs of a specific kind.
167      * @param kind Either {@code REGULAR_CODECS} or {@code ALL_CODECS}.
168      */
MediaCodecList(int kind)169     public MediaCodecList(int kind) {
170         initCodecList();
171         if (kind == REGULAR_CODECS) {
172             mCodecInfos = sRegularCodecInfos;
173         } else {
174             mCodecInfos = sAllCodecInfos;
175         }
176     }
177 
178     /**
179      * Returns the list of {@link MediaCodecInfo} objects for the list
180      * of media-codecs.
181      */
getCodecInfos()182     public final MediaCodecInfo[] getCodecInfos() {
183         return Arrays.copyOf(mCodecInfos, mCodecInfos.length);
184     }
185 
186     static {
187         System.loadLibrary("media_jni");
native_init()188         native_init();
189 
190         // mediaserver is not yet alive here
191     }
192 
193     /**
194      * Find a decoder supporting a given {@link MediaFormat} in the list
195      * of media-codecs.
196      *
197      * <p class=note>
198      * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP},
199      * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE
200      * frame rate}. Use
201      * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code>
202      * to clear any existing frame rate setting in the format.
203      *
204      * @see MediaCodecList.CodecCapabilities.isFormatSupported for format keys
205      * considered per android versions when evaluating suitable codecs.
206      *
207      * @param format A decoder media format with optional feature directives.
208      * @throws IllegalArgumentException if format is not a valid media format.
209      * @throws NullPointerException if format is null.
210      * @return the name of a decoder that supports the given format and feature
211      *         requests, or {@code null} if no such codec has been found.
212      */
findDecoderForFormat(MediaFormat format)213     public final String findDecoderForFormat(MediaFormat format) {
214         return findCodecForFormat(false /* encoder */, format);
215     }
216 
217     /**
218      * Find an encoder supporting a given {@link MediaFormat} in the list
219      * of media-codecs.
220      *
221      * <p class=note>
222      * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP},
223      * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE
224      * frame rate}. Use
225      * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code>
226      * to clear any existing frame rate setting in the format.
227      *
228      * @see MediaCodecList.CodecCapabilities.isFormatSupported for format keys
229      * considered per android versions when evaluating suitable codecs.
230      *
231      * @param format An encoder media format with optional feature directives.
232      * @throws IllegalArgumentException if format is not a valid media format.
233      * @throws NullPointerException if format is null.
234      * @return the name of an encoder that supports the given format and feature
235      *         requests, or {@code null} if no such codec has been found.
236      */
findEncoderForFormat(MediaFormat format)237     public final String findEncoderForFormat(MediaFormat format) {
238         return findCodecForFormat(true /* encoder */, format);
239     }
240 
findCodecForFormat(boolean encoder, MediaFormat format)241     private String findCodecForFormat(boolean encoder, MediaFormat format) {
242         String mime = format.getString(MediaFormat.KEY_MIME);
243         for (MediaCodecInfo info: mCodecInfos) {
244             if (info.isEncoder() != encoder) {
245                 continue;
246             }
247             try {
248                 MediaCodecInfo.CodecCapabilities caps = info.getCapabilitiesForType(mime);
249                 if (caps != null && caps.isFormatSupported(format)) {
250                     return info.getName();
251                 }
252             } catch (IllegalArgumentException e) {
253                 // type is not supported
254             }
255         }
256         return null;
257     }
258 }
259