/*
* 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;
}
}