Select non-primary tunneling decoder
Issue: #3100 Issue: #5547 PiperOrigin-RevId: 243181217
This commit is contained in:
parent
b84c51434f
commit
53934c13b2
@ -61,6 +61,14 @@
|
|||||||
present or newly absent.
|
present or newly absent.
|
||||||
* Add support for reading AC-4 streams
|
* Add support for reading AC-4 streams
|
||||||
([#5303](https://github.com/google/ExoPlayer/pull/5303)).
|
([#5303](https://github.com/google/ExoPlayer/pull/5303)).
|
||||||
|
* Video:
|
||||||
|
* Remove `MediaCodecSelector.DEFAULT_WITH_FALLBACK`. Apps should instead
|
||||||
|
signal that fallback should be used by passing `true` as the
|
||||||
|
`enableDecoderFallback` parameter when instantiating the video renderer.
|
||||||
|
* Support video tunneling when the decoder is not listed first for the MIME
|
||||||
|
type ([#3100](https://github.com/google/ExoPlayer/issues/3100)).
|
||||||
|
* Query `MediaCodecList.ALL_CODECS` when selecting a tunneling decoder
|
||||||
|
([#5547](https://github.com/google/ExoPlayer/issues/5547)).
|
||||||
* Add support for SHOUTcast ICY metadata
|
* Add support for SHOUTcast ICY metadata
|
||||||
([#3735](https://github.com/google/ExoPlayer/issues/3735)).
|
([#3735](https://github.com/google/ExoPlayer/issues/3735)).
|
||||||
* CEA-608: Improved conformance to the specification
|
* CEA-608: Improved conformance to the specification
|
||||||
@ -102,9 +110,6 @@
|
|||||||
([#5698](https://github.com/google/ExoPlayer/issues/5698),
|
([#5698](https://github.com/google/ExoPlayer/issues/5698),
|
||||||
[#5694](https://github.com/google/ExoPlayer/issues/5694)).
|
[#5694](https://github.com/google/ExoPlayer/issues/5694)).
|
||||||
* Move `PriorityTaskManager` from `DefaultLoadControl` to `SimpleExoPlayer`.
|
* Move `PriorityTaskManager` from `DefaultLoadControl` to `SimpleExoPlayer`.
|
||||||
* Remove `MediaCodecSelector.DEFAULT_WITH_FALLBACK`. Apps should instead signal
|
|
||||||
that fallback should be used by passing `true` as the `enableDecoderFallback`
|
|
||||||
parameter when instantiating the video renderer.
|
|
||||||
|
|
||||||
### 2.9.6 ###
|
### 2.9.6 ###
|
||||||
|
|
||||||
|
@ -289,11 +289,15 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<MediaCodecInfo> decoderInfos =
|
List<MediaCodecInfo> decoderInfos =
|
||||||
mediaCodecSelector.getDecoderInfos(format.sampleMimeType, requiresSecureDecryption);
|
mediaCodecSelector.getDecoderInfos(
|
||||||
|
format.sampleMimeType, requiresSecureDecryption, /* requiresTunnelingDecoder= */ false);
|
||||||
if (decoderInfos.isEmpty()) {
|
if (decoderInfos.isEmpty()) {
|
||||||
return requiresSecureDecryption
|
return requiresSecureDecryption
|
||||||
&& !mediaCodecSelector
|
&& !mediaCodecSelector
|
||||||
.getDecoderInfos(format.sampleMimeType, /* requiresSecureDecoder= */ false)
|
.getDecoderInfos(
|
||||||
|
format.sampleMimeType,
|
||||||
|
/* requiresSecureDecoder= */ false,
|
||||||
|
/* requiresTunnelingDecoder= */ false)
|
||||||
.isEmpty()
|
.isEmpty()
|
||||||
? FORMAT_UNSUPPORTED_DRM
|
? FORMAT_UNSUPPORTED_DRM
|
||||||
: FORMAT_UNSUPPORTED_SUBTYPE;
|
: FORMAT_UNSUPPORTED_SUBTYPE;
|
||||||
@ -322,7 +326,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
return Collections.singletonList(passthroughDecoderInfo);
|
return Collections.singletonList(passthroughDecoderInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.getDecoderInfos(mediaCodecSelector, format, requiresSecureDecoder);
|
return mediaCodecSelector.getDecoderInfos(
|
||||||
|
format.sampleMimeType, requiresSecureDecoder, /* requiresTunnelingDecoder= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -441,11 +441,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
* @return A list of {@link MediaCodecInfo}s corresponding to decoders. May be empty.
|
* @return A list of {@link MediaCodecInfo}s corresponding to decoders. May be empty.
|
||||||
* @throws DecoderQueryException Thrown if there was an error querying decoders.
|
* @throws DecoderQueryException Thrown if there was an error querying decoders.
|
||||||
*/
|
*/
|
||||||
protected List<MediaCodecInfo> getDecoderInfos(
|
protected abstract List<MediaCodecInfo> getDecoderInfos(
|
||||||
MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder)
|
MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder)
|
||||||
throws DecoderQueryException {
|
throws DecoderQueryException;
|
||||||
return mediaCodecSelector.getDecoderInfos(format.sampleMimeType, requiresSecureDecoder);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures a newly created {@link MediaCodec}.
|
* Configures a newly created {@link MediaCodec}.
|
||||||
|
@ -32,9 +32,11 @@ public interface MediaCodecSelector {
|
|||||||
MediaCodecSelector DEFAULT =
|
MediaCodecSelector DEFAULT =
|
||||||
new MediaCodecSelector() {
|
new MediaCodecSelector() {
|
||||||
@Override
|
@Override
|
||||||
public List<MediaCodecInfo> getDecoderInfos(String mimeType, boolean requiresSecureDecoder)
|
public List<MediaCodecInfo> getDecoderInfos(
|
||||||
|
String mimeType, boolean requiresSecureDecoder, boolean requiresTunnelingDecoder)
|
||||||
throws DecoderQueryException {
|
throws DecoderQueryException {
|
||||||
return MediaCodecUtil.getDecoderInfos(mimeType, requiresSecureDecoder);
|
return MediaCodecUtil.getDecoderInfos(
|
||||||
|
mimeType, requiresSecureDecoder, requiresTunnelingDecoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -48,10 +50,12 @@ public interface MediaCodecSelector {
|
|||||||
*
|
*
|
||||||
* @param mimeType The MIME type for which a decoder is required.
|
* @param mimeType The MIME type for which a decoder is required.
|
||||||
* @param requiresSecureDecoder Whether a secure decoder is required.
|
* @param requiresSecureDecoder Whether a secure decoder is required.
|
||||||
|
* @param requiresTunnelingDecoder Whether a tunneling decoder is required.
|
||||||
* @return A list of {@link MediaCodecInfo}s corresponding to decoders. May be empty.
|
* @return A list of {@link MediaCodecInfo}s corresponding to decoders. May be empty.
|
||||||
* @throws DecoderQueryException Thrown if there was an error querying decoders.
|
* @throws DecoderQueryException Thrown if there was an error querying decoders.
|
||||||
*/
|
*/
|
||||||
List<MediaCodecInfo> getDecoderInfos(String mimeType, boolean requiresSecureDecoder)
|
List<MediaCodecInfo> getDecoderInfos(
|
||||||
|
String mimeType, boolean requiresSecureDecoder, boolean requiresTunnelingDecoder)
|
||||||
throws DecoderQueryException;
|
throws DecoderQueryException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,17 +87,19 @@ public final class MediaCodecUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional call to warm the codec cache 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)}
|
* <p>Calling this method may speed up subsequent calls to {@link #getDecoderInfo(String, boolean,
|
||||||
* and {@link #getDecoderInfos(String, boolean)}.
|
* boolean)} and {@link #getDecoderInfos(String, boolean, boolean)}.
|
||||||
*
|
*
|
||||||
* @param mimeType The mime type.
|
* @param mimeType The mime type.
|
||||||
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
||||||
* unless secure decryption really is required.
|
* unless secure decryption really is required.
|
||||||
|
* @param tunneling Whether the decoder is required to support tunneling. Always pass false unless
|
||||||
|
* tunneling really is required.
|
||||||
*/
|
*/
|
||||||
public static void warmDecoderInfoCache(String mimeType, boolean secure) {
|
public static void warmDecoderInfoCache(String mimeType, boolean secure, boolean tunneling) {
|
||||||
try {
|
try {
|
||||||
getDecoderInfos(mimeType, secure);
|
getDecoderInfos(mimeType, secure, tunneling);
|
||||||
} catch (DecoderQueryException e) {
|
} catch (DecoderQueryException e) {
|
||||||
// Codec warming is best effort, so we can swallow the exception.
|
// Codec warming is best effort, so we can swallow the exception.
|
||||||
Log.e(TAG, "Codec warming failed", e);
|
Log.e(TAG, "Codec warming failed", e);
|
||||||
@ -112,7 +114,8 @@ public final class MediaCodecUtil {
|
|||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static MediaCodecInfo getPassthroughDecoderInfo() throws DecoderQueryException {
|
public static MediaCodecInfo getPassthroughDecoderInfo() throws DecoderQueryException {
|
||||||
MediaCodecInfo decoderInfo = getDecoderInfo(MimeTypes.AUDIO_RAW, /* secure= */ false);
|
MediaCodecInfo decoderInfo =
|
||||||
|
getDecoderInfo(MimeTypes.AUDIO_RAW, /* secure= */ false, /* tunneling= */ false);
|
||||||
return decoderInfo == null ? null : MediaCodecInfo.newPassthroughInstance(decoderInfo.name);
|
return decoderInfo == null ? null : MediaCodecInfo.newPassthroughInstance(decoderInfo.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,13 +125,15 @@ public final class MediaCodecUtil {
|
|||||||
* @param mimeType The MIME type.
|
* @param mimeType The MIME type.
|
||||||
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
||||||
* unless secure decryption really is required.
|
* unless secure decryption really is required.
|
||||||
|
* @param tunneling Whether the decoder is required to support tunneling. Always pass false unless
|
||||||
|
* tunneling really is required.
|
||||||
* @return A {@link MediaCodecInfo} describing the decoder, or null if no suitable decoder exists.
|
* @return A {@link MediaCodecInfo} describing the decoder, or null if no suitable decoder exists.
|
||||||
* @throws DecoderQueryException If there was an error querying the available decoders.
|
* @throws DecoderQueryException If there was an error querying the available decoders.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static MediaCodecInfo getDecoderInfo(String mimeType, boolean secure)
|
public static MediaCodecInfo getDecoderInfo(String mimeType, boolean secure, boolean tunneling)
|
||||||
throws DecoderQueryException {
|
throws DecoderQueryException {
|
||||||
List<MediaCodecInfo> decoderInfos = getDecoderInfos(mimeType, secure);
|
List<MediaCodecInfo> decoderInfos = getDecoderInfos(mimeType, secure, tunneling);
|
||||||
return decoderInfos.isEmpty() ? null : decoderInfos.get(0);
|
return decoderInfos.isEmpty() ? null : decoderInfos.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,19 +144,23 @@ public final class MediaCodecUtil {
|
|||||||
* @param mimeType The MIME type.
|
* @param mimeType The MIME type.
|
||||||
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
||||||
* unless secure decryption really is required.
|
* unless secure decryption really is required.
|
||||||
|
* @param tunneling Whether the decoder is required to support tunneling. Always pass false unless
|
||||||
|
* tunneling really is required.
|
||||||
* @return A list of all {@link MediaCodecInfo}s for the given mime type, in the order given by
|
* @return A list of all {@link MediaCodecInfo}s for the given mime type, in the order given by
|
||||||
* {@link MediaCodecList}.
|
* {@link MediaCodecList}.
|
||||||
* @throws DecoderQueryException If there was an error querying the available decoders.
|
* @throws DecoderQueryException If there was an error querying the available decoders.
|
||||||
*/
|
*/
|
||||||
public static synchronized List<MediaCodecInfo> getDecoderInfos(String mimeType, boolean secure)
|
public static synchronized List<MediaCodecInfo> getDecoderInfos(
|
||||||
throws DecoderQueryException {
|
String mimeType, boolean secure, boolean tunneling) throws DecoderQueryException {
|
||||||
CodecKey key = new CodecKey(mimeType, secure);
|
CodecKey key = new CodecKey(mimeType, secure, tunneling);
|
||||||
List<MediaCodecInfo> cachedDecoderInfos = decoderInfosCache.get(key);
|
List<MediaCodecInfo> cachedDecoderInfos = decoderInfosCache.get(key);
|
||||||
if (cachedDecoderInfos != null) {
|
if (cachedDecoderInfos != null) {
|
||||||
return cachedDecoderInfos;
|
return cachedDecoderInfos;
|
||||||
}
|
}
|
||||||
MediaCodecListCompat mediaCodecList = Util.SDK_INT >= 21
|
MediaCodecListCompat mediaCodecList =
|
||||||
? new MediaCodecListCompatV21(secure) : new MediaCodecListCompatV16();
|
Util.SDK_INT >= 21
|
||||||
|
? new MediaCodecListCompatV21(secure, tunneling)
|
||||||
|
: new MediaCodecListCompatV16();
|
||||||
ArrayList<MediaCodecInfo> decoderInfos = getDecoderInfosInternal(key, mediaCodecList, mimeType);
|
ArrayList<MediaCodecInfo> decoderInfos = getDecoderInfosInternal(key, mediaCodecList, mimeType);
|
||||||
if (secure && decoderInfos.isEmpty() && 21 <= Util.SDK_INT && Util.SDK_INT <= 23) {
|
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
|
// Some devices don't list secure decoders on API level 21 [Internal: b/18678462]. Try the
|
||||||
@ -165,7 +174,7 @@ public final class MediaCodecUtil {
|
|||||||
}
|
}
|
||||||
if (MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType)) {
|
if (MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType)) {
|
||||||
// E-AC3 decoders can decode JOC streams, but in 2-D rather than 3-D.
|
// E-AC3 decoders can decode JOC streams, but in 2-D rather than 3-D.
|
||||||
CodecKey eac3Key = new CodecKey(MimeTypes.AUDIO_E_AC3, key.secure);
|
CodecKey eac3Key = new CodecKey(MimeTypes.AUDIO_E_AC3, key.secure, key.tunneling);
|
||||||
ArrayList<MediaCodecInfo> eac3DecoderInfos =
|
ArrayList<MediaCodecInfo> eac3DecoderInfos =
|
||||||
getDecoderInfosInternal(eac3Key, mediaCodecList, mimeType);
|
getDecoderInfosInternal(eac3Key, mediaCodecList, mimeType);
|
||||||
decoderInfos.addAll(eac3DecoderInfos);
|
decoderInfos.addAll(eac3DecoderInfos);
|
||||||
@ -184,7 +193,8 @@ public final class MediaCodecUtil {
|
|||||||
public static int maxH264DecodableFrameSize() throws DecoderQueryException {
|
public static int maxH264DecodableFrameSize() throws DecoderQueryException {
|
||||||
if (maxH264DecodableFrameSize == -1) {
|
if (maxH264DecodableFrameSize == -1) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
MediaCodecInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, false);
|
MediaCodecInfo decoderInfo =
|
||||||
|
getDecoderInfo(MimeTypes.VIDEO_H264, /* secure= */ false, /* tunneling= */ false);
|
||||||
if (decoderInfo != null) {
|
if (decoderInfo != null) {
|
||||||
for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) {
|
for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) {
|
||||||
result = Math.max(avcLevelToMaxFrameSize(profileLevel.level), result);
|
result = Math.max(avcLevelToMaxFrameSize(profileLevel.level), result);
|
||||||
@ -202,12 +212,12 @@ public final class MediaCodecUtil {
|
|||||||
* Returns profile and level (as defined by {@link CodecProfileLevel}) corresponding to the given
|
* Returns profile and level (as defined by {@link CodecProfileLevel}) corresponding to the given
|
||||||
* codec description string (as defined by RFC 6381).
|
* codec description string (as defined by RFC 6381).
|
||||||
*
|
*
|
||||||
* @param codec A codec description string, as defined by RFC 6381.
|
* @param codec A codec description string, as defined by RFC 6381, or {@code null} if not known.
|
||||||
* @return A pair (profile constant, level constant) if {@code codec} is well-formed and
|
* @return A pair (profile constant, level constant) if {@code codec} is well-formed and
|
||||||
* recognized, or null otherwise
|
* recognized, or null otherwise
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Pair<Integer, Integer> getCodecProfileAndLevel(String codec) {
|
public static Pair<Integer, Integer> getCodecProfileAndLevel(@Nullable String codec) {
|
||||||
if (codec == null) {
|
if (codec == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -254,39 +264,61 @@ public final class MediaCodecUtil {
|
|||||||
// 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++) {
|
||||||
android.media.MediaCodecInfo codecInfo = mediaCodecList.getCodecInfoAt(i);
|
android.media.MediaCodecInfo codecInfo = mediaCodecList.getCodecInfoAt(i);
|
||||||
String codecName = codecInfo.getName();
|
String name = codecInfo.getName();
|
||||||
String codecSupportedType =
|
String supportedType =
|
||||||
getCodecSupportedType(codecInfo, codecName, secureDecodersExplicit, requestedMimeType);
|
getCodecSupportedType(codecInfo, name, secureDecodersExplicit, requestedMimeType);
|
||||||
if (codecSupportedType != null) {
|
if (supportedType == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(codecSupportedType);
|
CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(supportedType);
|
||||||
boolean secure = mediaCodecList.isSecurePlaybackSupported(mimeType, capabilities);
|
boolean tunnelingSupported =
|
||||||
boolean forceDisableAdaptive = codecNeedsDisableAdaptationWorkaround(codecName);
|
mediaCodecList.isFeatureSupported(
|
||||||
if ((secureDecodersExplicit && key.secure == secure)
|
CodecCapabilities.FEATURE_TunneledPlayback, supportedType, capabilities);
|
||||||
|
boolean tunnelingRequired =
|
||||||
|
mediaCodecList.isFeatureRequired(
|
||||||
|
CodecCapabilities.FEATURE_TunneledPlayback, supportedType, capabilities);
|
||||||
|
if ((!key.tunneling && tunnelingRequired) || (key.tunneling && !tunnelingSupported)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean secureSupported =
|
||||||
|
mediaCodecList.isFeatureSupported(
|
||||||
|
CodecCapabilities.FEATURE_SecurePlayback, supportedType, capabilities);
|
||||||
|
boolean secureRequired =
|
||||||
|
mediaCodecList.isFeatureRequired(
|
||||||
|
CodecCapabilities.FEATURE_SecurePlayback, supportedType, capabilities);
|
||||||
|
if ((!key.secure && secureRequired) || (key.secure && !secureSupported)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean forceDisableAdaptive = codecNeedsDisableAdaptationWorkaround(name);
|
||||||
|
if ((secureDecodersExplicit && key.secure == secureSupported)
|
||||||
|| (!secureDecodersExplicit && !key.secure)) {
|
|| (!secureDecodersExplicit && !key.secure)) {
|
||||||
decoderInfos.add(
|
decoderInfos.add(
|
||||||
MediaCodecInfo.newInstance(
|
MediaCodecInfo.newInstance(
|
||||||
codecName, mimeType, capabilities, forceDisableAdaptive, false));
|
name, mimeType, capabilities, forceDisableAdaptive, /* forceSecure= */ false));
|
||||||
} else if (!secureDecodersExplicit && secure) {
|
} else if (!secureDecodersExplicit && secureSupported) {
|
||||||
decoderInfos.add(
|
decoderInfos.add(
|
||||||
MediaCodecInfo.newInstance(
|
MediaCodecInfo.newInstance(
|
||||||
codecName + ".secure", mimeType, capabilities, forceDisableAdaptive, true));
|
name + ".secure",
|
||||||
|
mimeType,
|
||||||
|
capabilities,
|
||||||
|
forceDisableAdaptive,
|
||||||
|
/* forceSecure= */ true));
|
||||||
// It only makes sense to have one synthesized secure decoder, return immediately.
|
// It only makes sense to have one synthesized secure decoder, return immediately.
|
||||||
return decoderInfos;
|
return decoderInfos;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (Util.SDK_INT <= 23 && !decoderInfos.isEmpty()) {
|
if (Util.SDK_INT <= 23 && !decoderInfos.isEmpty()) {
|
||||||
// Suppress error querying secondary codec capabilities up to API level 23.
|
// Suppress error querying secondary codec capabilities up to API level 23.
|
||||||
Log.e(TAG, "Skipping codec " + codecName + " (failed to query capabilities)");
|
Log.e(TAG, "Skipping codec " + name + " (failed to query capabilities)");
|
||||||
} else {
|
} else {
|
||||||
// Rethrow error querying primary codec capabilities, or secondary codec
|
// Rethrow error querying primary codec capabilities, or secondary codec
|
||||||
// capabilities if API level is greater than 23.
|
// capabilities if API level is greater than 23.
|
||||||
Log.e(TAG, "Failed to query codec " + codecName + " (" + codecSupportedType + ")");
|
Log.e(TAG, "Failed to query codec " + name + " (" + supportedType + ")");
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return decoderInfos;
|
return decoderInfos;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// If the underlying mediaserver is in a bad state, we may catch an IllegalStateException
|
// If the underlying mediaserver is in a bad state, we may catch an IllegalStateException
|
||||||
@ -364,7 +396,8 @@ public final class MediaCodecUtil {
|
|||||||
|
|
||||||
// Work around https://github.com/google/ExoPlayer/issues/1528 and
|
// Work around https://github.com/google/ExoPlayer/issues/1528 and
|
||||||
// https://github.com/google/ExoPlayer/issues/3171.
|
// https://github.com/google/ExoPlayer/issues/3171.
|
||||||
if (Util.SDK_INT < 18 && "OMX.MTK.AUDIO.DECODER.AAC".equals(name)
|
if (Util.SDK_INT < 18
|
||||||
|
&& "OMX.MTK.AUDIO.DECODER.AAC".equals(name)
|
||||||
&& ("a70".equals(Util.DEVICE)
|
&& ("a70".equals(Util.DEVICE)
|
||||||
|| ("Xiaomi".equals(Util.MANUFACTURER) && Util.DEVICE.startsWith("HM")))) {
|
|| ("Xiaomi".equals(Util.MANUFACTURER) && Util.DEVICE.startsWith("HM")))) {
|
||||||
return false;
|
return false;
|
||||||
@ -417,9 +450,12 @@ public final class MediaCodecUtil {
|
|||||||
// Work around https://github.com/google/ExoPlayer/issues/548.
|
// Work around https://github.com/google/ExoPlayer/issues/548.
|
||||||
// VP8 decoder on Samsung Galaxy S3/S4/S4 Mini/Tab 3/Note 2 does not render video.
|
// VP8 decoder on Samsung Galaxy S3/S4/S4 Mini/Tab 3/Note 2 does not render video.
|
||||||
if (Util.SDK_INT <= 19
|
if (Util.SDK_INT <= 19
|
||||||
&& "OMX.SEC.vp8.dec".equals(name) && "samsung".equals(Util.MANUFACTURER)
|
&& "OMX.SEC.vp8.dec".equals(name)
|
||||||
&& (Util.DEVICE.startsWith("d2") || Util.DEVICE.startsWith("serrano")
|
&& "samsung".equals(Util.MANUFACTURER)
|
||||||
|| Util.DEVICE.startsWith("jflte") || Util.DEVICE.startsWith("santos")
|
&& (Util.DEVICE.startsWith("d2")
|
||||||
|
|| Util.DEVICE.startsWith("serrano")
|
||||||
|
|| Util.DEVICE.startsWith("jflte")
|
||||||
|
|| Util.DEVICE.startsWith("santos")
|
||||||
|| Util.DEVICE.startsWith("t0"))) {
|
|| Util.DEVICE.startsWith("t0"))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -659,12 +695,11 @@ public final class MediaCodecUtil {
|
|||||||
*/
|
*/
|
||||||
boolean secureDecodersExplicit();
|
boolean secureDecodersExplicit();
|
||||||
|
|
||||||
/**
|
/** Whether the specified {@link CodecCapabilities} {@code feature} is supported. */
|
||||||
* Whether secure playback is supported for the given {@link CodecCapabilities}, which should
|
boolean isFeatureSupported(String feature, String mimeType, CodecCapabilities capabilities);
|
||||||
* have been obtained from a {@link android.media.MediaCodecInfo} obtained from this list.
|
|
||||||
*/
|
|
||||||
boolean isSecurePlaybackSupported(String mimeType, CodecCapabilities capabilities);
|
|
||||||
|
|
||||||
|
/** Whether the specified {@link CodecCapabilities} {@code feature} is required. */
|
||||||
|
boolean isFeatureRequired(String feature, String mimeType, CodecCapabilities capabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(21)
|
@TargetApi(21)
|
||||||
@ -674,8 +709,11 @@ public final class MediaCodecUtil {
|
|||||||
|
|
||||||
private android.media.MediaCodecInfo[] mediaCodecInfos;
|
private android.media.MediaCodecInfo[] mediaCodecInfos;
|
||||||
|
|
||||||
public MediaCodecListCompatV21(boolean includeSecure) {
|
public MediaCodecListCompatV21(boolean includeSecure, boolean includeTunneling) {
|
||||||
codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS;
|
codecKind =
|
||||||
|
includeSecure || includeTunneling
|
||||||
|
? MediaCodecList.ALL_CODECS
|
||||||
|
: MediaCodecList.REGULAR_CODECS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -696,8 +734,15 @@ public final class MediaCodecUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSecurePlaybackSupported(String mimeType, CodecCapabilities capabilities) {
|
public boolean isFeatureSupported(
|
||||||
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
|
String feature, String mimeType, CodecCapabilities capabilities) {
|
||||||
|
return capabilities.isFeatureSupported(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFeatureRequired(
|
||||||
|
String feature, String mimeType, CodecCapabilities capabilities) {
|
||||||
|
return capabilities.isFeatureRequired(feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureMediaCodecInfosInitialized() {
|
private void ensureMediaCodecInfosInitialized() {
|
||||||
@ -727,10 +772,18 @@ public final class MediaCodecUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSecurePlaybackSupported(String mimeType, CodecCapabilities capabilities) {
|
public boolean isFeatureSupported(
|
||||||
|
String feature, String mimeType, CodecCapabilities capabilities) {
|
||||||
// Secure decoders weren't explicitly listed prior to API level 21. We assume that a secure
|
// Secure decoders weren't explicitly listed prior to API level 21. We assume that a secure
|
||||||
// H264 decoder exists.
|
// H264 decoder exists.
|
||||||
return MimeTypes.VIDEO_H264.equals(mimeType);
|
return CodecCapabilities.FEATURE_SecurePlayback.equals(feature)
|
||||||
|
&& MimeTypes.VIDEO_H264.equals(mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFeatureRequired(
|
||||||
|
String feature, String mimeType, CodecCapabilities capabilities) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -739,10 +792,12 @@ public final class MediaCodecUtil {
|
|||||||
|
|
||||||
public final String mimeType;
|
public final String mimeType;
|
||||||
public final boolean secure;
|
public final boolean secure;
|
||||||
|
public final boolean tunneling;
|
||||||
|
|
||||||
public CodecKey(String mimeType, boolean secure) {
|
public CodecKey(String mimeType, boolean secure, boolean tunneling) {
|
||||||
this.mimeType = mimeType;
|
this.mimeType = mimeType;
|
||||||
this.secure = secure;
|
this.secure = secure;
|
||||||
|
this.tunneling = tunneling;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -751,6 +806,7 @@ public final class MediaCodecUtil {
|
|||||||
int result = 1;
|
int result = 1;
|
||||||
result = prime * result + ((mimeType == null) ? 0 : mimeType.hashCode());
|
result = prime * result + ((mimeType == null) ? 0 : mimeType.hashCode());
|
||||||
result = prime * result + (secure ? 1231 : 1237);
|
result = prime * result + (secure ? 1231 : 1237);
|
||||||
|
result = prime * result + (tunneling ? 1231 : 1237);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -763,7 +819,9 @@ public final class MediaCodecUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
CodecKey other = (CodecKey) obj;
|
CodecKey other = (CodecKey) obj;
|
||||||
return TextUtils.equals(mimeType, other.mimeType) && secure == other.secure;
|
return TextUtils.equals(mimeType, other.mimeType)
|
||||||
|
&& secure == other.secure
|
||||||
|
&& tunneling == other.tunneling;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -901,5 +959,4 @@ public final class MediaCodecUtil {
|
|||||||
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(39, CodecProfileLevel.AACObjectELD);
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(39, CodecProfileLevel.AACObjectELD);
|
||||||
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(42, CodecProfileLevel.AACObjectXHE);
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(42, CodecProfileLevel.AACObjectXHE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ import com.google.android.exoplayer2.util.TraceUtil;
|
|||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import com.google.android.exoplayer2.video.VideoRendererEventListener.EventDispatcher;
|
import com.google.android.exoplayer2.video.VideoRendererEventListener.EventDispatcher;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -304,11 +305,14 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<MediaCodecInfo> decoderInfos =
|
List<MediaCodecInfo> decoderInfos =
|
||||||
mediaCodecSelector.getDecoderInfos(format.sampleMimeType, requiresSecureDecryption);
|
getDecoderInfos(mediaCodecSelector, format, requiresSecureDecryption);
|
||||||
if (decoderInfos.isEmpty()) {
|
if (decoderInfos.isEmpty()) {
|
||||||
return requiresSecureDecryption
|
return requiresSecureDecryption
|
||||||
&& !mediaCodecSelector
|
&& !mediaCodecSelector
|
||||||
.getDecoderInfos(format.sampleMimeType, /* requiresSecureDecoder= */ false)
|
.getDecoderInfos(
|
||||||
|
format.sampleMimeType,
|
||||||
|
/* requiresSecureDecoder= */ false,
|
||||||
|
/* requiresTunnelingDecoder= */ false)
|
||||||
.isEmpty()
|
.isEmpty()
|
||||||
? FORMAT_UNSUPPORTED_DRM
|
? FORMAT_UNSUPPORTED_DRM
|
||||||
: FORMAT_UNSUPPORTED_SUBTYPE;
|
: FORMAT_UNSUPPORTED_SUBTYPE;
|
||||||
@ -323,11 +327,34 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
decoderInfo.isSeamlessAdaptationSupported(format)
|
decoderInfo.isSeamlessAdaptationSupported(format)
|
||||||
? ADAPTIVE_SEAMLESS
|
? ADAPTIVE_SEAMLESS
|
||||||
: ADAPTIVE_NOT_SEAMLESS;
|
: ADAPTIVE_NOT_SEAMLESS;
|
||||||
int tunnelingSupport = decoderInfo.tunneling ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED;
|
int tunnelingSupport = TUNNELING_NOT_SUPPORTED;
|
||||||
|
if (isFormatSupported) {
|
||||||
|
List<MediaCodecInfo> tunnelingDecoderInfos =
|
||||||
|
mediaCodecSelector.getDecoderInfos(
|
||||||
|
format.sampleMimeType,
|
||||||
|
requiresSecureDecryption,
|
||||||
|
/* requiresTunnelingDecoder= */ true);
|
||||||
|
if (!tunnelingDecoderInfos.isEmpty()) {
|
||||||
|
MediaCodecInfo tunnelingDecoderInfo = tunnelingDecoderInfos.get(0);
|
||||||
|
if (tunnelingDecoderInfo.isFormatSupported(format)
|
||||||
|
&& tunnelingDecoderInfo.isSeamlessAdaptationSupported(format)) {
|
||||||
|
tunnelingSupport = TUNNELING_SUPPORTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
int formatSupport = isFormatSupported ? FORMAT_HANDLED : FORMAT_EXCEEDS_CAPABILITIES;
|
int formatSupport = isFormatSupported ? FORMAT_HANDLED : FORMAT_EXCEEDS_CAPABILITIES;
|
||||||
return adaptiveSupport | tunnelingSupport | formatSupport;
|
return adaptiveSupport | tunnelingSupport | formatSupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<MediaCodecInfo> getDecoderInfos(
|
||||||
|
MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder)
|
||||||
|
throws DecoderQueryException {
|
||||||
|
List<MediaCodecInfo> decoderInfos =
|
||||||
|
mediaCodecSelector.getDecoderInfos(format.sampleMimeType, requiresSecureDecoder, tunneling);
|
||||||
|
return Collections.unmodifiableList(decoderInfos);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEnabled(boolean joining) throws ExoPlaybackException {
|
protected void onEnabled(boolean joining) throws ExoPlaybackException {
|
||||||
super.onEnabled(joining);
|
super.onEnabled(joining);
|
||||||
|
@ -628,7 +628,9 @@ public final class DashStreamingTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDecoderInfoH264() throws DecoderQueryException {
|
public void testDecoderInfoH264() throws DecoderQueryException {
|
||||||
MediaCodecInfo decoderInfo = MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_H264, false);
|
MediaCodecInfo decoderInfo =
|
||||||
|
MediaCodecUtil.getDecoderInfo(
|
||||||
|
MimeTypes.VIDEO_H264, /* secure= */ false, /* tunneling= */ false);
|
||||||
assertThat(decoderInfo).isNotNull();
|
assertThat(decoderInfo).isNotNull();
|
||||||
assertThat(Util.SDK_INT < 21 || decoderInfo.adaptive).isTrue();
|
assertThat(Util.SDK_INT < 21 || decoderInfo.adaptive).isTrue();
|
||||||
}
|
}
|
||||||
@ -639,7 +641,11 @@ public final class DashStreamingTest {
|
|||||||
// Pass.
|
// Pass.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assertThat(MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_H265, false).adaptive).isTrue();
|
assertThat(
|
||||||
|
MediaCodecUtil.getDecoderInfo(
|
||||||
|
MimeTypes.VIDEO_H265, /* secure= */ false, /* tunneling= */ false)
|
||||||
|
.adaptive)
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -648,13 +654,18 @@ public final class DashStreamingTest {
|
|||||||
// Pass.
|
// Pass.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assertThat(MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_VP9, false).adaptive).isTrue();
|
assertThat(
|
||||||
|
MediaCodecUtil.getDecoderInfo(
|
||||||
|
MimeTypes.VIDEO_VP9, /* secure= */ false, /* tunneling= */ false)
|
||||||
|
.adaptive)
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal.
|
// Internal.
|
||||||
|
|
||||||
private static boolean shouldSkipAdaptiveTest(String mimeType) throws DecoderQueryException {
|
private static boolean shouldSkipAdaptiveTest(String mimeType) throws DecoderQueryException {
|
||||||
MediaCodecInfo decoderInfo = MediaCodecUtil.getDecoderInfo(mimeType, false);
|
MediaCodecInfo decoderInfo =
|
||||||
|
MediaCodecUtil.getDecoderInfo(mimeType, /* secure= */ false, /* tunneling= */ false);
|
||||||
return decoderInfo == null || !decoderInfo.adaptive;
|
return decoderInfo == null || !decoderInfo.adaptive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,8 @@ public final class DashTestRunner {
|
|||||||
if (Util.SDK_INT >= 18) {
|
if (Util.SDK_INT >= 18) {
|
||||||
try {
|
try {
|
||||||
// Force L3 if secure decoder is not available.
|
// Force L3 if secure decoder is not available.
|
||||||
if (MediaCodecUtil.getDecoderInfo(mimeType, true) == null) {
|
if (MediaCodecUtil.getDecoderInfo(mimeType, /* secure= */ true, /* tunneling= */ false)
|
||||||
|
== null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MediaDrm mediaDrm = MediaDrmBuilder.build();
|
MediaDrm mediaDrm = MediaDrmBuilder.build();
|
||||||
|
@ -82,12 +82,16 @@ public class EnumerateDecodersTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void enumerateDecoders(String mimeType) throws DecoderQueryException {
|
private void enumerateDecoders(String mimeType) throws DecoderQueryException {
|
||||||
logDecoderInfos(mimeType, /* secure= */ false);
|
logDecoderInfos(mimeType, /* secure= */ false, /* tunneling= */ false);
|
||||||
logDecoderInfos(mimeType, /* secure= */ true);
|
logDecoderInfos(mimeType, /* secure= */ true, /* tunneling= */ false);
|
||||||
|
logDecoderInfos(mimeType, /* secure= */ false, /* tunneling= */ true);
|
||||||
|
logDecoderInfos(mimeType, /* secure= */ true, /* tunneling= */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logDecoderInfos(String mimeType, boolean secure) throws DecoderQueryException {
|
private void logDecoderInfos(String mimeType, boolean secure, boolean tunneling)
|
||||||
List<MediaCodecInfo> mediaCodecInfos = MediaCodecUtil.getDecoderInfos(mimeType, secure);
|
throws DecoderQueryException {
|
||||||
|
List<MediaCodecInfo> mediaCodecInfos =
|
||||||
|
MediaCodecUtil.getDecoderInfos(mimeType, secure, tunneling);
|
||||||
for (MediaCodecInfo mediaCodecInfo : mediaCodecInfos) {
|
for (MediaCodecInfo mediaCodecInfo : mediaCodecInfos) {
|
||||||
CodecCapabilities capabilities = Assertions.checkNotNull(mediaCodecInfo.capabilities);
|
CodecCapabilities capabilities = Assertions.checkNotNull(mediaCodecInfo.capabilities);
|
||||||
metricsLogger.logMetric(
|
metricsLogger.logMetric(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user