diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 60bf20eb7a..7e031c30bf 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,7 @@ ### dev-v2 (not yet released) ### +* Added dependency on checkerframework annotations for static code analysis. * Optimize seeking in FMP4 by enabling seeking to the nearest sync sample within a fragment. This benefits standalone FMP4 playbacks, DASH and SmoothStreaming. * Moved initial bitrate estimate from `AdaptiveTrackSelection` to diff --git a/constants.gradle b/constants.gradle index ea0c2436c0..211a4568b3 100644 --- a/constants.gradle +++ b/constants.gradle @@ -31,6 +31,7 @@ project.ext { junitVersion = '4.12' truthVersion = '0.39' robolectricVersion = '3.7.1' + checkerframeworkVersion = '2.5.0' modulePrefix = ':' if (gradle.ext.has('exoplayerModulePrefix')) { modulePrefix += gradle.ext.exoplayerModulePrefix diff --git a/library/core/build.gradle b/library/core/build.gradle index fe6045c2e7..c7ae0db32a 100644 --- a/library/core/build.gradle +++ b/library/core/build.gradle @@ -46,6 +46,7 @@ android { dependencies { implementation 'com.android.support:support-annotations:' + supportLibraryVersion + implementation 'org.checkerframework:checker-qual:' + checkerframeworkVersion androidTestImplementation 'com.google.dexmaker:dexmaker:' + dexmakerVersion androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion androidTestImplementation 'com.google.truth:truth:' + truthVersion diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Format.java b/library/core/src/main/java/com/google/android/exoplayer2/Format.java index b18225fc70..61d416da09 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Format.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Format.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2; import android.os.Parcel; import android.os.Parcelable; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.util.MimeTypes; @@ -43,29 +44,21 @@ public final class Format implements Parcelable { */ public static final long OFFSET_SAMPLE_RELATIVE = Long.MAX_VALUE; - /** - * An identifier for the format, or null if unknown or not applicable. - */ - public final String id; + /** An identifier for the format, or null if unknown or not applicable. */ + public final @Nullable String id; /** * The average bandwidth in bits per second, or {@link #NO_VALUE} if unknown or not applicable. */ public final int bitrate; - /** - * Codecs of the format as described in RFC 6381, or null if unknown or not applicable. - */ - public final String codecs; - /** - * Metadata, or null if unknown or not applicable. - */ - public final Metadata metadata; + /** Codecs of the format as described in RFC 6381, or null if unknown or not applicable. */ + public final @Nullable String codecs; + /** Metadata, or null if unknown or not applicable. */ + public final @Nullable Metadata metadata; // Container specific. - /** - * The mime type of the container, or null if unknown or not applicable. - */ - public final String containerMimeType; + /** The mime type of the container, or null if unknown or not applicable. */ + public final @Nullable String containerMimeType; // Elementary stream specific. @@ -73,7 +66,7 @@ public final class Format implements Parcelable { * The mime type of the elementary stream (i.e. the individual samples), or null if unknown or not * applicable. */ - public final String sampleMimeType; + public final @Nullable String sampleMimeType; /** * The maximum size of a buffer of data (typically one sample), or {@link #NO_VALUE} if unknown or * not applicable. @@ -84,10 +77,8 @@ public final class Format implements Parcelable { * if initialization data is not required. */ public final List initializationData; - /** - * DRM initialization data if the stream is protected, or null otherwise. - */ - public final DrmInitData drmInitData; + /** DRM initialization data if the stream is protected, or null otherwise. */ + public final @Nullable DrmInitData drmInitData; // Video specific. @@ -117,14 +108,10 @@ public final class Format implements Parcelable { */ @C.StereoMode public final int stereoMode; - /** - * The projection data for 360/VR video, or null if not applicable. - */ - public final byte[] projectionData; - /** - * The color metadata associated with the video, helps with accurate color reproduction. - */ - public final ColorInfo colorInfo; + /** The projection data for 360/VR video, or null if not applicable. */ + public final @Nullable byte[] projectionData; + /** The color metadata associated with the video, helps with accurate color reproduction. */ + public final @Nullable ColorInfo colorInfo; // Audio specific. @@ -171,10 +158,8 @@ public final class Format implements Parcelable { @C.SelectionFlags public final int selectionFlags; - /** - * The language, or null if unknown or not applicable. - */ - public final String language; + /** The language, or null if unknown or not applicable. */ + public final @Nullable String language; /** * The Accessibility channel, or {@link #NO_VALUE} if not known or applicable. @@ -186,36 +171,72 @@ public final class Format implements Parcelable { // Video. - public static Format createVideoContainerFormat(String id, String containerMimeType, - String sampleMimeType, String codecs, int bitrate, int width, int height, - float frameRate, List initializationData, @C.SelectionFlags int selectionFlags) { + public static Format createVideoContainerFormat( + @Nullable String id, + @Nullable String containerMimeType, + String sampleMimeType, + String codecs, + int bitrate, + int width, + int height, + float frameRate, + List initializationData, + @C.SelectionFlags int selectionFlags) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, width, height, frameRate, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE, initializationData, null, null); } - public static Format createVideoSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, int maxInputSize, int width, int height, float frameRate, - List initializationData, DrmInitData drmInitData) { + public static Format createVideoSampleFormat( + @Nullable String id, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + int maxInputSize, + int width, + int height, + float frameRate, + List initializationData, + @Nullable DrmInitData drmInitData) { return createVideoSampleFormat(id, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, initializationData, NO_VALUE, NO_VALUE, drmInitData); } - public static Format createVideoSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, int maxInputSize, int width, int height, float frameRate, - List initializationData, int rotationDegrees, float pixelWidthHeightRatio, - DrmInitData drmInitData) { + public static Format createVideoSampleFormat( + @Nullable String id, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + int maxInputSize, + int width, + int height, + float frameRate, + List initializationData, + int rotationDegrees, + float pixelWidthHeightRatio, + @Nullable DrmInitData drmInitData) { return createVideoSampleFormat(id, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, initializationData, rotationDegrees, pixelWidthHeightRatio, null, NO_VALUE, null, drmInitData); } - public static Format createVideoSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, int maxInputSize, int width, int height, float frameRate, - List initializationData, int rotationDegrees, float pixelWidthHeightRatio, - byte[] projectionData, @C.StereoMode int stereoMode, ColorInfo colorInfo, - DrmInitData drmInitData) { + public static Format createVideoSampleFormat( + @Nullable String id, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + int maxInputSize, + int width, + int height, + float frameRate, + List initializationData, + int rotationDegrees, + float pixelWidthHeightRatio, + byte[] projectionData, + @C.StereoMode int stereoMode, + @Nullable ColorInfo colorInfo, + @Nullable DrmInitData drmInitData) { return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, colorInfo, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, NO_VALUE, @@ -224,37 +245,73 @@ public final class Format implements Parcelable { // Audio. - public static Format createAudioContainerFormat(String id, String containerMimeType, - String sampleMimeType, String codecs, int bitrate, int channelCount, int sampleRate, - List initializationData, @C.SelectionFlags int selectionFlags, String language) { + public static Format createAudioContainerFormat( + @Nullable String id, + @Nullable String containerMimeType, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + int channelCount, + int sampleRate, + List initializationData, + @C.SelectionFlags int selectionFlags, + @Nullable String language) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, channelCount, sampleRate, NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE, initializationData, null, null); } - public static Format createAudioSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, int maxInputSize, int channelCount, int sampleRate, - List initializationData, DrmInitData drmInitData, - @C.SelectionFlags int selectionFlags, String language) { + public static Format createAudioSampleFormat( + @Nullable String id, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + int maxInputSize, + int channelCount, + int sampleRate, + List initializationData, + @Nullable DrmInitData drmInitData, + @C.SelectionFlags int selectionFlags, + @Nullable String language) { return createAudioSampleFormat(id, sampleMimeType, codecs, bitrate, maxInputSize, channelCount, sampleRate, NO_VALUE, initializationData, drmInitData, selectionFlags, language); } - public static Format createAudioSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, int maxInputSize, int channelCount, int sampleRate, - @C.PcmEncoding int pcmEncoding, List initializationData, DrmInitData drmInitData, - @C.SelectionFlags int selectionFlags, String language) { + public static Format createAudioSampleFormat( + @Nullable String id, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + int maxInputSize, + int channelCount, + int sampleRate, + @C.PcmEncoding int pcmEncoding, + List initializationData, + @Nullable DrmInitData drmInitData, + @C.SelectionFlags int selectionFlags, + @Nullable String language) { return createAudioSampleFormat(id, sampleMimeType, codecs, bitrate, maxInputSize, channelCount, sampleRate, pcmEncoding, NO_VALUE, NO_VALUE, initializationData, drmInitData, selectionFlags, language, null); } - public static Format createAudioSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, int maxInputSize, int channelCount, int sampleRate, - @C.PcmEncoding int pcmEncoding, int encoderDelay, int encoderPadding, - List initializationData, DrmInitData drmInitData, - @C.SelectionFlags int selectionFlags, String language, Metadata metadata) { + public static Format createAudioSampleFormat( + @Nullable String id, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + int maxInputSize, + int channelCount, + int sampleRate, + @C.PcmEncoding int pcmEncoding, + int encoderDelay, + int encoderPadding, + List initializationData, + @Nullable DrmInitData drmInitData, + @C.SelectionFlags int selectionFlags, + @Nullable String language, + @Nullable Metadata metadata) { return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE, @@ -263,50 +320,87 @@ public final class Format implements Parcelable { // Text. - public static Format createTextContainerFormat(String id, String containerMimeType, - String sampleMimeType, String codecs, int bitrate, @C.SelectionFlags int selectionFlags, - String language) { + public static Format createTextContainerFormat( + @Nullable String id, + @Nullable String containerMimeType, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + @C.SelectionFlags int selectionFlags, + @Nullable String language) { return createTextContainerFormat(id, containerMimeType, sampleMimeType, codecs, bitrate, selectionFlags, language, NO_VALUE); } - public static Format createTextContainerFormat(String id, String containerMimeType, - String sampleMimeType, String codecs, int bitrate, @C.SelectionFlags int selectionFlags, - String language, int accessibilityChannel) { + public static Format createTextContainerFormat( + @Nullable String id, + @Nullable String containerMimeType, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + @C.SelectionFlags int selectionFlags, + @Nullable String language, + int accessibilityChannel) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, language, accessibilityChannel, OFFSET_SAMPLE_RELATIVE, null, null, null); } - public static Format createTextSampleFormat(String id, String sampleMimeType, - @C.SelectionFlags int selectionFlags, String language) { + public static Format createTextSampleFormat( + @Nullable String id, + String sampleMimeType, + @C.SelectionFlags int selectionFlags, + @Nullable String language) { return createTextSampleFormat(id, sampleMimeType, selectionFlags, language, null); } - public static Format createTextSampleFormat(String id, String sampleMimeType, - @C.SelectionFlags int selectionFlags, String language, DrmInitData drmInitData) { + public static Format createTextSampleFormat( + @Nullable String id, + String sampleMimeType, + @C.SelectionFlags int selectionFlags, + @Nullable String language, + @Nullable DrmInitData drmInitData) { return createTextSampleFormat(id, sampleMimeType, null, NO_VALUE, selectionFlags, language, NO_VALUE, drmInitData, OFFSET_SAMPLE_RELATIVE, Collections.emptyList()); } - public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, @C.SelectionFlags int selectionFlags, String language, int accessibilityChannel, - DrmInitData drmInitData) { + public static Format createTextSampleFormat( + @Nullable String id, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + @C.SelectionFlags int selectionFlags, + @Nullable String language, + int accessibilityChannel, + @Nullable DrmInitData drmInitData) { return createTextSampleFormat(id, sampleMimeType, codecs, bitrate, selectionFlags, language, accessibilityChannel, drmInitData, OFFSET_SAMPLE_RELATIVE, Collections.emptyList()); } - public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, @C.SelectionFlags int selectionFlags, String language, DrmInitData drmInitData, + public static Format createTextSampleFormat( + @Nullable String id, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + @C.SelectionFlags int selectionFlags, + @Nullable String language, + @Nullable DrmInitData drmInitData, long subsampleOffsetUs) { return createTextSampleFormat(id, sampleMimeType, codecs, bitrate, selectionFlags, language, NO_VALUE, drmInitData, subsampleOffsetUs, Collections.emptyList()); } - public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, @C.SelectionFlags int selectionFlags, String language, - int accessibilityChannel, DrmInitData drmInitData, long subsampleOffsetUs, + public static Format createTextSampleFormat( + @Nullable String id, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + @C.SelectionFlags int selectionFlags, + @Nullable String language, + int accessibilityChannel, + @Nullable DrmInitData drmInitData, + long subsampleOffsetUs, List initializationData) { return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, @@ -317,14 +411,14 @@ public final class Format implements Parcelable { // Image. public static Format createImageSampleFormat( - String id, - String sampleMimeType, - String codecs, + @Nullable String id, + @Nullable String sampleMimeType, + @Nullable String codecs, int bitrate, @C.SelectionFlags int selectionFlags, List initializationData, - String language, - DrmInitData drmInitData) { + @Nullable String language, + @Nullable DrmInitData drmInitData) { return new Format( id, null, @@ -356,36 +450,65 @@ public final class Format implements Parcelable { // Generic. - public static Format createContainerFormat(String id, String containerMimeType, - String sampleMimeType, String codecs, int bitrate, @C.SelectionFlags int selectionFlags, - String language) { + public static Format createContainerFormat( + @Nullable String id, + @Nullable String containerMimeType, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + @C.SelectionFlags int selectionFlags, + @Nullable String language) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE, null, null, null); } - public static Format createSampleFormat(String id, String sampleMimeType, - long subsampleOffsetUs) { + public static Format createSampleFormat( + @Nullable String id, @Nullable String sampleMimeType, long subsampleOffsetUs) { return new Format(id, null, sampleMimeType, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, NO_VALUE, subsampleOffsetUs, null, null, null); } - public static Format createSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, DrmInitData drmInitData) { + public static Format createSampleFormat( + @Nullable String id, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + @Nullable DrmInitData drmInitData) { return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE, null, drmInitData, null); } - /* package */ Format(String id, String containerMimeType, String sampleMimeType, String codecs, - int bitrate, int maxInputSize, int width, int height, float frameRate, int rotationDegrees, - float pixelWidthHeightRatio, byte[] projectionData, @C.StereoMode int stereoMode, - ColorInfo colorInfo, int channelCount, int sampleRate, @C.PcmEncoding int pcmEncoding, - int encoderDelay, int encoderPadding, @C.SelectionFlags int selectionFlags, String language, - int accessibilityChannel, long subsampleOffsetUs, List initializationData, - DrmInitData drmInitData, Metadata metadata) { + /* package */ Format( + @Nullable String id, + @Nullable String containerMimeType, + @Nullable String sampleMimeType, + @Nullable String codecs, + int bitrate, + int maxInputSize, + int width, + int height, + float frameRate, + int rotationDegrees, + float pixelWidthHeightRatio, + @Nullable byte[] projectionData, + @C.StereoMode int stereoMode, + @Nullable ColorInfo colorInfo, + int channelCount, + int sampleRate, + @C.PcmEncoding int pcmEncoding, + int encoderDelay, + int encoderPadding, + @C.SelectionFlags int selectionFlags, + @Nullable String language, + int accessibilityChannel, + long subsampleOffsetUs, + @Nullable List initializationData, + @Nullable DrmInitData drmInitData, + @Nullable Metadata metadata) { this.id = id; this.containerMimeType = containerMimeType; this.sampleMimeType = sampleMimeType; @@ -468,14 +591,14 @@ public final class Format implements Parcelable { } public Format copyWithContainerInfo( - String id, - String sampleMimeType, - String codecs, + @Nullable String id, + @Nullable String sampleMimeType, + @Nullable String codecs, int bitrate, int width, int height, @C.SelectionFlags int selectionFlags, - String language) { + @Nullable String language) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, @@ -512,7 +635,7 @@ public final class Format implements Parcelable { drmInitData, metadata); } - public Format copyWithDrmInitData(DrmInitData drmInitData) { + public Format copyWithDrmInitData(@Nullable DrmInitData drmInitData) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, @@ -520,7 +643,7 @@ public final class Format implements Parcelable { drmInitData, metadata); } - public Format copyWithMetadata(Metadata metadata) { + public Format copyWithMetadata(@Nullable Metadata metadata) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, @@ -574,7 +697,7 @@ public final class Format implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java index d30355699c..d1134dc3f6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java @@ -121,11 +121,11 @@ public final class FragmentedMp4Extractor implements Extractor { // Workarounds. @Flags private final int flags; - private final Track sideloadedTrack; + private final @Nullable Track sideloadedTrack; // Sideloaded data. private final List closedCaptionFormats; - private final DrmInitData sideloadedDrmInitData; + private final @Nullable DrmInitData sideloadedDrmInitData; // Track-linked data bundle, accessible as a whole through trackID. private final SparseArray trackBundles; @@ -136,7 +136,7 @@ public final class FragmentedMp4Extractor implements Extractor { private final ParsableByteArray nalBuffer; // Adjusts sample timestamps. - private final TimestampAdjuster timestampAdjuster; + private final @Nullable TimestampAdjuster timestampAdjuster; // Parser state. private final ParsableByteArray atomHeader; @@ -185,20 +185,23 @@ public final class FragmentedMp4Extractor implements Extractor { * @param flags Flags that control the extractor's behavior. * @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed. */ - public FragmentedMp4Extractor(@Flags int flags, TimestampAdjuster timestampAdjuster) { + public FragmentedMp4Extractor(@Flags int flags, @Nullable TimestampAdjuster timestampAdjuster) { this(flags, timestampAdjuster, null, null); } /** * @param flags Flags that control the extractor's behavior. * @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed. - * @param sideloadedTrack Sideloaded track information, in the case that the extractor - * will not receive a moov box in the input data. Null if a moov box is expected. + * @param sideloadedTrack Sideloaded track information, in the case that the extractor will not + * receive a moov box in the input data. Null if a moov box is expected. * @param sideloadedDrmInitData The {@link DrmInitData} to use for encrypted tracks. If null, the * pssh boxes (if present) will be used. */ - public FragmentedMp4Extractor(@Flags int flags, TimestampAdjuster timestampAdjuster, - Track sideloadedTrack, DrmInitData sideloadedDrmInitData) { + public FragmentedMp4Extractor( + @Flags int flags, + @Nullable TimestampAdjuster timestampAdjuster, + @Nullable Track sideloadedTrack, + @Nullable DrmInitData sideloadedDrmInitData) { this(flags, timestampAdjuster, sideloadedTrack, sideloadedDrmInitData, Collections.emptyList()); } @@ -206,15 +209,19 @@ public final class FragmentedMp4Extractor implements Extractor { /** * @param flags Flags that control the extractor's behavior. * @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed. - * @param sideloadedTrack Sideloaded track information, in the case that the extractor - * will not receive a moov box in the input data. Null if a moov box is expected. + * @param sideloadedTrack Sideloaded track information, in the case that the extractor will not + * receive a moov box in the input data. Null if a moov box is expected. * @param sideloadedDrmInitData The {@link DrmInitData} to use for encrypted tracks. If null, the * pssh boxes (if present) will be used. * @param closedCaptionFormats For tracks that contain SEI messages, the formats of the closed * caption channels to expose. */ - public FragmentedMp4Extractor(@Flags int flags, TimestampAdjuster timestampAdjuster, - Track sideloadedTrack, DrmInitData sideloadedDrmInitData, List closedCaptionFormats) { + public FragmentedMp4Extractor( + @Flags int flags, + @Nullable TimestampAdjuster timestampAdjuster, + @Nullable Track sideloadedTrack, + @Nullable DrmInitData sideloadedDrmInitData, + List closedCaptionFormats) { this(flags, timestampAdjuster, sideloadedTrack, sideloadedDrmInitData, closedCaptionFormats, null); } @@ -222,8 +229,8 @@ public final class FragmentedMp4Extractor implements Extractor { /** * @param flags Flags that control the extractor's behavior. * @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed. - * @param sideloadedTrack Sideloaded track information, in the case that the extractor - * will not receive a moov box in the input data. Null if a moov box is expected. + * @param sideloadedTrack Sideloaded track information, in the case that the extractor will not + * receive a moov box in the input data. Null if a moov box is expected. * @param sideloadedDrmInitData The {@link DrmInitData} to use for encrypted tracks. If null, the * pssh boxes (if present) will be used. * @param closedCaptionFormats For tracks that contain SEI messages, the formats of the closed @@ -232,8 +239,12 @@ public final class FragmentedMp4Extractor implements Extractor { * targeting the player, even if {@link #FLAG_ENABLE_EMSG_TRACK} is not set. Null if special * handling of emsg messages for players is not required. */ - public FragmentedMp4Extractor(@Flags int flags, TimestampAdjuster timestampAdjuster, - Track sideloadedTrack, DrmInitData sideloadedDrmInitData, List closedCaptionFormats, + public FragmentedMp4Extractor( + @Flags int flags, + @Nullable TimestampAdjuster timestampAdjuster, + @Nullable Track sideloadedTrack, + @Nullable DrmInitData sideloadedDrmInitData, + List closedCaptionFormats, @Nullable TrackOutput additionalEmsgTrackOutput) { this.flags = flags | (sideloadedTrack != null ? FLAG_SIDELOADED : 0); this.timestampAdjuster = timestampAdjuster; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java index 55ce41e4b1..84513ef4d3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.extractor.mp4; +import android.support.annotation.Nullable; import android.util.Log; import com.google.android.exoplayer2.util.ParsableByteArray; import java.nio.ByteBuffer; @@ -36,7 +37,7 @@ public final class PsshAtomUtil { * @param data The scheme specific data. * @return The PSSH atom. */ - public static byte[] buildPsshAtom(UUID systemId, byte[] data) { + public static byte[] buildPsshAtom(UUID systemId, @Nullable byte[] data) { return buildPsshAtom(systemId, null, data); } @@ -48,7 +49,8 @@ public final class PsshAtomUtil { * @param data The scheme specific data. * @return The PSSH atom. */ - public static byte[] buildPsshAtom(UUID systemId, UUID[] keyIds, byte[] data) { + public static byte[] buildPsshAtom( + UUID systemId, @Nullable UUID[] keyIds, @Nullable byte[] data) { boolean buildV1Atom = keyIds != null; int dataLength = data != null ? data.length : 0; int psshBoxLength = Atom.FULL_HEADER_SIZE + 16 /* SystemId */ + 4 /* DataSize */ + dataLength; @@ -77,14 +79,14 @@ public final class PsshAtomUtil { /** * Parses the UUID from a PSSH atom. Version 0 and 1 PSSH atoms are supported. - *

- * The UUID is only parsed if the data is a valid PSSH atom. + * + *

The UUID is only parsed if the data is a valid PSSH atom. * * @param atom The atom to parse. - * @return The parsed UUID. Null if the input is not a valid PSSH atom, or if the PSSH atom has - * an unsupported version. + * @return The parsed UUID. Null if the input is not a valid PSSH atom, or if the PSSH atom has an + * unsupported version. */ - public static UUID parseUuid(byte[] atom) { + public static @Nullable UUID parseUuid(byte[] atom) { PsshAtom parsedAtom = parsePsshAtom(atom); if (parsedAtom == null) { return null; @@ -111,8 +113,8 @@ public final class PsshAtomUtil { /** * Parses the scheme specific data from a PSSH atom. Version 0 and 1 PSSH atoms are supported. - *

- * The scheme specific data is only parsed if the data is a valid PSSH atom matching the given + * + *

The scheme specific data is only parsed if the data is a valid PSSH atom matching the given * UUID, or if the data is a valid PSSH atom of any type in the case that the passed UUID is null. * * @param atom The atom to parse. @@ -120,7 +122,7 @@ public final class PsshAtomUtil { * @return The parsed scheme specific data. Null if the input is not a valid PSSH atom, or if the * PSSH atom has an unsupported version, or if the PSSH atom does not match the passed UUID. */ - public static byte[] parseSchemeSpecificData(byte[] atom, UUID uuid) { + public static @Nullable byte[] parseSchemeSpecificData(byte[] atom, UUID uuid) { PsshAtom parsedAtom = parsePsshAtom(atom); if (parsedAtom == null) { return null; @@ -140,7 +142,7 @@ public final class PsshAtomUtil { * has an unsupported version. */ // TODO: Support parsing of the key ids for version 1 PSSH atoms. - private static PsshAtom parsePsshAtom(byte[] atom) { + private static @Nullable PsshAtom parsePsshAtom(byte[] atom) { ParsableByteArray atomData = new ParsableByteArray(atom); if (atomData.limit() < Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */) { // Data too short. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/Chunk.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/Chunk.java index 6c9ae690fc..0453a8fa12 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/Chunk.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/Chunk.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source.chunk; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.upstream.DataSource; @@ -51,7 +52,7 @@ public abstract class Chunk implements Loadable { * Optional data associated with the selection of the track to which this chunk belongs. Null if * the chunk does not belong to a track. */ - public final Object trackSelectionData; + public final @Nullable Object trackSelectionData; /** * The start time of the media contained by the chunk, or {@link C#TIME_UNSET} if the data * being loaded does not contain media samples. @@ -75,8 +76,15 @@ public abstract class Chunk implements Loadable { * @param startTimeUs See {@link #startTimeUs}. * @param endTimeUs See {@link #endTimeUs}. */ - public Chunk(DataSource dataSource, DataSpec dataSpec, int type, Format trackFormat, - int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs) { + public Chunk( + DataSource dataSource, + DataSpec dataSpec, + int type, + Format trackFormat, + int trackSelectionReason, + @Nullable Object trackSelectionData, + long startTimeUs, + long endTimeUs) { this.dataSource = Assertions.checkNotNull(dataSource); this.dataSpec = Assertions.checkNotNull(dataSpec); this.type = type; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java index f505beb511..6dd90b8735 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source.chunk; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.extractor.DefaultExtractorInput; @@ -44,8 +45,12 @@ public final class InitializationChunk extends Chunk { * @param trackSelectionData See {@link #trackSelectionData}. * @param extractorWrapper A wrapped extractor to use for parsing the initialization data. */ - public InitializationChunk(DataSource dataSource, DataSpec dataSpec, Format trackFormat, - int trackSelectionReason, Object trackSelectionData, + public InitializationChunk( + DataSource dataSource, + DataSpec dataSpec, + Format trackFormat, + int trackSelectionReason, + @Nullable Object trackSelectionData, ChunkExtractorWrapper extractorWrapper) { super(dataSource, dataSpec, C.DATA_TYPE_MEDIA_INITIALIZATION, trackFormat, trackSelectionReason, trackSelectionData, C.TIME_UNSET, C.TIME_UNSET); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/Assertions.java b/library/core/src/main/java/com/google/android/exoplayer2/util/Assertions.java index aee46eea0e..53c196a14f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/Assertions.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/Assertions.java @@ -16,8 +16,10 @@ package com.google.android.exoplayer2.util; import android.os.Looper; +import android.support.annotation.Nullable; import android.text.TextUtils; import com.google.android.exoplayer2.ExoPlayerLibraryInfo; +import org.checkerframework.checker.nullness.qual.EnsuresNonNull; /** * Provides methods for asserting the truth of expressions and properties. @@ -102,7 +104,8 @@ public final class Assertions { * @return The non-null reference that was validated. * @throws NullPointerException If {@code reference} is null. */ - public static T checkNotNull(T reference) { + @EnsuresNonNull({"#1"}) + public static T checkNotNull(@Nullable T reference) { if (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED && reference == null) { throw new NullPointerException(); } @@ -119,7 +122,8 @@ public final class Assertions { * @return The non-null reference that was validated. * @throws NullPointerException If {@code reference} is null. */ - public static T checkNotNull(T reference, Object errorMessage) { + @EnsuresNonNull({"#1"}) + public static T checkNotNull(@Nullable T reference, Object errorMessage) { if (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED && reference == null) { throw new NullPointerException(String.valueOf(errorMessage)); } @@ -133,7 +137,8 @@ public final class Assertions { * @return The non-null, non-empty string that was validated. * @throws IllegalArgumentException If {@code string} is null or 0-length. */ - public static String checkNotEmpty(String string) { + @EnsuresNonNull({"#1"}) + public static String checkNotEmpty(@Nullable String string) { if (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED && TextUtils.isEmpty(string)) { throw new IllegalArgumentException(); } @@ -149,7 +154,8 @@ public final class Assertions { * @return The non-null, non-empty string that was validated. * @throws IllegalArgumentException If {@code string} is null or 0-length. */ - public static String checkNotEmpty(String string, Object errorMessage) { + @EnsuresNonNull({"#1"}) + public static String checkNotEmpty(@Nullable String string, Object errorMessage) { if (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED && TextUtils.isEmpty(string)) { throw new IllegalArgumentException(String.valueOf(errorMessage)); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index 041ee55cf1..d13aa877e0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -134,14 +134,13 @@ public final class MimeTypes { return BASE_TYPE_APPLICATION.equals(getTopLevelType(mimeType)); } - /** * Derives a video sample mimeType from a codecs attribute. * * @param codecs The codecs attribute. * @return The derived video mimeType, or null if it could not be derived. */ - public static String getVideoMediaMimeType(String codecs) { + public static @Nullable String getVideoMediaMimeType(@Nullable String codecs) { if (codecs == null) { return null; } @@ -161,7 +160,7 @@ public final class MimeTypes { * @param codecs The codecs attribute. * @return The derived audio mimeType, or null if it could not be derived. */ - public static String getAudioMediaMimeType(String codecs) { + public static @Nullable String getAudioMediaMimeType(@Nullable String codecs) { if (codecs == null) { return null; } @@ -181,7 +180,7 @@ public final class MimeTypes { * @param codec The codec identifier to derive. * @return The mimeType, or null if it could not be derived. */ - public static String getMediaMimeType(String codec) { + public static @Nullable String getMediaMimeType(@Nullable String codec) { if (codec == null) { return null; } @@ -345,7 +344,7 @@ public final class MimeTypes { * @param mimeType The mimeType whose top-level type is required. * @return The top-level type, or null if the mimeType is null. */ - private static String getTopLevelType(String mimeType) { + private static @Nullable String getTopLevelType(@Nullable String mimeType) { if (mimeType == null) { return null; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java index 27630eac63..fe83ce13e6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java @@ -29,6 +29,7 @@ import android.net.Uri; import android.os.Build; import android.os.Parcel; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; import android.view.Display; @@ -190,7 +191,7 @@ public final class Util { * @param o2 The second object. * @return {@code o1 == null ? o2 == null : o1.equals(o2)}. */ - public static boolean areEqual(Object o1, Object o2) { + public static boolean areEqual(@Nullable Object o1, @Nullable Object o2) { return o1 == null ? o2 == null : o1.equals(o2); } @@ -224,6 +225,20 @@ public final class Util { list.subList(fromIndex, toIndex).clear(); } + /** + * Copies and optionally truncates an array. Prevents null array elements created by {@link + * Arrays#copyOf(Object[], int)} by ensuring the new length does not exceed the current length. + * + * @param input The input array. + * @param length The output array length. Must be less or equal to the length of the input array. + * @return The copied array. + */ + @SuppressWarnings("nullness:assignment.type.incompatible") + public static T[] nullSafeArrayCopy(T[] input, int length) { + Assertions.checkArgument(length <= input.length); + return Arrays.copyOf(input, length); + } + /** * Instantiates a new single threaded executor whose thread has the specified name. * diff --git a/library/dash/build.gradle b/library/dash/build.gradle index d2692eb7d9..fc4f1c5998 100644 --- a/library/dash/build.gradle +++ b/library/dash/build.gradle @@ -34,6 +34,8 @@ android { dependencies { implementation project(modulePrefix + 'library-core') + implementation 'org.checkerframework:checker-qual:' + checkerframeworkVersion + implementation 'org.checkerframework:checker-compat-qual:' + checkerframeworkVersion implementation 'com.android.support:support-annotations:' + supportLibraryVersion testImplementation project(modulePrefix + 'testutils-robolectric') } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashUtil.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashUtil.java index 54e356f6db..939cb89556 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashUtil.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashUtil.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.source.dash; import android.net.Uri; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.drm.DrmInitData; @@ -65,7 +66,7 @@ public final class DashUtil { * @throws IOException Thrown when there is an error while loading. * @throws InterruptedException Thrown if the thread was interrupted. */ - public static DrmInitData loadDrmInitData(DataSource dataSource, Period period) + public static @Nullable DrmInitData loadDrmInitData(DataSource dataSource, Period period) throws IOException, InterruptedException { int primaryTrackType = C.TRACK_TYPE_VIDEO; Representation representation = getFirstRepresentation(period, primaryTrackType); @@ -87,15 +88,16 @@ public final class DashUtil { * Loads initialization data for the {@code representation} and returns the sample {@link Format}. * * @param dataSource The source from which the data should be loaded. - * @param trackType The type of the representation. Typically one of the - * {@link com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. + * @param trackType The type of the representation. Typically one of the {@link + * com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. * @param representation The representation which initialization chunk belongs to. * @return the sample {@link Format} of the given representation. * @throws IOException Thrown when there is an error while loading. * @throws InterruptedException Thrown if the thread was interrupted. */ - public static Format loadSampleFormat(DataSource dataSource, int trackType, - Representation representation) throws IOException, InterruptedException { + public static @Nullable Format loadSampleFormat( + DataSource dataSource, int trackType, Representation representation) + throws IOException, InterruptedException { ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType, representation, false); return extractorWrapper == null ? null : extractorWrapper.getSampleFormats()[0]; @@ -106,28 +108,29 @@ public final class DashUtil { * ChunkIndex}. * * @param dataSource The source from which the data should be loaded. - * @param trackType The type of the representation. Typically one of the - * {@link com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. + * @param trackType The type of the representation. Typically one of the {@link + * com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. * @param representation The representation which initialization chunk belongs to. * @return The {@link ChunkIndex} of the given representation, or null if no initialization or * index data exists. * @throws IOException Thrown when there is an error while loading. * @throws InterruptedException Thrown if the thread was interrupted. */ - public static ChunkIndex loadChunkIndex(DataSource dataSource, int trackType, - Representation representation) throws IOException, InterruptedException { + public static @Nullable ChunkIndex loadChunkIndex( + DataSource dataSource, int trackType, Representation representation) + throws IOException, InterruptedException { ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType, representation, true); return extractorWrapper == null ? null : (ChunkIndex) extractorWrapper.getSeekMap(); } /** - * Loads initialization data for the {@code representation} and optionally index data then - * returns a {@link ChunkExtractorWrapper} which contains the output. + * Loads initialization data for the {@code representation} and optionally index data then returns + * a {@link ChunkExtractorWrapper} which contains the output. * * @param dataSource The source from which the data should be loaded. - * @param trackType The type of the representation. Typically one of the - * {@link com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. + * @param trackType The type of the representation. Typically one of the {@link + * com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. * @param representation The representation which initialization chunk belongs to. * @param loadIndex Whether to load index data too. * @return A {@link ChunkExtractorWrapper} for the {@code representation}, or null if no @@ -135,8 +138,9 @@ public final class DashUtil { * @throws IOException Thrown when there is an error while loading. * @throws InterruptedException Thrown if the thread was interrupted. */ - private static ChunkExtractorWrapper loadInitializationData(DataSource dataSource, int trackType, - Representation representation, boolean loadIndex) throws IOException, InterruptedException { + private static @Nullable ChunkExtractorWrapper loadInitializationData( + DataSource dataSource, int trackType, Representation representation, boolean loadIndex) + throws IOException, InterruptedException { RangedUri initializationUri = representation.getInitializationUri(); if (initializationUri == null) { return null; @@ -181,7 +185,7 @@ public final class DashUtil { return new ChunkExtractorWrapper(extractor, trackType, format); } - private static Representation getFirstRepresentation(Period period, int type) { + private static @Nullable Representation getFirstRepresentation(Period period, int type) { int index = period.getAdaptationSetIndex(type); if (index == C.INDEX_UNSET) { return null; diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/EventSampleStream.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/EventSampleStream.java index 694f9f843e..7fef59f6a1 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/EventSampleStream.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/EventSampleStream.java @@ -45,8 +45,10 @@ import java.io.IOException; EventSampleStream(EventStream eventStream, Format upstreamFormat, boolean eventStreamUpdatable) { this.upstreamFormat = upstreamFormat; + this.eventStream = eventStream; eventMessageEncoder = new EventMessageEncoder(); pendingSeekPositionUs = C.TIME_UNSET; + eventTimesUs = eventStream.presentationTimesUs; updateEventStream(eventStream, eventStreamUpdatable); } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java index affeeafe50..1bb08c4398 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java @@ -100,6 +100,7 @@ public final class PlayerEmsgHandler implements Handler.Callback { * messages that generate DASH media source events. * @param allocator An {@link Allocator} from which allocations can be obtained. */ + @SuppressWarnings("nullness") public PlayerEmsgHandler( DashManifest manifest, PlayerEmsgCallback playerEmsgCallback, Allocator allocator) { this.manifest = manifest; @@ -237,11 +238,10 @@ public final class PlayerEmsgHandler implements Handler.Callback { // Internal methods. private void handleManifestExpiredMessage(long eventTimeUs, long manifestPublishTimeMsInEmsg) { - if (!manifestPublishTimeToExpiryTimeUs.containsKey(manifestPublishTimeMsInEmsg)) { + Long previousExpiryTimeUs = manifestPublishTimeToExpiryTimeUs.get(manifestPublishTimeMsInEmsg); + if (previousExpiryTimeUs == null) { manifestPublishTimeToExpiryTimeUs.put(manifestPublishTimeMsInEmsg, eventTimeUs); } else { - long previousExpiryTimeUs = - manifestPublishTimeToExpiryTimeUs.get(manifestPublishTimeMsInEmsg); if (previousExpiryTimeUs > eventTimeUs) { manifestPublishTimeToExpiryTimeUs.put(manifestPublishTimeMsInEmsg, eventTimeUs); } @@ -253,10 +253,7 @@ public final class PlayerEmsgHandler implements Handler.Callback { notifySourceMediaPresentationEnded(); } - private Map.Entry ceilingExpiryEntryForPublishTime(long publishTimeMs) { - if (manifestPublishTimeToExpiryTimeUs.isEmpty()) { - return null; - } + private @Nullable Map.Entry ceilingExpiryEntryForPublishTime(long publishTimeMs) { return manifestPublishTimeToExpiryTimeUs.ceilingEntry(publishTimeMs); } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Descriptor.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Descriptor.java index 18d0a937ab..0e21df64bb 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Descriptor.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Descriptor.java @@ -49,7 +49,7 @@ public final class Descriptor { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/RangedUri.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/RangedUri.java index 1df061a796..e226667337 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/RangedUri.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/RangedUri.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.source.dash.manifest; import android.net.Uri; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.util.UriUtil; @@ -46,7 +47,7 @@ public final class RangedUri { * @param length The length of the range, or {@link C#LENGTH_UNSET} to indicate that the range is * unbounded. */ - public RangedUri(String referenceUri, long start, long length) { + public RangedUri(@Nullable String referenceUri, long start, long length) { this.referenceUri = referenceUri == null ? "" : referenceUri; this.start = start; this.length = length; @@ -74,18 +75,18 @@ public final class RangedUri { /** * Attempts to merge this {@link RangedUri} with another and an optional common base uri. - *

- * A merge is successful if both instances define the same {@link Uri} after resolution with the - * base uri, and if one starts the byte after the other ends, forming a contiguous region with + * + *

A merge is successful if both instances define the same {@link Uri} after resolution with + * the base uri, and if one starts the byte after the other ends, forming a contiguous region with * no overlap. - *

- * If {@code other} is null then the merge is considered unsuccessful, and null is returned. + * + *

If {@code other} is null then the merge is considered unsuccessful, and null is returned. * * @param other The {@link RangedUri} to merge. * @param baseUri The optional base Uri. * @return The merged {@link RangedUri} if the merge was successful. Null otherwise. */ - public RangedUri attemptMerge(RangedUri other, String baseUri) { + public @Nullable RangedUri attemptMerge(@Nullable RangedUri other, String baseUri) { final String resolvedUri = resolveUriString(baseUri); if (other == null || !resolvedUri.equals(other.resolveUriString(baseUri))) { return null; @@ -113,7 +114,7 @@ public final class RangedUri { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationKey.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationKey.java index 4ce1d06700..ac9c7794e0 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationKey.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationKey.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.source.dash.manifest; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; /** * Uniquely identifies a {@link Representation} in a {@link DashManifest}. @@ -81,7 +82,7 @@ public final class RepresentationKey implements Parcelable, Comparable