From ad69a93708b45a6510dda216b20d4a45def7645e Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 11 Feb 2019 18:29:18 +0000 Subject: [PATCH] Handle alternative DV MIME types Also detect ISOBMFF brand for DV when sniffing. PiperOrigin-RevId: 233433449 --- .../exoplayer2/extractor/mp4/Sniffer.java | 54 ++++++----- .../exoplayer2/mediacodec/MediaCodecUtil.java | 96 +++++++++++++------ 2 files changed, 96 insertions(+), 54 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java index a1c90bf1f2..b22c5203c3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java @@ -30,32 +30,34 @@ import java.io.IOException; /** The maximum number of bytes to peek when sniffing. */ private static final int SEARCH_LENGTH = 4 * 1024; - private static final int[] COMPATIBLE_BRANDS = new int[] { - Util.getIntegerCodeForString("isom"), - Util.getIntegerCodeForString("iso2"), - Util.getIntegerCodeForString("iso3"), - Util.getIntegerCodeForString("iso4"), - Util.getIntegerCodeForString("iso5"), - Util.getIntegerCodeForString("iso6"), - Util.getIntegerCodeForString("avc1"), - Util.getIntegerCodeForString("hvc1"), - Util.getIntegerCodeForString("hev1"), - Util.getIntegerCodeForString("mp41"), - Util.getIntegerCodeForString("mp42"), - Util.getIntegerCodeForString("3g2a"), - Util.getIntegerCodeForString("3g2b"), - Util.getIntegerCodeForString("3gr6"), - Util.getIntegerCodeForString("3gs6"), - Util.getIntegerCodeForString("3ge6"), - Util.getIntegerCodeForString("3gg6"), - Util.getIntegerCodeForString("M4V "), - Util.getIntegerCodeForString("M4A "), - Util.getIntegerCodeForString("f4v "), - Util.getIntegerCodeForString("kddi"), - Util.getIntegerCodeForString("M4VP"), - Util.getIntegerCodeForString("qt "), // Apple QuickTime - Util.getIntegerCodeForString("MSNV"), // Sony PSP - }; + private static final int[] COMPATIBLE_BRANDS = + new int[] { + Util.getIntegerCodeForString("isom"), + Util.getIntegerCodeForString("iso2"), + Util.getIntegerCodeForString("iso3"), + Util.getIntegerCodeForString("iso4"), + Util.getIntegerCodeForString("iso5"), + Util.getIntegerCodeForString("iso6"), + Util.getIntegerCodeForString("avc1"), + Util.getIntegerCodeForString("hvc1"), + Util.getIntegerCodeForString("hev1"), + Util.getIntegerCodeForString("mp41"), + Util.getIntegerCodeForString("mp42"), + Util.getIntegerCodeForString("3g2a"), + Util.getIntegerCodeForString("3g2b"), + Util.getIntegerCodeForString("3gr6"), + Util.getIntegerCodeForString("3gs6"), + Util.getIntegerCodeForString("3ge6"), + Util.getIntegerCodeForString("3gg6"), + Util.getIntegerCodeForString("M4V "), + Util.getIntegerCodeForString("M4A "), + Util.getIntegerCodeForString("f4v "), + Util.getIntegerCodeForString("kddi"), + Util.getIntegerCodeForString("M4VP"), + Util.getIntegerCodeForString("qt "), // Apple QuickTime + Util.getIntegerCodeForString("MSNV"), // Sony PSP + Util.getIntegerCodeForString("dby1"), // Dolby Vision + }; /** * Returns whether data peeked from the current position in {@code input} is consistent with the 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 0530057973..6509c8081c 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 @@ -250,34 +250,34 @@ public final class MediaCodecUtil { for (int i = 0; i < numberOfCodecs; i++) { android.media.MediaCodecInfo codecInfo = mediaCodecList.getCodecInfoAt(i); String codecName = codecInfo.getName(); - if (isCodecUsableDecoder(codecInfo, codecName, secureDecodersExplicit, requestedMimeType)) { - for (String supportedType : codecInfo.getSupportedTypes()) { - if (supportedType.equalsIgnoreCase(mimeType)) { - 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, - forceDisableAdaptive, false)); - } else if (!secureDecodersExplicit && secure) { - decoderInfos.add(MediaCodecInfo.newInstance(codecName + ".secure", mimeType, - capabilities, forceDisableAdaptive, true)); - // It only makes sense to have one synthesized secure decoder, return immediately. - return decoderInfos; - } - } catch (Exception e) { - if (Util.SDK_INT <= 23 && !decoderInfos.isEmpty()) { - // Suppress error querying secondary codec capabilities up to API level 23. - Log.e(TAG, "Skipping codec " + codecName + " (failed to query capabilities)"); - } else { - // Rethrow error querying primary codec capabilities, or secondary codec - // capabilities if API level is greater than 23. - Log.e(TAG, "Failed to query codec " + codecName + " (" + supportedType + ")"); - throw e; - } - } + String codecSupportedType = + getCodecSupportedType(codecInfo, codecName, secureDecodersExplicit, requestedMimeType); + if (codecSupportedType != null) { + try { + CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(codecSupportedType); + 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, forceDisableAdaptive, false)); + } else if (!secureDecodersExplicit && secure) { + decoderInfos.add( + MediaCodecInfo.newInstance( + codecName + ".secure", mimeType, capabilities, forceDisableAdaptive, true)); + // It only makes sense to have one synthesized secure decoder, return immediately. + return decoderInfos; + } + } catch (Exception e) { + if (Util.SDK_INT <= 23 && !decoderInfos.isEmpty()) { + // Suppress error querying secondary codec capabilities up to API level 23. + Log.e(TAG, "Skipping codec " + codecName + " (failed to query capabilities)"); + } else { + // Rethrow error querying primary codec capabilities, or secondary codec + // capabilities if API level is greater than 23. + Log.e(TAG, "Failed to query codec " + codecName + " (" + codecSupportedType + ")"); + throw e; } } } @@ -290,6 +290,46 @@ public final class MediaCodecUtil { } } + /** + * Returns the codec's supported type for decoding {@code requestedMimeType} on the current + * device, or {@code null} if the codec can't be used. + * + * @param info The codec information. + * @param name The name of the codec + * @param secureDecodersExplicit Whether secure decoders were explicitly listed, if present. + * @param requestedMimeType The originally requested MIME type, which may differ from the codec + * key MIME type if the codec key is being considered as a fallback. + * @return The codec's supported type for decoding {@code requestedMimeType}, or {@code null} if + * the codec can't be used. + */ + @Nullable + private static String getCodecSupportedType( + android.media.MediaCodecInfo info, + String name, + boolean secureDecodersExplicit, + String requestedMimeType) { + if (isCodecUsableDecoder(info, name, secureDecodersExplicit, requestedMimeType)) { + if (requestedMimeType.equals(MimeTypes.VIDEO_DOLBY_VISION)) { + // Handle decoders that declare support for DV via MIME types that aren't + // video/dolby-vision. + if ("OMX.MS.HEVCDV.Decoder".equals(name)) { + return "video/hevcdv"; + } else if ("OMX.RTK.video.decoder".equals(name) + || "OMX.realtek.video.decoder.tunneled".equals(name)) { + return "video/dv_hevc"; + } + } + + String[] supportedTypes = info.getSupportedTypes(); + for (String supportedType : supportedTypes) { + if (supportedType.equalsIgnoreCase(requestedMimeType)) { + return supportedType; + } + } + } + return null; + } + /** * Returns whether the specified codec is usable for decoding on the current device. *