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