Commit 7799e8fd by tonihei Committed by Oliver Woodman

Add dependency on nullness annotations and add missing annotations for DASH.

This includes only the (hopefully) non-debatable changes for the DASH module
and all needed changes for call into the core library.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=195097791
parent c466fabb
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
### dev-v2 (not yet released) ### ### 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 * Optimize seeking in FMP4 by enabling seeking to the nearest sync sample within
a fragment. This benefits standalone FMP4 playbacks, DASH and SmoothStreaming. a fragment. This benefits standalone FMP4 playbacks, DASH and SmoothStreaming.
* Moved initial bitrate estimate from `AdaptiveTrackSelection` to * Moved initial bitrate estimate from `AdaptiveTrackSelection` to
......
...@@ -31,6 +31,7 @@ project.ext { ...@@ -31,6 +31,7 @@ project.ext {
junitVersion = '4.12' junitVersion = '4.12'
truthVersion = '0.39' truthVersion = '0.39'
robolectricVersion = '3.7.1' robolectricVersion = '3.7.1'
checkerframeworkVersion = '2.5.0'
modulePrefix = ':' modulePrefix = ':'
if (gradle.ext.has('exoplayerModulePrefix')) { if (gradle.ext.has('exoplayerModulePrefix')) {
modulePrefix += gradle.ext.exoplayerModulePrefix modulePrefix += gradle.ext.exoplayerModulePrefix
......
...@@ -46,6 +46,7 @@ android { ...@@ -46,6 +46,7 @@ android {
dependencies { dependencies {
implementation 'com.android.support:support-annotations:' + supportLibraryVersion implementation 'com.android.support:support-annotations:' + supportLibraryVersion
implementation 'org.checkerframework:checker-qual:' + checkerframeworkVersion
androidTestImplementation 'com.google.dexmaker:dexmaker:' + dexmakerVersion androidTestImplementation 'com.google.dexmaker:dexmaker:' + dexmakerVersion
androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion
androidTestImplementation 'com.google.truth:truth:' + truthVersion androidTestImplementation 'com.google.truth:truth:' + truthVersion
......
...@@ -121,11 +121,11 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -121,11 +121,11 @@ public final class FragmentedMp4Extractor implements Extractor {
// Workarounds. // Workarounds.
@Flags private final int flags; @Flags private final int flags;
private final Track sideloadedTrack; private final @Nullable Track sideloadedTrack;
// Sideloaded data. // Sideloaded data.
private final List<Format> closedCaptionFormats; private final List<Format> closedCaptionFormats;
private final DrmInitData sideloadedDrmInitData; private final @Nullable DrmInitData sideloadedDrmInitData;
// Track-linked data bundle, accessible as a whole through trackID. // Track-linked data bundle, accessible as a whole through trackID.
private final SparseArray<TrackBundle> trackBundles; private final SparseArray<TrackBundle> trackBundles;
...@@ -136,7 +136,7 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -136,7 +136,7 @@ public final class FragmentedMp4Extractor implements Extractor {
private final ParsableByteArray nalBuffer; private final ParsableByteArray nalBuffer;
// Adjusts sample timestamps. // Adjusts sample timestamps.
private final TimestampAdjuster timestampAdjuster; private final @Nullable TimestampAdjuster timestampAdjuster;
// Parser state. // Parser state.
private final ParsableByteArray atomHeader; private final ParsableByteArray atomHeader;
...@@ -185,20 +185,23 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -185,20 +185,23 @@ public final class FragmentedMp4Extractor implements Extractor {
* @param flags Flags that control the extractor's behavior. * @param flags Flags that control the extractor's behavior.
* @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed. * @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); this(flags, timestampAdjuster, null, null);
} }
/** /**
* @param flags Flags that control the extractor's behavior. * @param flags Flags that control the extractor's behavior.
* @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed. * @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed.
* @param sideloadedTrack Sideloaded track information, in the case that the extractor * @param sideloadedTrack Sideloaded track information, in the case that the extractor will not
* will not receive a moov box in the input data. Null if a moov box is expected. * 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 * @param sideloadedDrmInitData The {@link DrmInitData} to use for encrypted tracks. If null, the
* pssh boxes (if present) will be used. * pssh boxes (if present) will be used.
*/ */
public FragmentedMp4Extractor(@Flags int flags, TimestampAdjuster timestampAdjuster, public FragmentedMp4Extractor(
Track sideloadedTrack, DrmInitData sideloadedDrmInitData) { @Flags int flags,
@Nullable TimestampAdjuster timestampAdjuster,
@Nullable Track sideloadedTrack,
@Nullable DrmInitData sideloadedDrmInitData) {
this(flags, timestampAdjuster, sideloadedTrack, sideloadedDrmInitData, this(flags, timestampAdjuster, sideloadedTrack, sideloadedDrmInitData,
Collections.<Format>emptyList()); Collections.<Format>emptyList());
} }
...@@ -206,15 +209,19 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -206,15 +209,19 @@ public final class FragmentedMp4Extractor implements Extractor {
/** /**
* @param flags Flags that control the extractor's behavior. * @param flags Flags that control the extractor's behavior.
* @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed. * @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed.
* @param sideloadedTrack Sideloaded track information, in the case that the extractor * @param sideloadedTrack Sideloaded track information, in the case that the extractor will not
* will not receive a moov box in the input data. Null if a moov box is expected. * 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 * @param sideloadedDrmInitData The {@link DrmInitData} to use for encrypted tracks. If null, the
* pssh boxes (if present) will be used. * pssh boxes (if present) will be used.
* @param closedCaptionFormats For tracks that contain SEI messages, the formats of the closed * @param closedCaptionFormats For tracks that contain SEI messages, the formats of the closed
* caption channels to expose. * caption channels to expose.
*/ */
public FragmentedMp4Extractor(@Flags int flags, TimestampAdjuster timestampAdjuster, public FragmentedMp4Extractor(
Track sideloadedTrack, DrmInitData sideloadedDrmInitData, List<Format> closedCaptionFormats) { @Flags int flags,
@Nullable TimestampAdjuster timestampAdjuster,
@Nullable Track sideloadedTrack,
@Nullable DrmInitData sideloadedDrmInitData,
List<Format> closedCaptionFormats) {
this(flags, timestampAdjuster, sideloadedTrack, sideloadedDrmInitData, this(flags, timestampAdjuster, sideloadedTrack, sideloadedDrmInitData,
closedCaptionFormats, null); closedCaptionFormats, null);
} }
...@@ -222,8 +229,8 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -222,8 +229,8 @@ public final class FragmentedMp4Extractor implements Extractor {
/** /**
* @param flags Flags that control the extractor's behavior. * @param flags Flags that control the extractor's behavior.
* @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed. * @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed.
* @param sideloadedTrack Sideloaded track information, in the case that the extractor * @param sideloadedTrack Sideloaded track information, in the case that the extractor will not
* will not receive a moov box in the input data. Null if a moov box is expected. * 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 * @param sideloadedDrmInitData The {@link DrmInitData} to use for encrypted tracks. If null, the
* pssh boxes (if present) will be used. * pssh boxes (if present) will be used.
* @param closedCaptionFormats For tracks that contain SEI messages, the formats of the closed * @param closedCaptionFormats For tracks that contain SEI messages, the formats of the closed
...@@ -232,8 +239,12 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -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 * 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. * handling of emsg messages for players is not required.
*/ */
public FragmentedMp4Extractor(@Flags int flags, TimestampAdjuster timestampAdjuster, public FragmentedMp4Extractor(
Track sideloadedTrack, DrmInitData sideloadedDrmInitData, List<Format> closedCaptionFormats, @Flags int flags,
@Nullable TimestampAdjuster timestampAdjuster,
@Nullable Track sideloadedTrack,
@Nullable DrmInitData sideloadedDrmInitData,
List<Format> closedCaptionFormats,
@Nullable TrackOutput additionalEmsgTrackOutput) { @Nullable TrackOutput additionalEmsgTrackOutput) {
this.flags = flags | (sideloadedTrack != null ? FLAG_SIDELOADED : 0); this.flags = flags | (sideloadedTrack != null ? FLAG_SIDELOADED : 0);
this.timestampAdjuster = timestampAdjuster; this.timestampAdjuster = timestampAdjuster;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.extractor.mp4; package com.google.android.exoplayer2.extractor.mp4;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
...@@ -36,7 +37,7 @@ public final class PsshAtomUtil { ...@@ -36,7 +37,7 @@ public final class PsshAtomUtil {
* @param data The scheme specific data. * @param data The scheme specific data.
* @return The PSSH atom. * @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); return buildPsshAtom(systemId, null, data);
} }
...@@ -48,7 +49,8 @@ public final class PsshAtomUtil { ...@@ -48,7 +49,8 @@ public final class PsshAtomUtil {
* @param data The scheme specific data. * @param data The scheme specific data.
* @return The PSSH atom. * @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; boolean buildV1Atom = keyIds != null;
int dataLength = data != null ? data.length : 0; int dataLength = data != null ? data.length : 0;
int psshBoxLength = Atom.FULL_HEADER_SIZE + 16 /* SystemId */ + 4 /* DataSize */ + dataLength; int psshBoxLength = Atom.FULL_HEADER_SIZE + 16 /* SystemId */ + 4 /* DataSize */ + dataLength;
...@@ -77,14 +79,14 @@ public final class PsshAtomUtil { ...@@ -77,14 +79,14 @@ public final class PsshAtomUtil {
/** /**
* Parses the UUID from a PSSH atom. Version 0 and 1 PSSH atoms are supported. * Parses the UUID from a PSSH atom. Version 0 and 1 PSSH atoms are supported.
* <p> *
* The UUID is only parsed if the data is a valid PSSH atom. * <p>The UUID is only parsed if the data is a valid PSSH atom.
* *
* @param atom The atom to parse. * @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 * @return The parsed UUID. Null if the input is not a valid PSSH atom, or if the PSSH atom has an
* an unsupported version. * unsupported version.
*/ */
public static UUID parseUuid(byte[] atom) { public static @Nullable UUID parseUuid(byte[] atom) {
PsshAtom parsedAtom = parsePsshAtom(atom); PsshAtom parsedAtom = parsePsshAtom(atom);
if (parsedAtom == null) { if (parsedAtom == null) {
return null; return null;
...@@ -111,8 +113,8 @@ public final class PsshAtomUtil { ...@@ -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. * Parses the scheme specific data from a PSSH atom. Version 0 and 1 PSSH atoms are supported.
* <p> *
* The scheme specific data is only parsed if the data is a valid PSSH atom matching the given * <p>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. * 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. * @param atom The atom to parse.
...@@ -120,7 +122,7 @@ public final class PsshAtomUtil { ...@@ -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 * @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. * 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); PsshAtom parsedAtom = parsePsshAtom(atom);
if (parsedAtom == null) { if (parsedAtom == null) {
return null; return null;
...@@ -140,7 +142,7 @@ public final class PsshAtomUtil { ...@@ -140,7 +142,7 @@ public final class PsshAtomUtil {
* has an unsupported version. * has an unsupported version.
*/ */
// TODO: Support parsing of the key ids for version 1 PSSH atoms. // 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); ParsableByteArray atomData = new ParsableByteArray(atom);
if (atomData.limit() < Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */) { if (atomData.limit() < Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */) {
// Data too short. // Data too short.
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source.chunk; package com.google.android.exoplayer2.source.chunk;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
...@@ -51,7 +52,7 @@ public abstract class Chunk implements Loadable { ...@@ -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 * Optional data associated with the selection of the track to which this chunk belongs. Null if
* the chunk does not belong to a track. * 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 * 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. * being loaded does not contain media samples.
...@@ -75,8 +76,15 @@ public abstract class Chunk implements Loadable { ...@@ -75,8 +76,15 @@ public abstract class Chunk implements Loadable {
* @param startTimeUs See {@link #startTimeUs}. * @param startTimeUs See {@link #startTimeUs}.
* @param endTimeUs See {@link #endTimeUs}. * @param endTimeUs See {@link #endTimeUs}.
*/ */
public Chunk(DataSource dataSource, DataSpec dataSpec, int type, Format trackFormat, public Chunk(
int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs) { DataSource dataSource,
DataSpec dataSpec,
int type,
Format trackFormat,
int trackSelectionReason,
@Nullable Object trackSelectionData,
long startTimeUs,
long endTimeUs) {
this.dataSource = Assertions.checkNotNull(dataSource); this.dataSource = Assertions.checkNotNull(dataSource);
this.dataSpec = Assertions.checkNotNull(dataSpec); this.dataSpec = Assertions.checkNotNull(dataSpec);
this.type = type; this.type = type;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source.chunk; package com.google.android.exoplayer2.source.chunk;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput; import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
...@@ -44,8 +45,12 @@ public final class InitializationChunk extends Chunk { ...@@ -44,8 +45,12 @@ public final class InitializationChunk extends Chunk {
* @param trackSelectionData See {@link #trackSelectionData}. * @param trackSelectionData See {@link #trackSelectionData}.
* @param extractorWrapper A wrapped extractor to use for parsing the initialization data. * @param extractorWrapper A wrapped extractor to use for parsing the initialization data.
*/ */
public InitializationChunk(DataSource dataSource, DataSpec dataSpec, Format trackFormat, public InitializationChunk(
int trackSelectionReason, Object trackSelectionData, DataSource dataSource,
DataSpec dataSpec,
Format trackFormat,
int trackSelectionReason,
@Nullable Object trackSelectionData,
ChunkExtractorWrapper extractorWrapper) { ChunkExtractorWrapper extractorWrapper) {
super(dataSource, dataSpec, C.DATA_TYPE_MEDIA_INITIALIZATION, trackFormat, trackSelectionReason, super(dataSource, dataSpec, C.DATA_TYPE_MEDIA_INITIALIZATION, trackFormat, trackSelectionReason,
trackSelectionData, C.TIME_UNSET, C.TIME_UNSET); trackSelectionData, C.TIME_UNSET, C.TIME_UNSET);
......
...@@ -16,8 +16,10 @@ ...@@ -16,8 +16,10 @@
package com.google.android.exoplayer2.util; package com.google.android.exoplayer2.util;
import android.os.Looper; import android.os.Looper;
import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo; import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
/** /**
* Provides methods for asserting the truth of expressions and properties. * Provides methods for asserting the truth of expressions and properties.
...@@ -102,7 +104,8 @@ public final class Assertions { ...@@ -102,7 +104,8 @@ public final class Assertions {
* @return The non-null reference that was validated. * @return The non-null reference that was validated.
* @throws NullPointerException If {@code reference} is null. * @throws NullPointerException If {@code reference} is null.
*/ */
public static <T> T checkNotNull(T reference) { @EnsuresNonNull({"#1"})
public static <T> T checkNotNull(@Nullable T reference) {
if (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED && reference == null) { if (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED && reference == null) {
throw new NullPointerException(); throw new NullPointerException();
} }
...@@ -119,7 +122,8 @@ public final class Assertions { ...@@ -119,7 +122,8 @@ public final class Assertions {
* @return The non-null reference that was validated. * @return The non-null reference that was validated.
* @throws NullPointerException If {@code reference} is null. * @throws NullPointerException If {@code reference} is null.
*/ */
public static <T> T checkNotNull(T reference, Object errorMessage) { @EnsuresNonNull({"#1"})
public static <T> T checkNotNull(@Nullable T reference, Object errorMessage) {
if (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED && reference == null) { if (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED && reference == null) {
throw new NullPointerException(String.valueOf(errorMessage)); throw new NullPointerException(String.valueOf(errorMessage));
} }
...@@ -133,7 +137,8 @@ public final class Assertions { ...@@ -133,7 +137,8 @@ public final class Assertions {
* @return The non-null, non-empty string that was validated. * @return The non-null, non-empty string that was validated.
* @throws IllegalArgumentException If {@code string} is null or 0-length. * @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)) { if (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED && TextUtils.isEmpty(string)) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
...@@ -149,7 +154,8 @@ public final class Assertions { ...@@ -149,7 +154,8 @@ public final class Assertions {
* @return The non-null, non-empty string that was validated. * @return The non-null, non-empty string that was validated.
* @throws IllegalArgumentException If {@code string} is null or 0-length. * @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)) { if (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED && TextUtils.isEmpty(string)) {
throw new IllegalArgumentException(String.valueOf(errorMessage)); throw new IllegalArgumentException(String.valueOf(errorMessage));
} }
......
...@@ -134,14 +134,13 @@ public final class MimeTypes { ...@@ -134,14 +134,13 @@ public final class MimeTypes {
return BASE_TYPE_APPLICATION.equals(getTopLevelType(mimeType)); return BASE_TYPE_APPLICATION.equals(getTopLevelType(mimeType));
} }
/** /**
* Derives a video sample mimeType from a codecs attribute. * Derives a video sample mimeType from a codecs attribute.
* *
* @param codecs The codecs attribute. * @param codecs The codecs attribute.
* @return The derived video mimeType, or null if it could not be derived. * @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) { if (codecs == null) {
return null; return null;
} }
...@@ -161,7 +160,7 @@ public final class MimeTypes { ...@@ -161,7 +160,7 @@ public final class MimeTypes {
* @param codecs The codecs attribute. * @param codecs The codecs attribute.
* @return The derived audio mimeType, or null if it could not be derived. * @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) { if (codecs == null) {
return null; return null;
} }
...@@ -181,7 +180,7 @@ public final class MimeTypes { ...@@ -181,7 +180,7 @@ public final class MimeTypes {
* @param codec The codec identifier to derive. * @param codec The codec identifier to derive.
* @return The mimeType, or null if it could not be derived. * @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) { if (codec == null) {
return null; return null;
} }
...@@ -345,7 +344,7 @@ public final class MimeTypes { ...@@ -345,7 +344,7 @@ public final class MimeTypes {
* @param mimeType The mimeType whose top-level type is required. * @param mimeType The mimeType whose top-level type is required.
* @return The top-level type, or null if the mimeType is null. * @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) { if (mimeType == null) {
return null; return null;
} }
......
...@@ -29,6 +29,7 @@ import android.net.Uri; ...@@ -29,6 +29,7 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Parcel; import android.os.Parcel;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.Display; import android.view.Display;
...@@ -190,7 +191,7 @@ public final class Util { ...@@ -190,7 +191,7 @@ public final class Util {
* @param o2 The second object. * @param o2 The second object.
* @return {@code o1 == null ? o2 == null : o1.equals(o2)}. * @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); return o1 == null ? o2 == null : o1.equals(o2);
} }
...@@ -225,6 +226,20 @@ public final class Util { ...@@ -225,6 +226,20 @@ public final class Util {
} }
/** /**
* 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> 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. * Instantiates a new single threaded executor whose thread has the specified name.
* *
* @param threadName The name of the thread. * @param threadName The name of the thread.
......
...@@ -34,6 +34,8 @@ android { ...@@ -34,6 +34,8 @@ android {
dependencies { dependencies {
implementation project(modulePrefix + 'library-core') 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 implementation 'com.android.support:support-annotations:' + supportLibraryVersion
testImplementation project(modulePrefix + 'testutils-robolectric') testImplementation project(modulePrefix + 'testutils-robolectric')
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.dash; package com.google.android.exoplayer2.source.dash;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmInitData;
...@@ -65,7 +66,7 @@ public final class DashUtil { ...@@ -65,7 +66,7 @@ public final class DashUtil {
* @throws IOException Thrown when there is an error while loading. * @throws IOException Thrown when there is an error while loading.
* @throws InterruptedException Thrown if the thread was interrupted. * @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 { throws IOException, InterruptedException {
int primaryTrackType = C.TRACK_TYPE_VIDEO; int primaryTrackType = C.TRACK_TYPE_VIDEO;
Representation representation = getFirstRepresentation(period, primaryTrackType); Representation representation = getFirstRepresentation(period, primaryTrackType);
...@@ -87,15 +88,16 @@ public final class DashUtil { ...@@ -87,15 +88,16 @@ public final class DashUtil {
* Loads initialization data for the {@code representation} and returns the sample {@link Format}. * 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 dataSource The source from which the data should be loaded.
* @param trackType The type of the representation. Typically one of the * @param trackType The type of the representation. Typically one of the {@link
* {@link com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. * com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
* @param representation The representation which initialization chunk belongs to. * @param representation The representation which initialization chunk belongs to.
* @return the sample {@link Format} of the given representation. * @return the sample {@link Format} of the given representation.
* @throws IOException Thrown when there is an error while loading. * @throws IOException Thrown when there is an error while loading.
* @throws InterruptedException Thrown if the thread was interrupted. * @throws InterruptedException Thrown if the thread was interrupted.
*/ */
public static Format loadSampleFormat(DataSource dataSource, int trackType, public static @Nullable Format loadSampleFormat(
Representation representation) throws IOException, InterruptedException { DataSource dataSource, int trackType, Representation representation)
throws IOException, InterruptedException {
ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType, ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType,
representation, false); representation, false);
return extractorWrapper == null ? null : extractorWrapper.getSampleFormats()[0]; return extractorWrapper == null ? null : extractorWrapper.getSampleFormats()[0];
...@@ -106,28 +108,29 @@ public final class DashUtil { ...@@ -106,28 +108,29 @@ public final class DashUtil {
* ChunkIndex}. * ChunkIndex}.
* *
* @param dataSource The source from which the data should be loaded. * @param dataSource The source from which the data should be loaded.
* @param trackType The type of the representation. Typically one of the * @param trackType The type of the representation. Typically one of the {@link
* {@link com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. * com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
* @param representation The representation which initialization chunk belongs to. * @param representation The representation which initialization chunk belongs to.
* @return The {@link ChunkIndex} of the given representation, or null if no initialization or * @return The {@link ChunkIndex} of the given representation, or null if no initialization or
* index data exists. * index data exists.
* @throws IOException Thrown when there is an error while loading. * @throws IOException Thrown when there is an error while loading.
* @throws InterruptedException Thrown if the thread was interrupted. * @throws InterruptedException Thrown if the thread was interrupted.
*/ */
public static ChunkIndex loadChunkIndex(DataSource dataSource, int trackType, public static @Nullable ChunkIndex loadChunkIndex(
Representation representation) throws IOException, InterruptedException { DataSource dataSource, int trackType, Representation representation)
throws IOException, InterruptedException {
ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType, ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType,
representation, true); representation, true);
return extractorWrapper == null ? null : (ChunkIndex) extractorWrapper.getSeekMap(); return extractorWrapper == null ? null : (ChunkIndex) extractorWrapper.getSeekMap();
} }
/** /**
* Loads initialization data for the {@code representation} and optionally index data then * Loads initialization data for the {@code representation} and optionally index data then returns
* returns a {@link ChunkExtractorWrapper} which contains the output. * a {@link ChunkExtractorWrapper} which contains the output.
* *
* @param dataSource The source from which the data should be loaded. * @param dataSource The source from which the data should be loaded.
* @param trackType The type of the representation. Typically one of the * @param trackType The type of the representation. Typically one of the {@link
* {@link com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. * com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
* @param representation The representation which initialization chunk belongs to. * @param representation The representation which initialization chunk belongs to.
* @param loadIndex Whether to load index data too. * @param loadIndex Whether to load index data too.
* @return A {@link ChunkExtractorWrapper} for the {@code representation}, or null if no * @return A {@link ChunkExtractorWrapper} for the {@code representation}, or null if no
...@@ -135,8 +138,9 @@ public final class DashUtil { ...@@ -135,8 +138,9 @@ public final class DashUtil {
* @throws IOException Thrown when there is an error while loading. * @throws IOException Thrown when there is an error while loading.
* @throws InterruptedException Thrown if the thread was interrupted. * @throws InterruptedException Thrown if the thread was interrupted.
*/ */
private static ChunkExtractorWrapper loadInitializationData(DataSource dataSource, int trackType, private static @Nullable ChunkExtractorWrapper loadInitializationData(
Representation representation, boolean loadIndex) throws IOException, InterruptedException { DataSource dataSource, int trackType, Representation representation, boolean loadIndex)
throws IOException, InterruptedException {
RangedUri initializationUri = representation.getInitializationUri(); RangedUri initializationUri = representation.getInitializationUri();
if (initializationUri == null) { if (initializationUri == null) {
return null; return null;
...@@ -181,7 +185,7 @@ public final class DashUtil { ...@@ -181,7 +185,7 @@ public final class DashUtil {
return new ChunkExtractorWrapper(extractor, trackType, format); 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); int index = period.getAdaptationSetIndex(type);
if (index == C.INDEX_UNSET) { if (index == C.INDEX_UNSET) {
return null; return null;
......
...@@ -45,8 +45,10 @@ import java.io.IOException; ...@@ -45,8 +45,10 @@ import java.io.IOException;
EventSampleStream(EventStream eventStream, Format upstreamFormat, boolean eventStreamUpdatable) { EventSampleStream(EventStream eventStream, Format upstreamFormat, boolean eventStreamUpdatable) {
this.upstreamFormat = upstreamFormat; this.upstreamFormat = upstreamFormat;
this.eventStream = eventStream;
eventMessageEncoder = new EventMessageEncoder(); eventMessageEncoder = new EventMessageEncoder();
pendingSeekPositionUs = C.TIME_UNSET; pendingSeekPositionUs = C.TIME_UNSET;
eventTimesUs = eventStream.presentationTimesUs;
updateEventStream(eventStream, eventStreamUpdatable); updateEventStream(eventStream, eventStreamUpdatable);
} }
......
...@@ -100,6 +100,7 @@ public final class PlayerEmsgHandler implements Handler.Callback { ...@@ -100,6 +100,7 @@ public final class PlayerEmsgHandler implements Handler.Callback {
* messages that generate DASH media source events. * messages that generate DASH media source events.
* @param allocator An {@link Allocator} from which allocations can be obtained. * @param allocator An {@link Allocator} from which allocations can be obtained.
*/ */
@SuppressWarnings("nullness")
public PlayerEmsgHandler( public PlayerEmsgHandler(
DashManifest manifest, PlayerEmsgCallback playerEmsgCallback, Allocator allocator) { DashManifest manifest, PlayerEmsgCallback playerEmsgCallback, Allocator allocator) {
this.manifest = manifest; this.manifest = manifest;
...@@ -237,11 +238,10 @@ public final class PlayerEmsgHandler implements Handler.Callback { ...@@ -237,11 +238,10 @@ public final class PlayerEmsgHandler implements Handler.Callback {
// Internal methods. // Internal methods.
private void handleManifestExpiredMessage(long eventTimeUs, long manifestPublishTimeMsInEmsg) { private void handleManifestExpiredMessage(long eventTimeUs, long manifestPublishTimeMsInEmsg) {
if (!manifestPublishTimeToExpiryTimeUs.containsKey(manifestPublishTimeMsInEmsg)) { Long previousExpiryTimeUs = manifestPublishTimeToExpiryTimeUs.get(manifestPublishTimeMsInEmsg);
if (previousExpiryTimeUs == null) {
manifestPublishTimeToExpiryTimeUs.put(manifestPublishTimeMsInEmsg, eventTimeUs); manifestPublishTimeToExpiryTimeUs.put(manifestPublishTimeMsInEmsg, eventTimeUs);
} else { } else {
long previousExpiryTimeUs =
manifestPublishTimeToExpiryTimeUs.get(manifestPublishTimeMsInEmsg);
if (previousExpiryTimeUs > eventTimeUs) { if (previousExpiryTimeUs > eventTimeUs) {
manifestPublishTimeToExpiryTimeUs.put(manifestPublishTimeMsInEmsg, eventTimeUs); manifestPublishTimeToExpiryTimeUs.put(manifestPublishTimeMsInEmsg, eventTimeUs);
} }
...@@ -253,10 +253,7 @@ public final class PlayerEmsgHandler implements Handler.Callback { ...@@ -253,10 +253,7 @@ public final class PlayerEmsgHandler implements Handler.Callback {
notifySourceMediaPresentationEnded(); notifySourceMediaPresentationEnded();
} }
private Map.Entry<Long, Long> ceilingExpiryEntryForPublishTime(long publishTimeMs) { private @Nullable Map.Entry<Long, Long> ceilingExpiryEntryForPublishTime(long publishTimeMs) {
if (manifestPublishTimeToExpiryTimeUs.isEmpty()) {
return null;
}
return manifestPublishTimeToExpiryTimeUs.ceilingEntry(publishTimeMs); return manifestPublishTimeToExpiryTimeUs.ceilingEntry(publishTimeMs);
} }
......
...@@ -49,7 +49,7 @@ public final class Descriptor { ...@@ -49,7 +49,7 @@ public final class Descriptor {
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(@Nullable Object obj) {
if (this == obj) { if (this == obj) {
return true; return true;
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.dash.manifest; package com.google.android.exoplayer2.source.dash.manifest;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.UriUtil; import com.google.android.exoplayer2.util.UriUtil;
...@@ -46,7 +47,7 @@ public final class RangedUri { ...@@ -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 * @param length The length of the range, or {@link C#LENGTH_UNSET} to indicate that the range is
* unbounded. * unbounded.
*/ */
public RangedUri(String referenceUri, long start, long length) { public RangedUri(@Nullable String referenceUri, long start, long length) {
this.referenceUri = referenceUri == null ? "" : referenceUri; this.referenceUri = referenceUri == null ? "" : referenceUri;
this.start = start; this.start = start;
this.length = length; this.length = length;
...@@ -74,18 +75,18 @@ public final class RangedUri { ...@@ -74,18 +75,18 @@ public final class RangedUri {
/** /**
* Attempts to merge this {@link RangedUri} with another and an optional common base uri. * Attempts to merge this {@link RangedUri} with another and an optional common base uri.
* <p> *
* A merge is successful if both instances define the same {@link Uri} after resolution with the * <p>A merge is successful if both instances define the same {@link Uri} after resolution with
* base uri, and if one starts the byte after the other ends, forming a contiguous region with * the base uri, and if one starts the byte after the other ends, forming a contiguous region with
* no overlap. * no overlap.
* <p> *
* If {@code other} is null then the merge is considered unsuccessful, and null is returned. * <p>If {@code other} is null then the merge is considered unsuccessful, and null is returned.
* *
* @param other The {@link RangedUri} to merge. * @param other The {@link RangedUri} to merge.
* @param baseUri The optional base Uri. * @param baseUri The optional base Uri.
* @return The merged {@link RangedUri} if the merge was successful. Null otherwise. * @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); final String resolvedUri = resolveUriString(baseUri);
if (other == null || !resolvedUri.equals(other.resolveUriString(baseUri))) { if (other == null || !resolvedUri.equals(other.resolveUriString(baseUri))) {
return null; return null;
...@@ -113,7 +114,7 @@ public final class RangedUri { ...@@ -113,7 +114,7 @@ public final class RangedUri {
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(@Nullable Object obj) {
if (this == obj) { if (this == obj) {
return true; return true;
} }
......
...@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.source.dash.manifest; ...@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.source.dash.manifest;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/** /**
* Uniquely identifies a {@link Representation} in a {@link DashManifest}. * Uniquely identifies a {@link Representation} in a {@link DashManifest}.
...@@ -81,7 +82,7 @@ public final class RepresentationKey implements Parcelable, Comparable<Represent ...@@ -81,7 +82,7 @@ public final class RepresentationKey implements Parcelable, Comparable<Represent
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
......
...@@ -151,7 +151,7 @@ public final class DashDownloader extends SegmentDownloader<DashManifest, Repres ...@@ -151,7 +151,7 @@ public final class DashDownloader extends SegmentDownloader<DashManifest, Repres
out.add(new Segment(startTimeUs, dataSpec)); out.add(new Segment(startTimeUs, dataSpec));
} }
private static DashSegmentIndex getSegmentIndex( private static @Nullable DashSegmentIndex getSegmentIndex(
DataSource dataSource, int trackType, Representation representation) DataSource dataSource, int trackType, Representation representation)
throws IOException, InterruptedException { throws IOException, InterruptedException {
DashSegmentIndex index = representation.getIndex(); DashSegmentIndex index = representation.getIndex();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment