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), getCanonicalName(index), getAttributes(index), caps);
115     }
116 
getCodecName(int index)117     /* package private */ static native final String getCodecName(int index);
118 
getCanonicalName(int index)119     /* package private */ static native final String getCanonicalName(int index);
120 
getAttributes(int index)121     /* package private */ static native final int getAttributes(int index);
122 
getSupportedTypes(int index)123     /* package private */ static native final String[] getSupportedTypes(int index);
124 
125     /* package private */ static native final MediaCodecInfo.CodecCapabilities
getCodecCapabilities(int index, String type)126         getCodecCapabilities(int index, String type);
127 
native_getGlobalSettings()128     /* package private */ static native final Map<String, Object> native_getGlobalSettings();
129 
findCodecByName(String codec)130     /* package private */ static native final int findCodecByName(String codec);
131 
132     /** @hide */
getInfoFor(String codec)133     public static MediaCodecInfo getInfoFor(String codec) {
134         initCodecList();
135         return sAllCodecInfos[findCodecByName(codec)];
136     }
137 
native_init()138     private static native final void native_init();
139 
140     /**
141      * Use in {@link #MediaCodecList} to enumerate only codecs that are suitable
142      * for regular (buffer-to-buffer) decoding or encoding.
143      *
144      * <em>NOTE:</em> These are the codecs that are returned prior to API 21,
145      * using the now deprecated static methods.
146      */
147     public static final int REGULAR_CODECS = 0;
148 
149     /**
150      * Use in {@link #MediaCodecList} to enumerate all codecs, even ones that are
151      * not suitable for regular (buffer-to-buffer) decoding or encoding.  These
152      * include codecs, for example, that only work with special input or output
153      * surfaces, such as secure-only or tunneled-only codecs.
154      *
155      * @see MediaCodecInfo.CodecCapabilities#isFormatSupported
156      * @see MediaCodecInfo.CodecCapabilities#FEATURE_SecurePlayback
157      * @see MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback
158      */
159     public static final int ALL_CODECS = 1;
160 
MediaCodecList()161     private MediaCodecList() {
162         this(REGULAR_CODECS);
163     }
164 
165     private MediaCodecInfo[] mCodecInfos;
166 
167     /**
168      * Create a list of media-codecs of a specific kind.
169      * @param kind Either {@code REGULAR_CODECS} or {@code ALL_CODECS}.
170      */
MediaCodecList(int kind)171     public MediaCodecList(int kind) {
172         initCodecList();
173         if (kind == REGULAR_CODECS) {
174             mCodecInfos = sRegularCodecInfos;
175         } else {
176             mCodecInfos = sAllCodecInfos;
177         }
178     }
179 
180     /**
181      * Returns the list of {@link MediaCodecInfo} objects for the list
182      * of media-codecs.
183      */
getCodecInfos()184     public final MediaCodecInfo[] getCodecInfos() {
185         return Arrays.copyOf(mCodecInfos, mCodecInfos.length);
186     }
187 
188     static {
189         System.loadLibrary("media_jni");
native_init()190         native_init();
191 
192         // mediaserver is not yet alive here
193     }
194 
195     /**
196      * Find a decoder supporting a given {@link MediaFormat} in the list
197      * of media-codecs.
198      *
199      * <p class=note>
200      * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP},
201      * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE
202      * frame rate}. Use
203      * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code>
204      * to clear any existing frame rate setting in the format.
205      *
206      * @see MediaCodecInfo.CodecCapabilities#isFormatSupported(MediaFormat) for format keys
207      * considered per android versions when evaluating suitable codecs.
208      *
209      * @param format A decoder media format with optional feature directives.
210      * @throws IllegalArgumentException if format is not a valid media format.
211      * @throws NullPointerException if format is null.
212      * @return the name of a decoder that supports the given format and feature
213      *         requests, or {@code null} if no such codec has been found.
214      */
findDecoderForFormat(MediaFormat format)215     public final String findDecoderForFormat(MediaFormat format) {
216         return findCodecForFormat(false /* encoder */, format);
217     }
218 
219     /**
220      * Find an encoder supporting a given {@link MediaFormat} in the list
221      * of media-codecs.
222      *
223      * <p class=note>
224      * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP},
225      * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE
226      * frame rate}. Use
227      * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code>
228      * to clear any existing frame rate setting in the format.
229      *
230      * @see MediaCodecInfo.CodecCapabilities#isFormatSupported(MediaFormat) for format keys
231      * considered per android versions when evaluating suitable codecs.
232      *
233      * @param format An encoder media format with optional feature directives.
234      * @throws IllegalArgumentException if format is not a valid media format.
235      * @throws NullPointerException if format is null.
236      * @return the name of an encoder that supports the given format and feature
237      *         requests, or {@code null} if no such codec has been found.
238      */
findEncoderForFormat(MediaFormat format)239     public final String findEncoderForFormat(MediaFormat format) {
240         return findCodecForFormat(true /* encoder */, format);
241     }
242 
findCodecForFormat(boolean encoder, MediaFormat format)243     private String findCodecForFormat(boolean encoder, MediaFormat format) {
244         String mime = format.getString(MediaFormat.KEY_MIME);
245         for (MediaCodecInfo info: mCodecInfos) {
246             if (info.isEncoder() != encoder) {
247                 continue;
248             }
249             try {
250                 MediaCodecInfo.CodecCapabilities caps = info.getCapabilitiesForType(mime);
251                 if (caps != null && caps.isFormatSupported(format)) {
252                     return info.getName();
253                 }
254             } catch (IllegalArgumentException e) {
255                 // type is not supported
256             }
257         }
258         return null;
259     }
260 }
261