/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media; import android.util.Log; import android.media.MediaCodecInfo; import java.util.ArrayList; import java.util.Arrays; import java.util.Map; /** * Allows you to enumerate available codecs, each specified as a {@link MediaCodecInfo} object, * find a codec supporting a given format and query the capabilities * of a given codec. * <p>See {@link MediaCodecInfo} for sample usage. */ final public class MediaCodecList { private static final String TAG = "MediaCodecList"; /** * Count the number of available (regular) codecs. * * @deprecated Use {@link #getCodecInfos} instead. * * @see #REGULAR_CODECS */ public static final int getCodecCount() { initCodecList(); return sRegularCodecInfos.length; } private static native final int native_getCodecCount(); /** * Return the {@link MediaCodecInfo} object for the codec at * the given {@code index} in the regular list. * * @deprecated Use {@link #getCodecInfos} instead. * * @see #REGULAR_CODECS */ public static final MediaCodecInfo getCodecInfoAt(int index) { initCodecList(); if (index < 0 || index > sRegularCodecInfos.length) { throw new IllegalArgumentException(); } return sRegularCodecInfos[index]; } /* package private */ static final Map<String, Object> getGlobalSettings() { return sGlobalSettings; } private static Object sInitLock = new Object(); private static MediaCodecInfo[] sAllCodecInfos; private static MediaCodecInfo[] sRegularCodecInfos; private static Map<String, Object> sGlobalSettings; private static final void initCodecList() { synchronized (sInitLock) { if (sRegularCodecInfos == null) { sGlobalSettings = native_getGlobalSettings(); int count = native_getCodecCount(); ArrayList<MediaCodecInfo> regulars = new ArrayList<MediaCodecInfo>(); ArrayList<MediaCodecInfo> all = new ArrayList<MediaCodecInfo>(); for (int index = 0; index < count; index++) { try { MediaCodecInfo info = getNewCodecInfoAt(index); all.add(info); info = info.makeRegular(); if (info != null) { regulars.add(info); } } catch (Exception e) { Log.e(TAG, "Could not get codec capabilities", e); } } sRegularCodecInfos = regulars.toArray(new MediaCodecInfo[regulars.size()]); sAllCodecInfos = all.toArray(new MediaCodecInfo[all.size()]); } } } private static MediaCodecInfo getNewCodecInfoAt(int index) { String[] supportedTypes = getSupportedTypes(index); MediaCodecInfo.CodecCapabilities[] caps = new MediaCodecInfo.CodecCapabilities[supportedTypes.length]; int typeIx = 0; for (String type: supportedTypes) { caps[typeIx++] = getCodecCapabilities(index, type); } return new MediaCodecInfo( getCodecName(index), isEncoder(index), caps); } /* package private */ static native final String getCodecName(int index); /* package private */ static native final boolean isEncoder(int index); /* package private */ static native final String[] getSupportedTypes(int index); /* package private */ static native final MediaCodecInfo.CodecCapabilities getCodecCapabilities(int index, String type); /* package private */ static native final Map<String, Object> native_getGlobalSettings(); /* package private */ static native final int findCodecByName(String codec); /** @hide */ public static MediaCodecInfo getInfoFor(String codec) { initCodecList(); return sAllCodecInfos[findCodecByName(codec)]; } private static native final void native_init(); /** * Use in {@link #MediaCodecList} to enumerate only codecs that are suitable * for regular (buffer-to-buffer) decoding or encoding. * * <em>NOTE:</em> These are the codecs that are returned prior to API 21, * using the now deprecated static methods. */ public static final int REGULAR_CODECS = 0; /** * Use in {@link #MediaCodecList} to enumerate all codecs, even ones that are * not suitable for regular (buffer-to-buffer) decoding or encoding. These * include codecs, for example, that only work with special input or output * surfaces, such as secure-only or tunneled-only codecs. * * @see MediaCodecInfo.CodecCapabilities#isFormatSupported * @see MediaCodecInfo.CodecCapabilities#FEATURE_SecurePlayback * @see MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback */ public static final int ALL_CODECS = 1; private MediaCodecList() { this(REGULAR_CODECS); } private MediaCodecInfo[] mCodecInfos; /** * Create a list of media-codecs of a specific kind. * @param kind Either {@code REGULAR_CODECS} or {@code ALL_CODECS}. */ public MediaCodecList(int kind) { initCodecList(); if (kind == REGULAR_CODECS) { mCodecInfos = sRegularCodecInfos; } else { mCodecInfos = sAllCodecInfos; } } /** * Returns the list of {@link MediaCodecInfo} objects for the list * of media-codecs. */ public final MediaCodecInfo[] getCodecInfos() { return Arrays.copyOf(mCodecInfos, mCodecInfos.length); } static { System.loadLibrary("media_jni"); native_init(); // mediaserver is not yet alive here } /** * Find a decoder supporting a given {@link MediaFormat} in the list * of media-codecs. * * <p class=note> * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP}, * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE * frame rate}. Use * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code> * to clear any existing frame rate setting in the format. * * @param format A decoder media format with optional feature directives. * @throws IllegalArgumentException if format is not a valid media format. * @throws NullPointerException if format is null. * @return the name of a decoder that supports the given format and feature * requests, or {@code null} if no such codec has been found. */ public final String findDecoderForFormat(MediaFormat format) { return findCodecForFormat(false /* encoder */, format); } /** * Find an encoder supporting a given {@link MediaFormat} in the list * of media-codecs. * * <p class=note> * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP}, * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE * frame rate}. Use * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code> * to clear any existing frame rate setting in the format. * * @param format An encoder media format with optional feature directives. * @throws IllegalArgumentException if format is not a valid media format. * @throws NullPointerException if format is null. * @return the name of an encoder that supports the given format and feature * requests, or {@code null} if no such codec has been found. */ public final String findEncoderForFormat(MediaFormat format) { return findCodecForFormat(true /* encoder */, format); } private String findCodecForFormat(boolean encoder, MediaFormat format) { String mime = format.getString(MediaFormat.KEY_MIME); for (MediaCodecInfo info: mCodecInfos) { if (info.isEncoder() != encoder) { continue; } try { MediaCodecInfo.CodecCapabilities caps = info.getCapabilitiesForType(mime); if (caps != null && caps.isFormatSupported(format)) { return info.getName(); } } catch (IllegalArgumentException e) { // type is not supported } } return null; } }