diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java index 34eb2a8fe4..21d2bee056 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java @@ -350,7 +350,12 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media protected List getDecoderInfos( MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder) throws DecoderQueryException { - if (allowPassthrough(format.channelCount, format.sampleMimeType)) { + @Nullable String mimeType = format.sampleMimeType; + if (mimeType == null) { + return Collections.emptyList(); + } + if (allowPassthrough(format.channelCount, mimeType)) { + @Nullable MediaCodecInfo passthroughDecoderInfo = mediaCodecSelector.getPassthroughDecoderInfo(); if (passthroughDecoderInfo != null) { return Collections.singletonList(passthroughDecoderInfo); @@ -358,9 +363,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } List decoderInfos = mediaCodecSelector.getDecoderInfos( - format.sampleMimeType, requiresSecureDecoder, /* requiresTunnelingDecoder= */ false); + mimeType, requiresSecureDecoder, /* requiresTunnelingDecoder= */ false); decoderInfos = MediaCodecUtil.getDecoderInfosSortedByFormatSupport(decoderInfos, format); - if (MimeTypes.AUDIO_E_AC3_JOC.equals(format.sampleMimeType)) { + if (MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType)) { // E-AC3 decoders can decode JOC streams, but in 2-D rather than 3-D. List decoderInfosWithEac3 = new ArrayList<>(decoderInfos); decoderInfosWithEac3.addAll( 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 2d76309b7a..07836672e5 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 @@ -38,6 +38,7 @@ import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.checkerframework.checker.nullness.qual.EnsuresNonNull; /** * A utility class for querying the available codecs. @@ -122,6 +123,7 @@ public final class MediaCodecUtil { */ @Nullable public static MediaCodecInfo getPassthroughDecoderInfo() throws DecoderQueryException { + @Nullable MediaCodecInfo decoderInfo = getDecoderInfo(MimeTypes.AUDIO_RAW, /* secure= */ false, /* tunneling= */ false); return decoderInfo == null ? null : MediaCodecInfo.newPassthroughInstance(decoderInfo.name); @@ -161,7 +163,7 @@ public final class MediaCodecUtil { public static synchronized List getDecoderInfos( String mimeType, boolean secure, boolean tunneling) throws DecoderQueryException { CodecKey key = new CodecKey(mimeType, secure, tunneling); - List cachedDecoderInfos = decoderInfosCache.get(key); + @Nullable List cachedDecoderInfos = decoderInfosCache.get(key); if (cachedDecoderInfos != null) { return cachedDecoderInfos; } @@ -214,6 +216,7 @@ public final class MediaCodecUtil { public static int maxH264DecodableFrameSize() throws DecoderQueryException { if (maxH264DecodableFrameSize == -1) { int result = 0; + @Nullable MediaCodecInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, /* secure= */ false, /* tunneling= */ false); if (decoderInfo != null) { @@ -287,6 +290,7 @@ public final class MediaCodecUtil { for (int i = 0; i < numberOfCodecs; i++) { android.media.MediaCodecInfo codecInfo = mediaCodecList.getCodecInfoAt(i); String name = codecInfo.getName(); + @Nullable String codecMimeType = getCodecMimeType(codecInfo, name, secureDecodersExplicit, mimeType); if (codecMimeType == null) { continue; @@ -652,6 +656,7 @@ public final class MediaCodecUtil { && ("OMX.Exynos.AVC.Decoder".equals(name) || "OMX.Exynos.AVC.Decoder.secure".equals(name)); } + @Nullable private static Pair getDolbyVisionProfileAndLevel( String codec, String[] parts) { if (parts.length < 3) { @@ -665,14 +670,14 @@ public final class MediaCodecUtil { Log.w(TAG, "Ignoring malformed Dolby Vision codec string: " + codec); return null; } - String profileString = matcher.group(1); - Integer profile = DOLBY_VISION_STRING_TO_PROFILE.get(profileString); + @Nullable String profileString = matcher.group(1); + @Nullable Integer profile = DOLBY_VISION_STRING_TO_PROFILE.get(profileString); if (profile == null) { Log.w(TAG, "Unknown Dolby Vision profile string: " + profileString); return null; } String levelString = parts[2]; - Integer level = DOLBY_VISION_STRING_TO_LEVEL.get(levelString); + @Nullable Integer level = DOLBY_VISION_STRING_TO_LEVEL.get(levelString); if (level == null) { Log.w(TAG, "Unknown Dolby Vision level string: " + levelString); return null; @@ -680,6 +685,7 @@ public final class MediaCodecUtil { return new Pair<>(profile, level); } + @Nullable private static Pair getHevcProfileAndLevel(String codec, String[] parts) { if (parts.length < 4) { // The codec has fewer parts than required by the HEVC codec string format. @@ -692,7 +698,7 @@ public final class MediaCodecUtil { Log.w(TAG, "Ignoring malformed HEVC codec string: " + codec); return null; } - String profileString = matcher.group(1); + @Nullable String profileString = matcher.group(1); int profile; if ("1".equals(profileString)) { profile = CodecProfileLevel.HEVCProfileMain; @@ -702,8 +708,8 @@ public final class MediaCodecUtil { Log.w(TAG, "Unknown HEVC profile string: " + profileString); return null; } - String levelString = parts[3]; - Integer level = HEVC_CODEC_STRING_TO_PROFILE_LEVEL.get(levelString); + @Nullable String levelString = parts[3]; + @Nullable Integer level = HEVC_CODEC_STRING_TO_PROFILE_LEVEL.get(levelString); if (level == null) { Log.w(TAG, "Unknown HEVC level string: " + levelString); return null; @@ -711,6 +717,7 @@ public final class MediaCodecUtil { return new Pair<>(profile, level); } + @Nullable private static Pair getAvcProfileAndLevel(String codec, String[] parts) { if (parts.length < 2) { // The codec has fewer parts than required by the AVC codec string format. @@ -751,6 +758,7 @@ public final class MediaCodecUtil { return new Pair<>(profile, level); } + @Nullable private static Pair getVp9ProfileAndLevel(String codec, String[] parts) { if (parts.length < 3) { Log.w(TAG, "Ignoring malformed VP9 codec string: " + codec); @@ -779,6 +787,7 @@ public final class MediaCodecUtil { return new Pair<>(profile, level); } + @Nullable private static Pair getAv1ProfileAndLevel( String codec, String[] parts, @Nullable ColorInfo colorInfo) { if (parts.length < 4) { @@ -874,7 +883,7 @@ public final class MediaCodecUtil { try { // Get the object type indication, which is a hexadecimal value (see RFC 6381/ISO 14496-1). int objectTypeIndication = Integer.parseInt(parts[1], 16); - String mimeType = MimeTypes.getMimeTypeFromMp4ObjectType(objectTypeIndication); + @Nullable String mimeType = MimeTypes.getMimeTypeFromMp4ObjectType(objectTypeIndication); if (MimeTypes.AUDIO_AAC.equals(mimeType)) { // For MPEG-4 audio this is followed by an audio object type indication as a decimal number. int audioObjectTypeIndication = Integer.parseInt(parts[2]); @@ -932,7 +941,7 @@ public final class MediaCodecUtil { private final int codecKind; - private android.media.MediaCodecInfo[] mediaCodecInfos; + @Nullable private android.media.MediaCodecInfo[] mediaCodecInfos; public MediaCodecListCompatV21(boolean includeSecure, boolean includeTunneling) { codecKind = @@ -970,6 +979,7 @@ public final class MediaCodecUtil { return capabilities.isFeatureRequired(feature); } + @EnsuresNonNull({"mediaCodecInfos"}) private void ensureMediaCodecInfosInitialized() { if (mediaCodecInfos == null) { mediaCodecInfos = new MediaCodecList(codecKind).getCodecInfos(); @@ -1028,7 +1038,7 @@ public final class MediaCodecUtil { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((mimeType == null) ? 0 : mimeType.hashCode()); + result = prime * result + mimeType.hashCode(); result = prime * result + (secure ? 1231 : 1237); result = prime * result + (tunneling ? 1231 : 1237); return result; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 7c5ae5f0cb..871ad1689c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -389,11 +389,15 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { boolean requiresSecureDecoder, boolean requiresTunnelingDecoder) throws DecoderQueryException { + @Nullable String mimeType = format.sampleMimeType; + if (mimeType == null) { + return Collections.emptyList(); + } List decoderInfos = mediaCodecSelector.getDecoderInfos( - format.sampleMimeType, requiresSecureDecoder, requiresTunnelingDecoder); + mimeType, requiresSecureDecoder, requiresTunnelingDecoder); decoderInfos = MediaCodecUtil.getDecoderInfosSortedByFormatSupport(decoderInfos, format); - if (MimeTypes.VIDEO_DOLBY_VISION.equals(format.sampleMimeType)) { + if (MimeTypes.VIDEO_DOLBY_VISION.equals(mimeType)) { // Fall back to H.264/AVC or H.265/HEVC for the relevant DV profiles. @Nullable Pair codecProfileAndLevel = MediaCodecUtil.getCodecProfileAndLevel(format);