diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java index 6914b2f52c..3c788a60a4 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java @@ -71,7 +71,7 @@ public final class MediaCodecInfo { * @return The created instance. */ public static MediaCodecInfo newPassthroughInstance(String name) { - return new MediaCodecInfo(name, null, null); + return new MediaCodecInfo(name, null, null, false); } /** @@ -84,18 +84,29 @@ public final class MediaCodecInfo { */ public static MediaCodecInfo newInstance(String name, String mimeType, CodecCapabilities capabilities) { - return new MediaCodecInfo(name, mimeType, capabilities); + return new MediaCodecInfo(name, mimeType, capabilities, false); } /** - * @param name The name of the decoder. - * @param capabilities The capabilities of the decoder. + * Creates an instance. + * + * @param name The name of the {@link MediaCodec}. + * @param mimeType A mime type supported by the {@link MediaCodec}. + * @param capabilities The capabilities of the {@link MediaCodec} for the specified mime type. + * @param forceDisableAdaptive Whether {@link #adaptive} should be forced to {@code false}. + * @return The created instance. */ - private MediaCodecInfo(String name, String mimeType, CodecCapabilities capabilities) { + public static MediaCodecInfo newInstance(String name, String mimeType, + CodecCapabilities capabilities, boolean forceDisableAdaptive) { + return new MediaCodecInfo(name, mimeType, capabilities, forceDisableAdaptive); + } + + private MediaCodecInfo(String name, String mimeType, CodecCapabilities capabilities, + boolean forceDisableAdaptive) { this.name = Assertions.checkNotNull(name); this.mimeType = mimeType; this.capabilities = capabilities; - adaptive = capabilities != null && isAdaptive(capabilities); + adaptive = !forceDisableAdaptive && capabilities != null && isAdaptive(capabilities); tunneling = capabilities != null && isTunneling(capabilities); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index d58dbc4065..ebc0a0cda3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -339,7 +339,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } String codecName = decoderInfo.name; - codecIsAdaptive = decoderInfo.adaptive && !codecNeedsDisableAdaptationWorkaround(codecName); + codecIsAdaptive = decoderInfo.adaptive; codecNeedsDiscardToSpsWorkaround = codecNeedsDiscardToSpsWorkaround(codecName, format); codecNeedsFlushWorkaround = codecNeedsFlushWorkaround(codecName); codecNeedsAdaptationWorkaround = codecNeedsAdaptationWorkaround(codecName); @@ -1188,18 +1188,4 @@ public abstract class MediaCodecRenderer extends BaseRenderer { && "OMX.MTK.AUDIO.DECODER.MP3".equals(name); } - /** - * Returns whether the decoder is known to fail when adapting, despite advertising itself as an - * adaptive decoder. - *

- * If true is returned then we explicitly disable adaptation for the decoder. - * - * @param name The decoder name. - * @return True if the decoder is known to fail when adapting. - */ - private static boolean codecNeedsDisableAdaptationWorkaround(String name) { - return Util.SDK_INT <= 19 && Util.MODEL.equals("ODROID-XU3") - && ("OMX.Exynos.AVC.Decoder".equals(name) || "OMX.Exynos.AVC.Decoder.secure".equals(name)); - } - } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java index 2bb3603df9..5369dffeb6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java @@ -160,6 +160,55 @@ public final class MediaCodecUtil { return decoderInfos; } + /** + * 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 { + if (maxH264DecodableFrameSize == -1) { + int result = 0; + MediaCodecInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, false); + if (decoderInfo != null) { + for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) { + result = Math.max(avcLevelToMaxFrameSize(profileLevel.level), result); + } + // We assume support for at least 480p (SDK_INT >= 21) or 360p (SDK_INT < 21), which are + // the levels mandated by the Android CDD. + result = Math.max(result, Util.SDK_INT >= 21 ? (720 * 480) : (480 * 360)); + } + maxH264DecodableFrameSize = result; + } + return maxH264DecodableFrameSize; + } + + /** + * Returns profile and level (as defined by {@link CodecProfileLevel}) corresponding to the given + * codec description string (as defined by RFC 6381). + * + * @param codec A codec description string, as defined by RFC 6381. + * @return A pair (profile constant, level constant) if {@code codec} is well-formed and + * recognized, or null otherwise + */ + public static Pair getCodecProfileAndLevel(String codec) { + if (codec == null) { + return null; + } + String[] parts = codec.split("\\."); + switch (parts[0]) { + case CODEC_ID_HEV1: + case CODEC_ID_HVC1: + return getHevcProfileAndLevel(codec, parts); + case CODEC_ID_AVC1: + case CODEC_ID_AVC2: + return getAvcProfileAndLevel(codec, parts); + default: + return null; + } + } + + // Internal methods. + private static List getDecoderInfosInternal( CodecKey key, MediaCodecListCompat mediaCodecList) throws DecoderQueryException { try { @@ -177,12 +226,14 @@ public final class MediaCodecUtil { try { CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(supportedType); boolean secure = mediaCodecList.isSecurePlaybackSupported(mimeType, capabilities); + boolean forceDisableAdaptive = codecNeedsDisableAdaptationWorkaround(codecName); if ((secureDecodersExplicit && key.secure == secure) || (!secureDecodersExplicit && !key.secure)) { - decoderInfos.add(MediaCodecInfo.newInstance(codecName, mimeType, capabilities)); + decoderInfos.add(MediaCodecInfo.newInstance(codecName, mimeType, capabilities, + forceDisableAdaptive)); } else if (!secureDecodersExplicit && secure) { decoderInfos.add(MediaCodecInfo.newInstance(codecName + ".secure", mimeType, - capabilities)); + capabilities, forceDisableAdaptive)); // It only makes sense to have one synthesized secure decoder, return immediately. return decoderInfos; } @@ -289,50 +340,16 @@ public final class MediaCodecUtil { } /** - * Returns the maximum frame size supported by the default H264 decoder. + * Returns whether the decoder is known to fail when adapting, despite advertising itself as an + * adaptive decoder. * - * @return The maximum frame size for an H264 stream that can be decoded on the device. + * @param name The decoder name. + * @return True if the decoder is known to fail when adapting. */ - public static int maxH264DecodableFrameSize() throws DecoderQueryException { - if (maxH264DecodableFrameSize == -1) { - int result = 0; - MediaCodecInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, false); - if (decoderInfo != null) { - for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) { - result = Math.max(avcLevelToMaxFrameSize(profileLevel.level), result); - } - // We assume support for at least 480p (SDK_INT >= 21) or 360p (SDK_INT < 21), which are - // the levels mandated by the Android CDD. - result = Math.max(result, Util.SDK_INT >= 21 ? (720 * 480) : (480 * 360)); - } - maxH264DecodableFrameSize = result; - } - return maxH264DecodableFrameSize; - } - - /** - * Returns profile and level (as defined by {@link CodecProfileLevel}) corresponding to the given - * codec description string (as defined by RFC 6381). - * - * @param codec A codec description string, as defined by RFC 6381. - * @return A pair (profile constant, level constant) if {@code codec} is well-formed and - * recognized, or null otherwise - */ - public static Pair getCodecProfileAndLevel(String codec) { - if (codec == null) { - return null; - } - String[] parts = codec.split("\\."); - switch (parts[0]) { - case CODEC_ID_HEV1: - case CODEC_ID_HVC1: - return getHevcProfileAndLevel(codec, parts); - case CODEC_ID_AVC1: - case CODEC_ID_AVC2: - return getAvcProfileAndLevel(codec, parts); - default: - return null; - } + private static boolean codecNeedsDisableAdaptationWorkaround(String name) { + return Util.SDK_INT <= 22 + && (Util.MODEL.equals("ODROID-XU3") || Util.MODEL.equals("Nexus 10")) + && ("OMX.Exynos.AVC.Decoder".equals(name) || "OMX.Exynos.AVC.Decoder.secure".equals(name)); } private static Pair getHevcProfileAndLevel(String codec, String[] parts) {