Fix nullness warning for MediaSource/MediaPeriod classes.

PiperOrigin-RevId: 249652301
This commit is contained in:
tonihei 2019-05-23 16:54:45 +01:00 committed by Toni
parent 14c46bc406
commit 3e990a3d24
10 changed files with 146 additions and 104 deletions

View File

@ -19,6 +19,7 @@ import android.util.Pair;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.util.Assertions;
/** /**
* Abstract base class for the concatenation of one or more {@link Timeline}s. * Abstract base class for the concatenation of one or more {@link Timeline}s.
@ -35,6 +36,7 @@ import com.google.android.exoplayer2.Timeline;
* @param concatenatedUid UID of a period in a concatenated timeline. * @param concatenatedUid UID of a period in a concatenated timeline.
* @return UID of the child timeline this period belongs to. * @return UID of the child timeline this period belongs to.
*/ */
@SuppressWarnings("nullness:return.type.incompatible")
public static Object getChildTimelineUidFromConcatenatedUid(Object concatenatedUid) { public static Object getChildTimelineUidFromConcatenatedUid(Object concatenatedUid) {
return ((Pair<?, ?>) concatenatedUid).first; return ((Pair<?, ?>) concatenatedUid).first;
} }
@ -45,6 +47,7 @@ import com.google.android.exoplayer2.Timeline;
* @param concatenatedUid UID of a period in a concatenated timeline. * @param concatenatedUid UID of a period in a concatenated timeline.
* @return UID of the period in the child timeline. * @return UID of the period in the child timeline.
*/ */
@SuppressWarnings("nullness:return.type.incompatible")
public static Object getChildPeriodUidFromConcatenatedUid(Object concatenatedUid) { public static Object getChildPeriodUidFromConcatenatedUid(Object concatenatedUid) {
return ((Pair<?, ?>) concatenatedUid).second; return ((Pair<?, ?>) concatenatedUid).second;
} }
@ -220,7 +223,9 @@ import com.google.android.exoplayer2.Timeline;
setIds); setIds);
period.windowIndex += firstWindowIndexInChild; period.windowIndex += firstWindowIndexInChild;
if (setIds) { if (setIds) {
period.uid = getConcatenatedUid(getChildUidByChildIndex(childIndex), period.uid); period.uid =
getConcatenatedUid(
getChildUidByChildIndex(childIndex), Assertions.checkNotNull(period.uid));
} }
return period; return period;
} }

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import androidx.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.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
@ -25,6 +26,7 @@ import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** /**
* Wraps a {@link MediaPeriod} and clips its {@link SampleStream}s to provide a subsequence of their * Wraps a {@link MediaPeriod} and clips its {@link SampleStream}s to provide a subsequence of their
@ -37,8 +39,8 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
*/ */
public final MediaPeriod mediaPeriod; public final MediaPeriod mediaPeriod;
private MediaPeriod.Callback callback; @Nullable private MediaPeriod.Callback callback;
private ClippingSampleStream[] sampleStreams; private @NullableType ClippingSampleStream[] sampleStreams;
private long pendingInitialDiscontinuityPositionUs; private long pendingInitialDiscontinuityPositionUs;
/* package */ long startUs; /* package */ long startUs;
/* package */ long endUs; /* package */ long endUs;
@ -95,10 +97,14 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
} }
@Override @Override
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags, public long selectTracks(
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) { @NullableType TrackSelection[] selections,
boolean[] mayRetainStreamFlags,
@NullableType SampleStream[] streams,
boolean[] streamResetFlags,
long positionUs) {
sampleStreams = new ClippingSampleStream[streams.length]; sampleStreams = new ClippingSampleStream[streams.length];
SampleStream[] childStreams = new SampleStream[streams.length]; @NullableType SampleStream[] childStreams = new SampleStream[streams.length];
for (int i = 0; i < streams.length; i++) { for (int i = 0; i < streams.length; i++) {
sampleStreams[i] = (ClippingSampleStream) streams[i]; sampleStreams[i] = (ClippingSampleStream) streams[i];
childStreams[i] = sampleStreams[i] != null ? sampleStreams[i].childStream : null; childStreams[i] = sampleStreams[i] != null ? sampleStreams[i].childStream : null;
@ -119,7 +125,7 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
for (int i = 0; i < streams.length; i++) { for (int i = 0; i < streams.length; i++) {
if (childStreams[i] == null) { if (childStreams[i] == null) {
sampleStreams[i] = null; sampleStreams[i] = null;
} else if (streams[i] == null || sampleStreams[i].childStream != childStreams[i]) { } else if (sampleStreams[i] == null || sampleStreams[i].childStream != childStreams[i]) {
sampleStreams[i] = new ClippingSampleStream(childStreams[i]); sampleStreams[i] = new ClippingSampleStream(childStreams[i]);
} }
streams[i] = sampleStreams[i]; streams[i] = sampleStreams[i];
@ -209,12 +215,12 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
@Override @Override
public void onPrepared(MediaPeriod mediaPeriod) { public void onPrepared(MediaPeriod mediaPeriod) {
callback.onPrepared(this); Assertions.checkNotNull(callback).onPrepared(this);
} }
@Override @Override
public void onContinueLoadingRequested(MediaPeriod source) { public void onContinueLoadingRequested(MediaPeriod source) {
callback.onContinueLoadingRequested(this); Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
} }
/* package */ boolean isPendingInitialDiscontinuity() { /* package */ boolean isPendingInitialDiscontinuity() {
@ -238,7 +244,8 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
} }
} }
private static boolean shouldKeepInitialDiscontinuity(long startUs, TrackSelection[] selections) { private static boolean shouldKeepInitialDiscontinuity(
long startUs, @NullableType TrackSelection[] selections) {
// If the clipping start position is non-zero, the clipping sample streams will adjust // If the clipping start position is non-zero, the clipping sample streams will adjust
// timestamps on buffers they read from the unclipped sample streams. These adjusted buffer // timestamps on buffers they read from the unclipped sample streams. These adjusted buffer
// timestamps can be negative, because sample streams provide buffers starting at a key-frame, // timestamps can be negative, because sample streams provide buffers starting at a key-frame,
@ -300,7 +307,7 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
} }
int result = childStream.readData(formatHolder, buffer, requireFormat); int result = childStream.readData(formatHolder, buffer, requireFormat);
if (result == C.RESULT_FORMAT_READ) { if (result == C.RESULT_FORMAT_READ) {
Format format = formatHolder.format; Format format = Assertions.checkNotNull(formatHolder.format);
if (format.encoderDelay != 0 || format.encoderPadding != 0) { if (format.encoderDelay != 0 || format.encoderPadding != 0) {
// Clear gapless playback metadata if the start/end points don't match the media. // Clear gapless playback metadata if the start/end points don't match the media.
int encoderDelay = startUs != 0 ? 0 : format.encoderDelay; int encoderDelay = startUs != 0 ? 0 : format.encoderDelay;
@ -328,7 +335,5 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
} }
return childStream.skipData(positionUs); return childStream.skipData(positionUs);
} }
} }
} }

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
@ -86,9 +87,9 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> {
private final ArrayList<ClippingMediaPeriod> mediaPeriods; private final ArrayList<ClippingMediaPeriod> mediaPeriods;
private final Timeline.Window window; private final Timeline.Window window;
private @Nullable Object manifest; @Nullable private Object manifest;
private ClippingTimeline clippingTimeline; @Nullable private ClippingTimeline clippingTimeline;
private IllegalClippingException clippingError; @Nullable private IllegalClippingException clippingError;
private long periodStartUs; private long periodStartUs;
private long periodEndUs; private long periodEndUs;
@ -222,7 +223,7 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> {
Assertions.checkState(mediaPeriods.remove(mediaPeriod)); Assertions.checkState(mediaPeriods.remove(mediaPeriod));
mediaSource.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod); mediaSource.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod);
if (mediaPeriods.isEmpty() && !allowDynamicClippingUpdates) { if (mediaPeriods.isEmpty() && !allowDynamicClippingUpdates) {
refreshClippedTimeline(clippingTimeline.timeline); refreshClippedTimeline(Assertions.checkNotNull(clippingTimeline).timeline);
} }
} }

View File

@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.SeekParameters;
@ -22,6 +24,7 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import java.io.IOException; import java.io.IOException;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** /**
* Media period that wraps a media source and defers calling its {@link * Media period that wraps a media source and defers calling its {@link
@ -47,10 +50,10 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
private final Allocator allocator; private final Allocator allocator;
private MediaPeriod mediaPeriod; @Nullable private MediaPeriod mediaPeriod;
private Callback callback; @Nullable private Callback callback;
private long preparePositionUs; private long preparePositionUs;
private @Nullable PrepareErrorListener listener; @Nullable private PrepareErrorListener listener;
private boolean notifiedPrepareError; private boolean notifiedPrepareError;
private long preparePositionOverrideUs; private long preparePositionOverrideUs;
@ -150,53 +153,57 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
@Override @Override
public TrackGroupArray getTrackGroups() { public TrackGroupArray getTrackGroups() {
return mediaPeriod.getTrackGroups(); return castNonNull(mediaPeriod).getTrackGroups();
} }
@Override @Override
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags, public long selectTracks(
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) { @NullableType TrackSelection[] selections,
boolean[] mayRetainStreamFlags,
@NullableType SampleStream[] streams,
boolean[] streamResetFlags,
long positionUs) {
if (preparePositionOverrideUs != C.TIME_UNSET && positionUs == preparePositionUs) { if (preparePositionOverrideUs != C.TIME_UNSET && positionUs == preparePositionUs) {
positionUs = preparePositionOverrideUs; positionUs = preparePositionOverrideUs;
preparePositionOverrideUs = C.TIME_UNSET; preparePositionOverrideUs = C.TIME_UNSET;
} }
return mediaPeriod.selectTracks(selections, mayRetainStreamFlags, streams, streamResetFlags, return castNonNull(mediaPeriod)
positionUs); .selectTracks(selections, mayRetainStreamFlags, streams, streamResetFlags, positionUs);
} }
@Override @Override
public void discardBuffer(long positionUs, boolean toKeyframe) { public void discardBuffer(long positionUs, boolean toKeyframe) {
mediaPeriod.discardBuffer(positionUs, toKeyframe); castNonNull(mediaPeriod).discardBuffer(positionUs, toKeyframe);
} }
@Override @Override
public long readDiscontinuity() { public long readDiscontinuity() {
return mediaPeriod.readDiscontinuity(); return castNonNull(mediaPeriod).readDiscontinuity();
} }
@Override @Override
public long getBufferedPositionUs() { public long getBufferedPositionUs() {
return mediaPeriod.getBufferedPositionUs(); return castNonNull(mediaPeriod).getBufferedPositionUs();
} }
@Override @Override
public long seekToUs(long positionUs) { public long seekToUs(long positionUs) {
return mediaPeriod.seekToUs(positionUs); return castNonNull(mediaPeriod).seekToUs(positionUs);
} }
@Override @Override
public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) { public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) {
return mediaPeriod.getAdjustedSeekPositionUs(positionUs, seekParameters); return castNonNull(mediaPeriod).getAdjustedSeekPositionUs(positionUs, seekParameters);
} }
@Override @Override
public long getNextLoadPositionUs() { public long getNextLoadPositionUs() {
return mediaPeriod.getNextLoadPositionUs(); return castNonNull(mediaPeriod).getNextLoadPositionUs();
} }
@Override @Override
public void reevaluateBuffer(long positionUs) { public void reevaluateBuffer(long positionUs) {
mediaPeriod.reevaluateBuffer(positionUs); castNonNull(mediaPeriod).reevaluateBuffer(positionUs);
} }
@Override @Override
@ -206,14 +213,14 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
@Override @Override
public void onContinueLoadingRequested(MediaPeriod source) { public void onContinueLoadingRequested(MediaPeriod source) {
callback.onContinueLoadingRequested(this); castNonNull(callback).onContinueLoadingRequested(this);
} }
// MediaPeriod.Callback implementation // MediaPeriod.Callback implementation
@Override @Override
public void onPrepared(MediaPeriod mediaPeriod) { public void onPrepared(MediaPeriod mediaPeriod) {
callback.onPrepared(this); castNonNull(callback).onPrepared(this);
} }
private long getPreparePositionWithOverride(long preparePositionUs) { private long getPreparePositionWithOverride(long preparePositionUs) {

View File

@ -242,8 +242,8 @@ public final class ExtractorMediaSource extends BaseMediaSource
Uri uri, Uri uri,
DataSource.Factory dataSourceFactory, DataSource.Factory dataSourceFactory,
ExtractorsFactory extractorsFactory, ExtractorsFactory extractorsFactory,
Handler eventHandler, @Nullable Handler eventHandler,
EventListener eventListener) { @Nullable EventListener eventListener) {
this(uri, dataSourceFactory, extractorsFactory, eventHandler, eventListener, null); this(uri, dataSourceFactory, extractorsFactory, eventHandler, eventListener, null);
} }
@ -264,9 +264,9 @@ public final class ExtractorMediaSource extends BaseMediaSource
Uri uri, Uri uri,
DataSource.Factory dataSourceFactory, DataSource.Factory dataSourceFactory,
ExtractorsFactory extractorsFactory, ExtractorsFactory extractorsFactory,
Handler eventHandler, @Nullable Handler eventHandler,
EventListener eventListener, @Nullable EventListener eventListener,
String customCacheKey) { @Nullable String customCacheKey) {
this( this(
uri, uri,
dataSourceFactory, dataSourceFactory,
@ -296,9 +296,9 @@ public final class ExtractorMediaSource extends BaseMediaSource
Uri uri, Uri uri,
DataSource.Factory dataSourceFactory, DataSource.Factory dataSourceFactory,
ExtractorsFactory extractorsFactory, ExtractorsFactory extractorsFactory,
Handler eventHandler, @Nullable Handler eventHandler,
EventListener eventListener, @Nullable EventListener eventListener,
String customCacheKey, @Nullable String customCacheKey,
int continueLoadingCheckIntervalBytes) { int continueLoadingCheckIntervalBytes) {
this( this(
uri, uri,

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
@ -23,6 +24,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** /**
* Merges multiple {@link MediaPeriod}s. * Merges multiple {@link MediaPeriod}s.
@ -35,9 +37,8 @@ import java.util.IdentityHashMap;
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private final ArrayList<MediaPeriod> childrenPendingPreparation; private final ArrayList<MediaPeriod> childrenPendingPreparation;
private Callback callback; @Nullable private Callback callback;
private TrackGroupArray trackGroups; @Nullable private TrackGroupArray trackGroups;
private MediaPeriod[] enabledPeriods; private MediaPeriod[] enabledPeriods;
private SequenceableLoader compositeSequenceableLoader; private SequenceableLoader compositeSequenceableLoader;
@ -49,6 +50,7 @@ import java.util.IdentityHashMap;
compositeSequenceableLoader = compositeSequenceableLoader =
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(); compositeSequenceableLoaderFactory.createCompositeSequenceableLoader();
streamPeriodIndices = new IdentityHashMap<>(); streamPeriodIndices = new IdentityHashMap<>();
enabledPeriods = new MediaPeriod[0];
} }
@Override @Override
@ -69,12 +71,16 @@ import java.util.IdentityHashMap;
@Override @Override
public TrackGroupArray getTrackGroups() { public TrackGroupArray getTrackGroups() {
return trackGroups; return Assertions.checkNotNull(trackGroups);
} }
@Override @Override
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags, public long selectTracks(
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) { @NullableType TrackSelection[] selections,
boolean[] mayRetainStreamFlags,
@NullableType SampleStream[] streams,
boolean[] streamResetFlags,
long positionUs) {
// Map each selection and stream onto a child period index. // Map each selection and stream onto a child period index.
int[] streamChildIndices = new int[selections.length]; int[] streamChildIndices = new int[selections.length];
int[] selectionChildIndices = new int[selections.length]; int[] selectionChildIndices = new int[selections.length];
@ -94,9 +100,9 @@ import java.util.IdentityHashMap;
} }
streamPeriodIndices.clear(); streamPeriodIndices.clear();
// Select tracks for each child, copying the resulting streams back into a new streams array. // Select tracks for each child, copying the resulting streams back into a new streams array.
SampleStream[] newStreams = new SampleStream[selections.length]; @NullableType SampleStream[] newStreams = new SampleStream[selections.length];
SampleStream[] childStreams = new SampleStream[selections.length]; @NullableType SampleStream[] childStreams = new SampleStream[selections.length];
TrackSelection[] childSelections = new TrackSelection[selections.length]; @NullableType TrackSelection[] childSelections = new TrackSelection[selections.length];
ArrayList<MediaPeriod> enabledPeriodsList = new ArrayList<>(periods.length); ArrayList<MediaPeriod> enabledPeriodsList = new ArrayList<>(periods.length);
for (int i = 0; i < periods.length; i++) { for (int i = 0; i < periods.length; i++) {
for (int j = 0; j < selections.length; j++) { for (int j = 0; j < selections.length; j++) {
@ -114,10 +120,10 @@ import java.util.IdentityHashMap;
for (int j = 0; j < selections.length; j++) { for (int j = 0; j < selections.length; j++) {
if (selectionChildIndices[j] == i) { if (selectionChildIndices[j] == i) {
// Assert that the child provided a stream for the selection. // Assert that the child provided a stream for the selection.
Assertions.checkState(childStreams[j] != null); SampleStream childStream = Assertions.checkNotNull(childStreams[j]);
newStreams[j] = childStreams[j]; newStreams[j] = childStreams[j];
periodEnabled = true; periodEnabled = true;
streamPeriodIndices.put(childStreams[j], i); streamPeriodIndices.put(childStream, i);
} else if (streamChildIndices[j] == i) { } else if (streamChildIndices[j] == i) {
// Assert that the child cleared any previous stream. // Assert that the child cleared any previous stream.
Assertions.checkState(childStreams[j] == null); Assertions.checkState(childStreams[j] == null);
@ -208,7 +214,8 @@ import java.util.IdentityHashMap;
@Override @Override
public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) { public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) {
return enabledPeriods[0].getAdjustedSeekPositionUs(positionUs, seekParameters); MediaPeriod queryPeriod = enabledPeriods.length > 0 ? enabledPeriods[0] : periods[0];
return queryPeriod.getAdjustedSeekPositionUs(positionUs, seekParameters);
} }
// MediaPeriod.Callback implementation // MediaPeriod.Callback implementation
@ -233,12 +240,12 @@ import java.util.IdentityHashMap;
} }
} }
trackGroups = new TrackGroupArray(trackGroupArray); trackGroups = new TrackGroupArray(trackGroupArray);
callback.onPrepared(this); Assertions.checkNotNull(callback).onPrepared(this);
} }
@Override @Override
public void onContinueLoadingRequested(MediaPeriod ignored) { public void onContinueLoadingRequested(MediaPeriod ignored) {
callback.onContinueLoadingRequested(this); Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
} }
} }

View File

@ -71,9 +71,9 @@ public final class MergingMediaSource extends CompositeMediaSource<Integer> {
private final ArrayList<MediaSource> pendingTimelineSources; private final ArrayList<MediaSource> pendingTimelineSources;
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private Object primaryManifest; @Nullable private Object primaryManifest;
private int periodCount; private int periodCount;
private IllegalMergeException mergeError; @Nullable private IllegalMergeException mergeError;
/** /**
* @param mediaSources The {@link MediaSource}s to merge. * @param mediaSources The {@link MediaSource}s to merge.
@ -170,11 +170,13 @@ public final class MergingMediaSource extends CompositeMediaSource<Integer> {
} }
@Override @Override
protected @Nullable MediaPeriodId getMediaPeriodIdForChildMediaPeriodId( @Nullable
protected MediaPeriodId getMediaPeriodIdForChildMediaPeriodId(
Integer id, MediaPeriodId mediaPeriodId) { Integer id, MediaPeriodId mediaPeriodId) {
return id == 0 ? mediaPeriodId : null; return id == 0 ? mediaPeriodId : null;
} }
@Nullable
private IllegalMergeException checkTimelineMerges(Timeline timeline) { private IllegalMergeException checkTimelineMerges(Timeline timeline) {
if (periodCount == PERIOD_COUNT_UNSET) { if (periodCount == PERIOD_COUNT_UNSET) {
periodCount = timeline.getPeriodCount(); periodCount = timeline.getPeriodCount();

View File

@ -31,11 +31,14 @@ import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.upstream.Loader.Loadable; import com.google.android.exoplayer2.upstream.Loader.Loadable;
import com.google.android.exoplayer2.upstream.StatsDataSource; import com.google.android.exoplayer2.upstream.StatsDataSource;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* A {@link MediaPeriod} with a single sample. * A {@link MediaPeriod} with a single sample.
@ -64,8 +67,7 @@ import java.util.Arrays;
/* package */ boolean notifiedReadingStarted; /* package */ boolean notifiedReadingStarted;
/* package */ boolean loadingFinished; /* package */ boolean loadingFinished;
/* package */ boolean loadingSucceeded; /* package */ byte @MonotonicNonNull [] sampleData;
/* package */ byte[] sampleData;
/* package */ int sampleSize; /* package */ int sampleSize;
public SingleSampleMediaPeriod( public SingleSampleMediaPeriod(
@ -112,8 +114,12 @@ import java.util.Arrays;
} }
@Override @Override
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags, public long selectTracks(
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) { @NullableType TrackSelection[] selections,
boolean[] mayRetainStreamFlags,
@NullableType SampleStream[] streams,
boolean[] streamResetFlags,
long positionUs) {
for (int i = 0; i < selections.length; i++) { for (int i = 0; i < selections.length; i++) {
if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) { if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
sampleStreams.remove(streams[i]); sampleStreams.remove(streams[i]);
@ -204,9 +210,8 @@ import java.util.Arrays;
public void onLoadCompleted(SourceLoadable loadable, long elapsedRealtimeMs, public void onLoadCompleted(SourceLoadable loadable, long elapsedRealtimeMs,
long loadDurationMs) { long loadDurationMs) {
sampleSize = (int) loadable.dataSource.getBytesRead(); sampleSize = (int) loadable.dataSource.getBytesRead();
sampleData = loadable.sampleData; sampleData = Assertions.checkNotNull(loadable.sampleData);
loadingFinished = true; loadingFinished = true;
loadingSucceeded = true;
eventDispatcher.loadCompleted( eventDispatcher.loadCompleted(
loadable.dataSpec, loadable.dataSpec,
loadable.dataSource.getLastOpenedUri(), loadable.dataSource.getLastOpenedUri(),
@ -325,7 +330,7 @@ import java.util.Arrays;
streamState = STREAM_STATE_SEND_SAMPLE; streamState = STREAM_STATE_SEND_SAMPLE;
return C.RESULT_FORMAT_READ; return C.RESULT_FORMAT_READ;
} else if (loadingFinished) { } else if (loadingFinished) {
if (loadingSucceeded) { if (sampleData != null) {
buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME); buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME);
buffer.timeUs = 0; buffer.timeUs = 0;
if (buffer.isFlagsOnly()) { if (buffer.isFlagsOnly()) {
@ -371,7 +376,7 @@ import java.util.Arrays;
private final StatsDataSource dataSource; private final StatsDataSource dataSource;
private byte[] sampleData; @Nullable private byte[] sampleData;
public SourceLoadable(DataSpec dataSpec, DataSource dataSource) { public SourceLoadable(DataSpec dataSpec, DataSource dataSource) {
this.dataSpec = dataSpec; this.dataSpec = dataSpec;

View File

@ -18,12 +18,15 @@ package com.google.android.exoplayer2.source.ads;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.CheckResult; import androidx.annotation.CheckResult;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.Arrays; import java.util.Arrays;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** /**
* Represents ad group times relative to the start of the media and information on the state and * Represents ad group times relative to the start of the media and information on the state and
@ -45,9 +48,9 @@ public final class AdPlaybackState {
/** The number of ads in the ad group, or {@link C#LENGTH_UNSET} if unknown. */ /** The number of ads in the ad group, or {@link C#LENGTH_UNSET} if unknown. */
public final int count; public final int count;
/** The URI of each ad in the ad group. */ /** The URI of each ad in the ad group. */
public final Uri[] uris; public final @NullableType Uri[] uris;
/** The state of each ad in the ad group. */ /** The state of each ad in the ad group. */
public final @AdState int[] states; @AdState public final int[] states;
/** The durations of each ad in the ad group, in microseconds. */ /** The durations of each ad in the ad group, in microseconds. */
public final long[] durationsUs; public final long[] durationsUs;
@ -60,7 +63,8 @@ public final class AdPlaybackState {
/* durationsUs= */ new long[0]); /* durationsUs= */ new long[0]);
} }
private AdGroup(int count, @AdState int[] states, Uri[] uris, long[] durationsUs) { private AdGroup(
int count, @AdState int[] states, @NullableType Uri[] uris, long[] durationsUs) {
Assertions.checkArgument(states.length == uris.length); Assertions.checkArgument(states.length == uris.length);
this.count = count; this.count = count;
this.states = states; this.states = states;
@ -98,7 +102,7 @@ public final class AdPlaybackState {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@ -130,7 +134,7 @@ public final class AdPlaybackState {
Assertions.checkArgument(this.count == C.LENGTH_UNSET && states.length <= count); Assertions.checkArgument(this.count == C.LENGTH_UNSET && states.length <= count);
@AdState int[] states = copyStatesWithSpaceForAdCount(this.states, count); @AdState int[] states = copyStatesWithSpaceForAdCount(this.states, count);
long[] durationsUs = copyDurationsUsWithSpaceForAdCount(this.durationsUs, count); long[] durationsUs = copyDurationsUsWithSpaceForAdCount(this.durationsUs, count);
Uri[] uris = Arrays.copyOf(this.uris, count); @NullableType Uri[] uris = Arrays.copyOf(this.uris, count);
return new AdGroup(count, states, uris, durationsUs); return new AdGroup(count, states, uris, durationsUs);
} }
@ -151,7 +155,7 @@ public final class AdPlaybackState {
this.durationsUs.length == states.length this.durationsUs.length == states.length
? this.durationsUs ? this.durationsUs
: copyDurationsUsWithSpaceForAdCount(this.durationsUs, states.length); : copyDurationsUsWithSpaceForAdCount(this.durationsUs, states.length);
Uri[] uris = Arrays.copyOf(this.uris, states.length); @NullableType Uri[] uris = Arrays.copyOf(this.uris, states.length);
uris[index] = uri; uris[index] = uri;
states[index] = AD_STATE_AVAILABLE; states[index] = AD_STATE_AVAILABLE;
return new AdGroup(count, states, uris, durationsUs); return new AdGroup(count, states, uris, durationsUs);
@ -177,6 +181,7 @@ public final class AdPlaybackState {
this.durationsUs.length == states.length this.durationsUs.length == states.length
? this.durationsUs ? this.durationsUs
: copyDurationsUsWithSpaceForAdCount(this.durationsUs, states.length); : copyDurationsUsWithSpaceForAdCount(this.durationsUs, states.length);
@NullableType
Uri[] uris = Uri[] uris =
this.uris.length == states.length ? this.uris : Arrays.copyOf(this.uris, states.length); this.uris.length == states.length ? this.uris : Arrays.copyOf(this.uris, states.length);
states[index] = state; states[index] = state;
@ -362,7 +367,7 @@ public final class AdPlaybackState {
if (adGroups[adGroupIndex].count == adCount) { if (adGroups[adGroupIndex].count == adCount) {
return this; return this;
} }
AdGroup[] adGroups = Arrays.copyOf(this.adGroups, this.adGroups.length); AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length);
adGroups[adGroupIndex] = this.adGroups[adGroupIndex].withAdCount(adCount); adGroups[adGroupIndex] = this.adGroups[adGroupIndex].withAdCount(adCount);
return new AdPlaybackState(adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs); return new AdPlaybackState(adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs);
} }
@ -370,7 +375,7 @@ public final class AdPlaybackState {
/** Returns an instance with the specified ad URI. */ /** Returns an instance with the specified ad URI. */
@CheckResult @CheckResult
public AdPlaybackState withAdUri(int adGroupIndex, int adIndexInAdGroup, Uri uri) { public AdPlaybackState withAdUri(int adGroupIndex, int adIndexInAdGroup, Uri uri) {
AdGroup[] adGroups = Arrays.copyOf(this.adGroups, this.adGroups.length); AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length);
adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdUri(uri, adIndexInAdGroup); adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdUri(uri, adIndexInAdGroup);
return new AdPlaybackState(adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs); return new AdPlaybackState(adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs);
} }
@ -378,7 +383,7 @@ public final class AdPlaybackState {
/** Returns an instance with the specified ad marked as played. */ /** Returns an instance with the specified ad marked as played. */
@CheckResult @CheckResult
public AdPlaybackState withPlayedAd(int adGroupIndex, int adIndexInAdGroup) { public AdPlaybackState withPlayedAd(int adGroupIndex, int adIndexInAdGroup) {
AdGroup[] adGroups = Arrays.copyOf(this.adGroups, this.adGroups.length); AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length);
adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdState(AD_STATE_PLAYED, adIndexInAdGroup); adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdState(AD_STATE_PLAYED, adIndexInAdGroup);
return new AdPlaybackState(adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs); return new AdPlaybackState(adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs);
} }
@ -386,7 +391,7 @@ public final class AdPlaybackState {
/** Returns an instance with the specified ad marked as skipped. */ /** Returns an instance with the specified ad marked as skipped. */
@CheckResult @CheckResult
public AdPlaybackState withSkippedAd(int adGroupIndex, int adIndexInAdGroup) { public AdPlaybackState withSkippedAd(int adGroupIndex, int adIndexInAdGroup) {
AdGroup[] adGroups = Arrays.copyOf(this.adGroups, this.adGroups.length); AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length);
adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdState(AD_STATE_SKIPPED, adIndexInAdGroup); adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdState(AD_STATE_SKIPPED, adIndexInAdGroup);
return new AdPlaybackState(adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs); return new AdPlaybackState(adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs);
} }
@ -394,7 +399,7 @@ public final class AdPlaybackState {
/** Returns an instance with the specified ad marked as having a load error. */ /** Returns an instance with the specified ad marked as having a load error. */
@CheckResult @CheckResult
public AdPlaybackState withAdLoadError(int adGroupIndex, int adIndexInAdGroup) { public AdPlaybackState withAdLoadError(int adGroupIndex, int adIndexInAdGroup) {
AdGroup[] adGroups = Arrays.copyOf(this.adGroups, this.adGroups.length); AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length);
adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdState(AD_STATE_ERROR, adIndexInAdGroup); adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdState(AD_STATE_ERROR, adIndexInAdGroup);
return new AdPlaybackState(adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs); return new AdPlaybackState(adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs);
} }
@ -405,7 +410,7 @@ public final class AdPlaybackState {
*/ */
@CheckResult @CheckResult
public AdPlaybackState withSkippedAdGroup(int adGroupIndex) { public AdPlaybackState withSkippedAdGroup(int adGroupIndex) {
AdGroup[] adGroups = Arrays.copyOf(this.adGroups, this.adGroups.length); AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length);
adGroups[adGroupIndex] = adGroups[adGroupIndex].withAllAdsSkipped(); adGroups[adGroupIndex] = adGroups[adGroupIndex].withAllAdsSkipped();
return new AdPlaybackState(adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs); return new AdPlaybackState(adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs);
} }
@ -413,7 +418,7 @@ public final class AdPlaybackState {
/** Returns an instance with the specified ad durations, in microseconds. */ /** Returns an instance with the specified ad durations, in microseconds. */
@CheckResult @CheckResult
public AdPlaybackState withAdDurationsUs(long[][] adDurationUs) { public AdPlaybackState withAdDurationsUs(long[][] adDurationUs) {
AdGroup[] adGroups = Arrays.copyOf(this.adGroups, this.adGroups.length); AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length);
for (int adGroupIndex = 0; adGroupIndex < adGroupCount; adGroupIndex++) { for (int adGroupIndex = 0; adGroupIndex < adGroupCount; adGroupIndex++) {
adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdDurationsUs(adDurationUs[adGroupIndex]); adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdDurationsUs(adDurationUs[adGroupIndex]);
} }
@ -441,7 +446,7 @@ public final class AdPlaybackState {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }

View File

@ -47,6 +47,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** /**
* A {@link MediaSource} that inserts ads linearly with a provided content media source. This source * A {@link MediaSource} that inserts ads linearly with a provided content media source. This source
@ -114,7 +115,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
*/ */
public RuntimeException getRuntimeExceptionForUnexpected() { public RuntimeException getRuntimeExceptionForUnexpected() {
Assertions.checkState(type == TYPE_UNEXPECTED); Assertions.checkState(type == TYPE_UNEXPECTED);
return (RuntimeException) getCause(); return (RuntimeException) Assertions.checkNotNull(getCause());
} }
} }
@ -131,12 +132,12 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
private final Timeline.Period period; private final Timeline.Period period;
// Accessed on the player thread. // Accessed on the player thread.
private ComponentListener componentListener; @Nullable private ComponentListener componentListener;
private Timeline contentTimeline; @Nullable private Timeline contentTimeline;
private Object contentManifest; @Nullable private Object contentManifest;
private AdPlaybackState adPlaybackState; @Nullable private AdPlaybackState adPlaybackState;
private MediaSource[][] adGroupMediaSources; private @NullableType MediaSource[][] adGroupMediaSources;
private Timeline[][] adGroupTimelines; private @NullableType Timeline[][] adGroupTimelines;
/** /**
* Constructs a new source that inserts ads linearly with the content specified by {@code * Constructs a new source that inserts ads linearly with the content specified by {@code
@ -202,24 +203,25 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
@Override @Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) { public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
AdPlaybackState adPlaybackState = Assertions.checkNotNull(this.adPlaybackState);
if (adPlaybackState.adGroupCount > 0 && id.isAd()) { if (adPlaybackState.adGroupCount > 0 && id.isAd()) {
int adGroupIndex = id.adGroupIndex; int adGroupIndex = id.adGroupIndex;
int adIndexInAdGroup = id.adIndexInAdGroup; int adIndexInAdGroup = id.adIndexInAdGroup;
Uri adUri = adPlaybackState.adGroups[adGroupIndex].uris[adIndexInAdGroup]; Uri adUri =
Assertions.checkNotNull(adPlaybackState.adGroups[adGroupIndex].uris[adIndexInAdGroup]);
if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) { if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) {
MediaSource adMediaSource = adMediaSourceFactory.createMediaSource(adUri); int adCount = adIndexInAdGroup + 1;
int oldAdCount = adGroupMediaSources[adGroupIndex].length; adGroupMediaSources[adGroupIndex] =
if (adIndexInAdGroup >= oldAdCount) { Arrays.copyOf(adGroupMediaSources[adGroupIndex], adCount);
int adCount = adIndexInAdGroup + 1; adGroupTimelines[adGroupIndex] = Arrays.copyOf(adGroupTimelines[adGroupIndex], adCount);
adGroupMediaSources[adGroupIndex] =
Arrays.copyOf(adGroupMediaSources[adGroupIndex], adCount);
adGroupTimelines[adGroupIndex] = Arrays.copyOf(adGroupTimelines[adGroupIndex], adCount);
}
adGroupMediaSources[adGroupIndex][adIndexInAdGroup] = adMediaSource;
deferredMediaPeriodByAdMediaSource.put(adMediaSource, new ArrayList<>());
prepareChildSource(id, adMediaSource);
} }
MediaSource mediaSource = adGroupMediaSources[adGroupIndex][adIndexInAdGroup]; MediaSource mediaSource = adGroupMediaSources[adGroupIndex][adIndexInAdGroup];
if (mediaSource == null) {
mediaSource = adMediaSourceFactory.createMediaSource(adUri);
adGroupMediaSources[adGroupIndex][adIndexInAdGroup] = mediaSource;
deferredMediaPeriodByAdMediaSource.put(mediaSource, new ArrayList<>());
prepareChildSource(id, mediaSource);
}
DeferredMediaPeriod deferredMediaPeriod = DeferredMediaPeriod deferredMediaPeriod =
new DeferredMediaPeriod(mediaSource, id, allocator, startPositionUs); new DeferredMediaPeriod(mediaSource, id, allocator, startPositionUs);
deferredMediaPeriod.setPrepareErrorListener( deferredMediaPeriod.setPrepareErrorListener(
@ -227,7 +229,8 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.get(mediaSource); List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.get(mediaSource);
if (mediaPeriods == null) { if (mediaPeriods == null) {
Object periodUid = Object periodUid =
adGroupTimelines[adGroupIndex][adIndexInAdGroup].getUidOfPeriod(/* periodIndex= */ 0); Assertions.checkNotNull(adGroupTimelines[adGroupIndex][adIndexInAdGroup])
.getUidOfPeriod(/* periodIndex= */ 0);
MediaPeriodId adSourceMediaPeriodId = new MediaPeriodId(periodUid, id.windowSequenceNumber); MediaPeriodId adSourceMediaPeriodId = new MediaPeriodId(periodUid, id.windowSequenceNumber);
deferredMediaPeriod.createPeriod(adSourceMediaPeriodId); deferredMediaPeriod.createPeriod(adSourceMediaPeriodId);
} else { } else {
@ -258,7 +261,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
@Override @Override
public void releaseSourceInternal() { public void releaseSourceInternal() {
super.releaseSourceInternal(); super.releaseSourceInternal();
componentListener.release(); Assertions.checkNotNull(componentListener).release();
componentListener = null; componentListener = null;
deferredMediaPeriodByAdMediaSource.clear(); deferredMediaPeriodByAdMediaSource.clear();
contentTimeline = null; contentTimeline = null;
@ -305,7 +308,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
maybeUpdateSourceInfo(); maybeUpdateSourceInfo();
} }
private void onContentSourceInfoRefreshed(Timeline timeline, Object manifest) { private void onContentSourceInfoRefreshed(Timeline timeline, @Nullable Object manifest) {
Assertions.checkArgument(timeline.getPeriodCount() == 1); Assertions.checkArgument(timeline.getPeriodCount() == 1);
contentTimeline = timeline; contentTimeline = timeline;
contentManifest = manifest; contentManifest = manifest;
@ -330,6 +333,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
} }
private void maybeUpdateSourceInfo() { private void maybeUpdateSourceInfo() {
Timeline contentTimeline = this.contentTimeline;
if (adPlaybackState != null && contentTimeline != null) { if (adPlaybackState != null && contentTimeline != null) {
adPlaybackState = adPlaybackState.withAdDurationsUs(getAdDurations(adGroupTimelines, period)); adPlaybackState = adPlaybackState.withAdDurationsUs(getAdDurations(adGroupTimelines, period));
Timeline timeline = Timeline timeline =
@ -340,7 +344,8 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
} }
} }
private static long[][] getAdDurations(Timeline[][] adTimelines, Timeline.Period period) { private static long[][] getAdDurations(
@NullableType Timeline[][] adTimelines, Timeline.Period period) {
long[][] adDurations = new long[adTimelines.length][]; long[][] adDurations = new long[adTimelines.length][];
for (int i = 0; i < adTimelines.length; i++) { for (int i = 0; i < adTimelines.length; i++) {
adDurations[i] = new long[adTimelines[i].length]; adDurations[i] = new long[adTimelines[i].length];