mirror of
https://github.com/androidx/media.git
synced 2025-05-06 23:20:42 +08:00
Update MediaCodecUtil in V2.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=121566154
This commit is contained in:
parent
7f98887468
commit
27132bb2da
@ -26,7 +26,10 @@ import android.media.MediaCodecList;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A utility class for querying the available codecs.
|
||||
@ -50,12 +53,15 @@ public final class MediaCodecUtil {
|
||||
|
||||
private static final String TAG = "MediaCodecUtil";
|
||||
|
||||
private static final HashMap<CodecKey, DecoderInfo> codecs = new HashMap<>();
|
||||
private static final HashMap<CodecKey, List<DecoderInfo>> decoderInfosCache = new HashMap<>();
|
||||
|
||||
// Lazily initialized.
|
||||
private static int maxH264DecodableFrameSize = -1;
|
||||
|
||||
private MediaCodecUtil() {}
|
||||
|
||||
/**
|
||||
* Optional call to pre-populate cached decoder information for a given mime type.
|
||||
* Optional call to warm the codec cache for a given mime type.
|
||||
* <p>
|
||||
* Calling this method may speed up subsequent calls to {@link #getDecoderInfo(String, boolean)}.
|
||||
*
|
||||
@ -63,9 +69,9 @@ public final class MediaCodecUtil {
|
||||
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
||||
* unless secure decryption really is required.
|
||||
*/
|
||||
public static synchronized void warmDecoderInfoCache(String mimeType, boolean secure) {
|
||||
public static void warmDecoderInfoCache(String mimeType, boolean secure) {
|
||||
try {
|
||||
getDecoderInfo(mimeType, secure);
|
||||
getDecoderInfos(mimeType, secure);
|
||||
} catch (DecoderQueryException e) {
|
||||
// Codec warming is best effort, so we can swallow the exception.
|
||||
Log.e(TAG, "Codec warming failed", e);
|
||||
@ -73,85 +79,89 @@ public final class MediaCodecUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link DecoderInfo} describing the most suitable decoder for a given mime type.
|
||||
* Returns information about the decoder that will be used for a given 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, or null if no suitable decoder exists.
|
||||
* @return Information about the decoder that will be used, or null if no decoder exists.
|
||||
*/
|
||||
public static synchronized DecoderInfo getDecoderInfo(String mimeType, boolean secure)
|
||||
public static DecoderInfo getDecoderInfo(String mimeType, boolean secure)
|
||||
throws DecoderQueryException {
|
||||
List<DecoderInfo> decoderInfos = getDecoderInfos(mimeType, secure);
|
||||
return decoderInfos.isEmpty() ? null : decoderInfos.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link DecoderInfo}s for the given mime type, in the order given by
|
||||
* {@link MediaCodecList}.
|
||||
*
|
||||
* @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 A list of all @{link DecoderInfo}s for the given mime type,
|
||||
*/
|
||||
public static synchronized List<DecoderInfo> getDecoderInfos(String mimeType, boolean secure)
|
||||
throws DecoderQueryException {
|
||||
CodecKey key = new CodecKey(mimeType, secure);
|
||||
if (codecs.containsKey(key)) {
|
||||
return codecs.get(key);
|
||||
List<DecoderInfo> decoderInfos = decoderInfosCache.get(key);
|
||||
if (decoderInfos != null) {
|
||||
return decoderInfos;
|
||||
}
|
||||
MediaCodecListCompat mediaCodecList = Util.SDK_INT >= 21
|
||||
? new MediaCodecListCompatV21(secure) : new MediaCodecListCompatV16();
|
||||
DecoderInfo info = getDecoderInfo(key, mediaCodecList);
|
||||
if (secure && info == null && 21 <= Util.SDK_INT && Util.SDK_INT <= 23) {
|
||||
decoderInfos = getDecoderInfosInternal(key, mediaCodecList);
|
||||
if (secure && decoderInfos.isEmpty() && 21 <= Util.SDK_INT && Util.SDK_INT <= 23) {
|
||||
// Some devices don't list secure decoders on API level 21 [Internal: b/18678462]. Try the
|
||||
// legacy path. We also try this path on API levels 22 and 23 as a defensive measure.
|
||||
// TODO: Verify that the issue cannot occur on API levels 22 and 23, and tighten this block
|
||||
// to execute on API level 21 only if confirmed.
|
||||
mediaCodecList = new MediaCodecListCompatV16();
|
||||
info = getDecoderInfo(key, mediaCodecList);
|
||||
if (info != null) {
|
||||
decoderInfos = getDecoderInfosInternal(key, mediaCodecList);
|
||||
if (!decoderInfos.isEmpty()) {
|
||||
Log.w(TAG, "MediaCodecList API didn't list secure decoder for: " + mimeType
|
||||
+ ". Assuming: " + info.name);
|
||||
+ ". Assuming: " + decoderInfos.get(0).name);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
decoderInfos = Collections.unmodifiableList(decoderInfos);
|
||||
decoderInfosCache.put(key, decoderInfos);
|
||||
return decoderInfos;
|
||||
}
|
||||
|
||||
private static DecoderInfo getDecoderInfo(CodecKey key, MediaCodecListCompat mediaCodecList)
|
||||
throws DecoderQueryException {
|
||||
private static List<DecoderInfo> getDecoderInfosInternal(
|
||||
CodecKey key, MediaCodecListCompat mediaCodecList) throws DecoderQueryException {
|
||||
try {
|
||||
return getDecoderInfoInternal(key, mediaCodecList);
|
||||
} catch (Exception e) {
|
||||
// If the underlying mediaserver is in a bad state, we may catch an IllegalStateException
|
||||
// or an IllegalArgumentException here.
|
||||
throw new DecoderQueryException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static DecoderInfo getDecoderInfoInternal(CodecKey key,
|
||||
MediaCodecListCompat mediaCodecList) {
|
||||
List<DecoderInfo> decoderInfos = new ArrayList<>();
|
||||
String mimeType = key.mimeType;
|
||||
int numberOfCodecs = mediaCodecList.getCodecCount();
|
||||
boolean secureDecodersExplicit = mediaCodecList.secureDecodersExplicit();
|
||||
// Note: MediaCodecList is sorted by the framework such that the best decoders come first.
|
||||
for (int i = 0; i < numberOfCodecs; i++) {
|
||||
MediaCodecInfo info = mediaCodecList.getCodecInfoAt(i);
|
||||
String codecName = info.getName();
|
||||
if (isCodecUsableDecoder(info, codecName, secureDecodersExplicit)) {
|
||||
String[] supportedTypes = info.getSupportedTypes();
|
||||
for (String supportedType : supportedTypes) {
|
||||
MediaCodecInfo codecInfo = mediaCodecList.getCodecInfoAt(i);
|
||||
String codecName = codecInfo.getName();
|
||||
if (isCodecUsableDecoder(codecInfo, codecName, secureDecodersExplicit)) {
|
||||
for (String supportedType : codecInfo.getSupportedTypes()) {
|
||||
if (supportedType.equalsIgnoreCase(mimeType)) {
|
||||
CodecCapabilities capabilities = info.getCapabilitiesForType(supportedType);
|
||||
boolean secure = mediaCodecList.isSecurePlaybackSupported(key.mimeType, capabilities);
|
||||
if (!secureDecodersExplicit) {
|
||||
// Cache variants for both insecure and (if we think it's supported) secure playback.
|
||||
codecs.put(key.secure ? new CodecKey(mimeType, false) : key,
|
||||
new DecoderInfo(codecName, capabilities));
|
||||
if (secure) {
|
||||
codecs.put(key.secure ? key : new CodecKey(mimeType, true),
|
||||
new DecoderInfo(codecName + ".secure", capabilities));
|
||||
}
|
||||
} else {
|
||||
// Only cache this variant. If both insecure and secure decoders are available, they
|
||||
// should both be listed separately.
|
||||
codecs.put(key.secure == secure ? key : new CodecKey(mimeType, secure),
|
||||
new DecoderInfo(codecName, capabilities));
|
||||
}
|
||||
if (codecs.containsKey(key)) {
|
||||
return codecs.get(key);
|
||||
CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(supportedType);
|
||||
boolean secure = mediaCodecList.isSecurePlaybackSupported(mimeType, capabilities);
|
||||
if ((secureDecodersExplicit && key.secure == secure)
|
||||
|| (!secureDecodersExplicit && !key.secure)) {
|
||||
decoderInfos.add(new DecoderInfo(codecName, capabilities));
|
||||
} else if (!secureDecodersExplicit && secure) {
|
||||
decoderInfos.add(new DecoderInfo(codecName + ".secure", capabilities));
|
||||
// It only makes sense to have one synthesized secure decoder, return immediately.
|
||||
return decoderInfos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return decoderInfos;
|
||||
} catch (Exception e) {
|
||||
// If the underlying mediaserver is in a bad state, we may catch an IllegalStateException
|
||||
// or an IllegalArgumentException here.
|
||||
throw new DecoderQueryException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -212,48 +222,22 @@ public final class MediaCodecUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the default H264 decoder supports the specified profile at the specified level.
|
||||
*
|
||||
* @param profile A profile constant from {@link CodecProfileLevel}.
|
||||
* @param level A profile level from {@link CodecProfileLevel}.
|
||||
* @return Whether the specified profile is supported at the specified level.
|
||||
*/
|
||||
public static boolean isH264ProfileSupported(int profile, int level)
|
||||
throws DecoderQueryException {
|
||||
DecoderInfo info = getDecoderInfo(MimeTypes.VIDEO_H264, false);
|
||||
if (info == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CodecProfileLevel[] profileLevels = info.getProfileLevels();
|
||||
for (CodecProfileLevel profileLevel : profileLevels) {
|
||||
if (profileLevel.profile == profile && profileLevel.level >= level) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum frame size supported by the default H264 decoder.
|
||||
*
|
||||
* @return The maximum frame size for an H264 stream that can be decoded on the device.
|
||||
*/
|
||||
public static int maxH264DecodableFrameSize() throws DecoderQueryException {
|
||||
DecoderInfo info = getDecoderInfo(MimeTypes.VIDEO_H264, false);
|
||||
if (info == null) {
|
||||
return 0;
|
||||
if (maxH264DecodableFrameSize == -1) {
|
||||
int result = 0;
|
||||
DecoderInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, false);
|
||||
if (decoderInfo != null) {
|
||||
for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) {
|
||||
result = Math.max(avcLevelToMaxFrameSize(profileLevel.level), result);
|
||||
}
|
||||
|
||||
int maxH264DecodableFrameSize = 0;
|
||||
CodecProfileLevel[] profileLevels = info.getProfileLevels();
|
||||
for (CodecProfileLevel profileLevel : profileLevels) {
|
||||
maxH264DecodableFrameSize = Math.max(avcLevelToMaxFrameSize(profileLevel.level),
|
||||
maxH264DecodableFrameSize);
|
||||
}
|
||||
|
||||
maxH264DecodableFrameSize = result;
|
||||
}
|
||||
return maxH264DecodableFrameSize;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user