Use new MediaCodecList APIs on L.
This commit is contained in:
parent
685e1d1f06
commit
6a544da2f8
@ -29,7 +29,7 @@ public final class DecoderInfo {
|
|||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the decoder is adaptive.
|
* Whether the decoder supports seamless resolution switches.
|
||||||
*
|
*
|
||||||
* @see android.media.MediaCodecInfo.CodecCapabilities#isFeatureSupported(String)
|
* @see android.media.MediaCodecInfo.CodecCapabilities#isFeatureSupported(String)
|
||||||
* @see android.media.MediaCodecInfo.CodecCapabilities#FEATURE_AdaptivePlayback
|
* @see android.media.MediaCodecInfo.CodecCapabilities#FEATURE_AdaptivePlayback
|
||||||
|
@ -280,11 +280,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderInfo selectedDecoderInfo = MediaCodecUtil.getDecoderInfo(mimeType);
|
DecoderInfo selectedDecoderInfo = MediaCodecUtil.getDecoderInfo(mimeType,
|
||||||
|
requiresSecureDecoder);
|
||||||
String selectedDecoderName = selectedDecoderInfo.name;
|
String selectedDecoderName = selectedDecoderInfo.name;
|
||||||
if (requiresSecureDecoder) {
|
|
||||||
selectedDecoderName = getSecureDecoderName(selectedDecoderName);
|
|
||||||
}
|
|
||||||
codecIsAdaptive = selectedDecoderInfo.adaptive;
|
codecIsAdaptive = selectedDecoderInfo.adaptive;
|
||||||
try {
|
try {
|
||||||
codec = MediaCodec.createByCodecName(selectedDecoderName);
|
codec = MediaCodec.createByCodecName(selectedDecoderName);
|
||||||
@ -765,13 +763,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
MediaCodec codec, ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo, int bufferIndex,
|
MediaCodec codec, ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo, int bufferIndex,
|
||||||
boolean shouldSkip) throws ExoPlaybackException;
|
boolean shouldSkip) throws ExoPlaybackException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name of the secure variant of a given decoder.
|
|
||||||
*/
|
|
||||||
private static String getSecureDecoderName(String rawDecoderName) {
|
|
||||||
return rawDecoderName + ".secure";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyDecoderInitializationError(final DecoderInitializationException e) {
|
private void notifyDecoderInitializationError(final DecoderInitializationException e) {
|
||||||
if (eventHandler != null && eventListener != null) {
|
if (eventHandler != null && eventListener != null) {
|
||||||
eventHandler.post(new Runnable() {
|
eventHandler.post(new Runnable() {
|
||||||
|
@ -23,6 +23,7 @@ import android.media.MediaCodecInfo;
|
|||||||
import android.media.MediaCodecInfo.CodecCapabilities;
|
import android.media.MediaCodecInfo.CodecCapabilities;
|
||||||
import android.media.MediaCodecInfo.CodecProfileLevel;
|
import android.media.MediaCodecInfo.CodecProfileLevel;
|
||||||
import android.media.MediaCodecList;
|
import android.media.MediaCodecList;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -33,60 +34,79 @@ import java.util.HashMap;
|
|||||||
@TargetApi(16)
|
@TargetApi(16)
|
||||||
public class MediaCodecUtil {
|
public class MediaCodecUtil {
|
||||||
|
|
||||||
private static final HashMap<String, Pair<MediaCodecInfo, CodecCapabilities>> codecs =
|
private static final HashMap<CodecKey, Pair<String, CodecCapabilities>> codecs =
|
||||||
new HashMap<String, Pair<MediaCodecInfo, CodecCapabilities>>();
|
new HashMap<CodecKey, Pair<String, CodecCapabilities>>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get information about the decoder that will be used for a given mime type. If no decoder
|
* Get information about the decoder that will be used for a given mime type.
|
||||||
* exists for the mime type then null is returned.
|
|
||||||
*
|
*
|
||||||
* @param mimeType The mime type.
|
* @param mimeType The mime type.
|
||||||
|
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
||||||
|
* unless secure decryption really is required.
|
||||||
* @return Information about the decoder that will be used, or null if no decoder exists.
|
* @return Information about the decoder that will be used, or null if no decoder exists.
|
||||||
*/
|
*/
|
||||||
public static DecoderInfo getDecoderInfo(String mimeType) {
|
public static DecoderInfo getDecoderInfo(String mimeType, boolean secure) {
|
||||||
Pair<MediaCodecInfo, CodecCapabilities> info = getMediaCodecInfo(mimeType);
|
Pair<String, CodecCapabilities> info = getMediaCodecInfo(mimeType, secure);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new DecoderInfo(info.first.getName(), isAdaptive(info.second));
|
return new DecoderInfo(info.first, isAdaptive(info.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional call to warm the codec cache. Call from any appropriate
|
* Optional call to warm the codec cache for a given mime type.
|
||||||
* place to hide latency.
|
* <p>
|
||||||
*/
|
* Calling this method may speed up subsequent calls to {@link #getDecoderInfo(String, boolean)}.
|
||||||
public static synchronized void warmCodecs(String[] mimeTypes) {
|
|
||||||
for (int i = 0; i < mimeTypes.length; i++) {
|
|
||||||
getMediaCodecInfo(mimeTypes[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the best decoder and its capabilities for the given mimeType. If there's no decoder
|
|
||||||
* returns null.
|
|
||||||
*
|
*
|
||||||
* TODO: We need to use the new object based MediaCodecList API.
|
* @param mimeType The mime type.
|
||||||
|
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
||||||
|
* unless secure decryption really is required.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
public static synchronized void warmCodec(String mimeType, boolean secure) {
|
||||||
private static synchronized Pair<MediaCodecInfo, CodecCapabilities> getMediaCodecInfo(
|
getMediaCodecInfo(mimeType, secure);
|
||||||
String mimeType) {
|
|
||||||
Pair<MediaCodecInfo, CodecCapabilities> result = codecs.get(mimeType);
|
|
||||||
if (result != null) {
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
int numberOfCodecs = MediaCodecList.getCodecCount();
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the best decoder and its capabilities for the given mimeType.
|
||||||
|
*/
|
||||||
|
private static synchronized Pair<String, CodecCapabilities> getMediaCodecInfo(
|
||||||
|
String mimeType, boolean secure) {
|
||||||
|
CodecKey key = new CodecKey(mimeType, secure);
|
||||||
|
if (codecs.containsKey(key)) {
|
||||||
|
return codecs.get(key);
|
||||||
|
}
|
||||||
|
MediaCodecListCompat mediaCodecList = Util.SDK_INT >= 21
|
||||||
|
? new MediaCodecListCompatV21(secure) : new MediaCodecListCompatV16();
|
||||||
|
int numberOfCodecs = mediaCodecList.getCodecCount();
|
||||||
|
boolean secureDecodersExplicit = mediaCodecList.secureDecodersExplicit();
|
||||||
// Note: MediaCodecList is sorted by the framework such that the best decoders come first.
|
// Note: MediaCodecList is sorted by the framework such that the best decoders come first.
|
||||||
for (int i = 0; i < numberOfCodecs; i++) {
|
for (int i = 0; i < numberOfCodecs; i++) {
|
||||||
MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
|
MediaCodecInfo info = mediaCodecList.getCodecInfoAt(i);
|
||||||
String codecName = info.getName();
|
String codecName = info.getName();
|
||||||
if (!info.isEncoder() && codecName.startsWith("OMX.") && !codecName.endsWith(".secure")) {
|
if (!info.isEncoder() && codecName.startsWith("OMX.")
|
||||||
|
&& (secureDecodersExplicit || !codecName.endsWith(".secure"))) {
|
||||||
String[] supportedTypes = info.getSupportedTypes();
|
String[] supportedTypes = info.getSupportedTypes();
|
||||||
for (int j = 0; j < supportedTypes.length; j++) {
|
for (int j = 0; j < supportedTypes.length; j++) {
|
||||||
String supportedType = supportedTypes[j];
|
String supportedType = supportedTypes[j];
|
||||||
if (supportedType.equalsIgnoreCase(mimeType)) {
|
if (supportedType.equalsIgnoreCase(mimeType)) {
|
||||||
result = Pair.create(info, info.getCapabilitiesForType(supportedType));
|
CodecCapabilities capabilities = info.getCapabilitiesForType(supportedType);
|
||||||
codecs.put(mimeType, result);
|
if (!secureDecodersExplicit) {
|
||||||
return result;
|
// Cache variants for secure and insecure playback. Note that the secure decoder is
|
||||||
|
// inferred, and may not actually exist.
|
||||||
|
codecs.put(key.secure ? new CodecKey(mimeType, false) : key,
|
||||||
|
Pair.create(codecName, capabilities));
|
||||||
|
codecs.put(key.secure ? key : new CodecKey(mimeType, true),
|
||||||
|
Pair.create(codecName + ".secure", capabilities));
|
||||||
|
} else {
|
||||||
|
// We can only cache this variant. The other should be listed explicitly.
|
||||||
|
boolean codecSecure = mediaCodecList.isSecurePlaybackSupported(
|
||||||
|
info.getCapabilitiesForType(supportedType));
|
||||||
|
codecs.put(key.secure == codecSecure ? key : new CodecKey(mimeType, codecSecure),
|
||||||
|
Pair.create(codecName, capabilities));
|
||||||
|
}
|
||||||
|
if (codecs.containsKey(key)) {
|
||||||
|
return codecs.get(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,7 +133,7 @@ public class MediaCodecUtil {
|
|||||||
* @return Whether the specified profile is supported at the specified level.
|
* @return Whether the specified profile is supported at the specified level.
|
||||||
*/
|
*/
|
||||||
public static boolean isH264ProfileSupported(int profile, int level) {
|
public static boolean isH264ProfileSupported(int profile, int level) {
|
||||||
Pair<MediaCodecInfo, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264);
|
Pair<String, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264, false);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -133,7 +153,7 @@ public class MediaCodecUtil {
|
|||||||
* @return the maximum frame size for an H264 stream that can be decoded on the device.
|
* @return the maximum frame size for an H264 stream that can be decoded on the device.
|
||||||
*/
|
*/
|
||||||
public static int maxH264DecodableFrameSize() {
|
public static int maxH264DecodableFrameSize() {
|
||||||
Pair<MediaCodecInfo, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264);
|
Pair<String, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264, false);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -177,4 +197,123 @@ public class MediaCodecUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface MediaCodecListCompat {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of codecs in the list.
|
||||||
|
*/
|
||||||
|
public int getCodecCount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The info at the specified index in the list.
|
||||||
|
*
|
||||||
|
* @param index The index.
|
||||||
|
*/
|
||||||
|
public MediaCodecInfo getCodecInfoAt(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns whether secure decoders are explicitly listed, if present.
|
||||||
|
*/
|
||||||
|
public boolean secureDecodersExplicit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether secure playback is supported for the given {@link CodecCapabilities}, which should
|
||||||
|
* have been obtained from a {@link MediaCodecInfo} obtained from this list.
|
||||||
|
* <p>
|
||||||
|
* May only be called if {@link #secureDecodersExplicit()} returns true.
|
||||||
|
*/
|
||||||
|
public boolean isSecurePlaybackSupported(CodecCapabilities capabilities);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(21)
|
||||||
|
private static final class MediaCodecListCompatV21 implements MediaCodecListCompat {
|
||||||
|
|
||||||
|
private final MediaCodecInfo[] mediaCodecInfos;
|
||||||
|
|
||||||
|
public MediaCodecListCompatV21(boolean includeSecure) {
|
||||||
|
int codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS;
|
||||||
|
mediaCodecInfos = new MediaCodecList(codecKind).getCodecInfos();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCodecCount() {
|
||||||
|
return mediaCodecInfos.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaCodecInfo getCodecInfoAt(int index) {
|
||||||
|
return mediaCodecInfos[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean secureDecodersExplicit() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSecurePlaybackSupported(CodecCapabilities capabilities) {
|
||||||
|
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private static final class MediaCodecListCompatV16 implements MediaCodecListCompat {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCodecCount() {
|
||||||
|
return MediaCodecList.getCodecCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaCodecInfo getCodecInfoAt(int index) {
|
||||||
|
return MediaCodecList.getCodecInfoAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean secureDecodersExplicit() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSecurePlaybackSupported(CodecCapabilities capabilities) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CodecKey {
|
||||||
|
|
||||||
|
public final String mimeType;
|
||||||
|
public final boolean secure;
|
||||||
|
|
||||||
|
public CodecKey(String mimeType, boolean secure) {
|
||||||
|
this.mimeType = mimeType;
|
||||||
|
this.secure = secure;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((mimeType == null) ? 0 : mimeType.hashCode());
|
||||||
|
result = prime * result + (secure ? 1231 : 1237);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || obj.getClass() != CodecKey.class) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CodecKey other = (CodecKey) obj;
|
||||||
|
return TextUtils.equals(mimeType, other.mimeType) && secure == other.secure;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user