diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/BaseMediaChunk.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/BaseMediaChunk.java index 74d8ddad3d..b508c1da1d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/BaseMediaChunk.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/BaseMediaChunk.java @@ -20,6 +20,8 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; +import com.google.android.exoplayer2.util.Assertions; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * A base implementation of {@link MediaChunk} that outputs to a {@link BaseMediaChunkOutput}. @@ -37,8 +39,8 @@ public abstract class BaseMediaChunk extends MediaChunk { */ public final long clippedEndTimeUs; - private BaseMediaChunkOutput output; - private int[] firstSampleIndices; + private @MonotonicNonNull BaseMediaChunkOutput output; + private int @MonotonicNonNull [] firstSampleIndices; /** * @param dataSource The source from which the data should be loaded. @@ -87,14 +89,14 @@ public abstract class BaseMediaChunk extends MediaChunk { * from this chunk. */ public final int getFirstSampleIndex(int trackIndex) { - return firstSampleIndices[trackIndex]; + return Assertions.checkStateNotNull(firstSampleIndices)[trackIndex]; } /** * Returns the output most recently passed to {@link #init(BaseMediaChunkOutput)}. */ protected final BaseMediaChunkOutput getOutput() { - return output; + return Assertions.checkStateNotNull(output); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/BaseMediaChunkOutput.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/BaseMediaChunkOutput.java index a23e506d08..50c37f8b31 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/BaseMediaChunkOutput.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/BaseMediaChunkOutput.java @@ -58,9 +58,7 @@ public final class BaseMediaChunkOutput implements TrackOutputProvider { public int[] getWriteIndices() { int[] writeIndices = new int[sampleQueues.length]; for (int i = 0; i < sampleQueues.length; i++) { - if (sampleQueues[i] != null) { - writeIndices[i] = sampleQueues[i].getWriteIndex(); - } + writeIndices[i] = sampleQueues[i].getWriteIndex(); } return writeIndices; } @@ -71,9 +69,7 @@ public final class BaseMediaChunkOutput implements TrackOutputProvider { */ public void setSampleOffsetUs(long sampleOffsetUs) { for (SampleQueue sampleQueue : sampleQueues) { - if (sampleQueue != null) { - sampleQueue.setSampleOffsetUs(sampleOffsetUs); - } + sampleQueue.setSampleOffsetUs(sampleOffsetUs); } } 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 a794f67fe2..f9e58e2353 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 @@ -42,10 +42,7 @@ public abstract class Chunk implements Loadable { * reporting only. */ public final int type; - /** - * The format of the track to which this chunk belongs, or null if the chunk does not belong to - * a track. - */ + /** The format of the track to which this chunk belongs. */ public final Format trackFormat; /** * One of the {@link C} {@code SELECTION_REASON_*} constants if the chunk belongs to a track. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkExtractorWrapper.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkExtractorWrapper.java index c4c8647a55..7fdc5d34ca 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkExtractorWrapper.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkExtractorWrapper.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.source.chunk; +import static com.google.android.exoplayer2.util.Util.castNonNull; + import android.util.SparseArray; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; @@ -28,6 +30,7 @@ import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.ParsableByteArray; import java.io.IOException; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * An {@link Extractor} wrapper for loading chunks that contain a single primary track, and possibly @@ -63,10 +66,10 @@ public final class ChunkExtractorWrapper implements ExtractorOutput { private final SparseArray bindingTrackOutputs; private boolean extractorInitialized; - private TrackOutputProvider trackOutputProvider; + @Nullable private TrackOutputProvider trackOutputProvider; private long endTimeUs; - private SeekMap seekMap; - private Format[] sampleFormats; + private @MonotonicNonNull SeekMap seekMap; + private Format @MonotonicNonNull [] sampleFormats; /** * @param extractor The extractor to wrap. @@ -84,15 +87,19 @@ public final class ChunkExtractorWrapper implements ExtractorOutput { } /** - * Returns the {@link SeekMap} most recently output by the extractor, or null. + * Returns the {@link SeekMap} most recently output by the extractor, or null if the extractor has + * not output a {@link SeekMap}. */ + @Nullable public SeekMap getSeekMap() { return seekMap; } /** - * Returns the sample {@link Format}s most recently output by the extractor, or null. + * Returns the sample {@link Format}s for the tracks identified by the extractor, or null if the + * extractor has not finished identifying tracks. */ + @Nullable public Format[] getSampleFormats() { return sampleFormats; } @@ -146,7 +153,7 @@ public final class ChunkExtractorWrapper implements ExtractorOutput { public void endTracks() { Format[] sampleFormats = new Format[bindingTrackOutputs.size()]; for (int i = 0; i < bindingTrackOutputs.size(); i++) { - sampleFormats[i] = bindingTrackOutputs.valueAt(i).sampleFormat; + sampleFormats[i] = Assertions.checkStateNotNull(bindingTrackOutputs.valueAt(i).sampleFormat); } this.sampleFormats = sampleFormats; } @@ -162,21 +169,21 @@ public final class ChunkExtractorWrapper implements ExtractorOutput { private final int id; private final int type; - private final Format manifestFormat; + @Nullable private final Format manifestFormat; private final DummyTrackOutput dummyTrackOutput; - public Format sampleFormat; - private TrackOutput trackOutput; + public @MonotonicNonNull Format sampleFormat; + private @MonotonicNonNull TrackOutput trackOutput; private long endTimeUs; - public BindingTrackOutput(int id, int type, Format manifestFormat) { + public BindingTrackOutput(int id, int type, @Nullable Format manifestFormat) { this.id = id; this.type = type; this.manifestFormat = manifestFormat; dummyTrackOutput = new DummyTrackOutput(); } - public void bind(TrackOutputProvider trackOutputProvider, long endTimeUs) { + public void bind(@Nullable TrackOutputProvider trackOutputProvider, long endTimeUs) { if (trackOutputProvider == null) { trackOutput = dummyTrackOutput; return; @@ -192,29 +199,31 @@ public final class ChunkExtractorWrapper implements ExtractorOutput { public void format(Format format) { sampleFormat = manifestFormat != null ? format.copyWithManifestFormatInfo(manifestFormat) : format; - trackOutput.format(sampleFormat); + castNonNull(trackOutput).format(sampleFormat); } @Override public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput) throws IOException, InterruptedException { - return trackOutput.sampleData(input, length, allowEndOfInput); + return castNonNull(trackOutput).sampleData(input, length, allowEndOfInput); } @Override public void sampleData(ParsableByteArray data, int length) { - trackOutput.sampleData(data, length); + castNonNull(trackOutput).sampleData(data, length); } @Override - public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset, - CryptoData cryptoData) { + public void sampleMetadata( + long timeUs, + @C.BufferFlags int flags, + int size, + int offset, + @Nullable CryptoData cryptoData) { if (endTimeUs != C.TIME_UNSET && timeUs >= endTimeUs) { trackOutput = dummyTrackOutput; } - trackOutput.sampleMetadata(timeUs, flags, size, offset, cryptoData); + castNonNull(trackOutput).sampleMetadata(timeUs, flags, size, offset, cryptoData); } - } - } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java index 5b71b01d49..bccbaf65a1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java @@ -38,6 +38,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * A {@link SampleStream} that loads media in {@link Chunk}s, obtained from a {@link ChunkSource}. @@ -61,8 +62,8 @@ public class ChunkSampleStream implements SampleStream, S public final int primaryTrackType; - @Nullable private final int[] embeddedTrackTypes; - @Nullable private final Format[] embeddedTrackFormats; + private final int[] embeddedTrackTypes; + private final Format[] embeddedTrackFormats; private final boolean[] embeddedTracksSelected; private final T chunkSource; private final SequenceableLoader.Callback> callback; @@ -76,7 +77,7 @@ public class ChunkSampleStream implements SampleStream, S private final SampleQueue[] embeddedSampleQueues; private final BaseMediaChunkOutput chunkOutput; - private Format primaryDownstreamTrackFormat; + private @MonotonicNonNull Format primaryDownstreamTrackFormat; @Nullable private ReleaseCallback releaseCallback; private long pendingResetPositionUs; private long lastSeekPositionUs; @@ -113,8 +114,8 @@ public class ChunkSampleStream implements SampleStream, S LoadErrorHandlingPolicy loadErrorHandlingPolicy, EventDispatcher eventDispatcher) { this.primaryTrackType = primaryTrackType; - this.embeddedTrackTypes = embeddedTrackTypes; - this.embeddedTrackFormats = embeddedTrackFormats; + this.embeddedTrackTypes = embeddedTrackTypes == null ? new int[0] : embeddedTrackTypes; + this.embeddedTrackFormats = embeddedTrackFormats == null ? new Format[0] : embeddedTrackFormats; this.chunkSource = chunkSource; this.callback = callback; this.eventDispatcher = eventDispatcher; @@ -124,7 +125,7 @@ public class ChunkSampleStream implements SampleStream, S mediaChunks = new ArrayList<>(); readOnlyMediaChunks = Collections.unmodifiableList(mediaChunks); - int embeddedTrackCount = embeddedTrackTypes == null ? 0 : embeddedTrackTypes.length; + int embeddedTrackCount = this.embeddedTrackTypes.length; embeddedSampleQueues = new SampleQueue[embeddedTrackCount]; embeddedTracksSelected = new boolean[embeddedTrackCount]; int[] trackTypes = new int[1 + embeddedTrackCount]; @@ -139,7 +140,7 @@ public class ChunkSampleStream implements SampleStream, S new SampleQueue(allocator, DrmSessionManager.getDummyDrmSessionManager()); embeddedSampleQueues[i] = sampleQueue; sampleQueues[i + 1] = sampleQueue; - trackTypes[i + 1] = embeddedTrackTypes[i]; + trackTypes[i + 1] = this.embeddedTrackTypes[i]; } chunkOutput = new BaseMediaChunkOutput(trackTypes, sampleQueues); @@ -251,7 +252,7 @@ public class ChunkSampleStream implements SampleStream, S } // Detect whether the seek is to the start of a chunk that's at least partially buffered. - BaseMediaChunk seekToMediaChunk = null; + @Nullable BaseMediaChunk seekToMediaChunk = null; for (int i = 0; i < mediaChunks.size(); i++) { BaseMediaChunk mediaChunk = mediaChunks.get(i); long mediaChunkStartTimeUs = mediaChunk.startTimeUs; @@ -463,7 +464,7 @@ public class ChunkSampleStream implements SampleStream, S ? loadErrorHandlingPolicy.getBlacklistDurationMsFor( loadable.type, loadDurationMs, error, errorCount) : C.TIME_UNSET; - LoadErrorAction loadErrorAction = null; + @Nullable LoadErrorAction loadErrorAction = null; if (chunkSource.onChunkLoadError(loadable, cancelable, error, blacklistDurationMs)) { if (cancelable) { loadErrorAction = Loader.DONT_RETRY; @@ -533,7 +534,7 @@ public class ChunkSampleStream implements SampleStream, S } chunkSource.getNextChunk(positionUs, loadPositionUs, chunkQueue, nextChunkHolder); boolean endOfStream = nextChunkHolder.endOfStream; - Chunk loadable = nextChunkHolder.chunk; + @Nullable Chunk loadable = nextChunkHolder.chunk; nextChunkHolder.clear(); if (endOfStream) { @@ -764,7 +765,7 @@ public class ChunkSampleStream implements SampleStream, S } @Override - public void maybeThrowError() throws IOException { + public void maybeThrowError() { // Do nothing. Errors will be thrown from the primary stream. } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/DataChunk.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/DataChunk.java index f3bea8aeb5..4c22532506 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/DataChunk.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/DataChunk.java @@ -52,16 +52,16 @@ public abstract class DataChunk extends Chunk { Format trackFormat, int trackSelectionReason, @Nullable Object trackSelectionData, - byte[] data) { + @Nullable byte[] data) { super(dataSource, dataSpec, type, trackFormat, trackSelectionReason, trackSelectionData, C.TIME_UNSET, C.TIME_UNSET); - this.data = data; + this.data = data == null ? Util.EMPTY_BYTE_ARRAY : data; } /** * Returns the array in which the data is held. - *

- * This method should be used for recycling the holder only, and not for reading the data. + * + *

This method should be used for recycling the holder only, and not for reading the data. * * @return The array in which the data is held. */ @@ -108,9 +108,7 @@ public abstract class DataChunk extends Chunk { protected abstract void consume(byte[] data, int limit) throws IOException; private void maybeExpandData(int limit) { - if (data == null) { - data = new byte[READ_GRANULARITY]; - } else if (data.length < limit + READ_GRANULARITY) { + if (data.length < limit + READ_GRANULARITY) { // The new length is calculated as (data.length + READ_GRANULARITY) rather than // (limit + READ_GRANULARITY) in order to avoid small increments in the length. data = Arrays.copyOf(data, data.length + READ_GRANULARITY); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/MediaChunkListIterator.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/MediaChunkListIterator.java deleted file mode 100644 index ca64e1affd..0000000000 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/MediaChunkListIterator.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer2.source.chunk; - -import com.google.android.exoplayer2.upstream.DataSpec; -import java.util.List; - -/** A {@link MediaChunkIterator} which iterates over a {@link List} of {@link MediaChunk}s. */ -public final class MediaChunkListIterator extends BaseMediaChunkIterator { - - private final List chunks; - private final boolean reverseOrder; - - /** - * Creates iterator. - * - * @param chunks The list of chunks to iterate over. - * @param reverseOrder Whether to iterate in reverse order. - */ - public MediaChunkListIterator(List chunks, boolean reverseOrder) { - super(0, chunks.size() - 1); - this.chunks = chunks; - this.reverseOrder = reverseOrder; - } - - @Override - public DataSpec getDataSpec() { - return getCurrentChunk().dataSpec; - } - - @Override - public long getChunkStartTimeUs() { - return getCurrentChunk().startTimeUs; - } - - @Override - public long getChunkEndTimeUs() { - return getCurrentChunk().endTimeUs; - } - - private MediaChunk getCurrentChunk() { - int index = (int) super.getCurrentIndex(); - if (reverseOrder) { - index = chunks.size() - 1 - index; - } - return chunks.get(index); - } -} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/package-info.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/package-info.java new file mode 100644 index 0000000000..c57494dc1c --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@NonNullApi +package com.google.android.exoplayer2.source.chunk; + +import com.google.android.exoplayer2.util.NonNullApi; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/chunk/MediaChunkListIteratorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/chunk/MediaChunkListIteratorTest.java deleted file mode 100644 index d2169d0a38..0000000000 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/chunk/MediaChunkListIteratorTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer2.source.chunk; - -import static com.google.common.truth.Truth.assertThat; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.testutil.FakeMediaChunk; -import java.util.Arrays; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** Tests for {@link MediaChunkListIterator}. */ -@RunWith(AndroidJUnit4.class) -public class MediaChunkListIteratorTest { - - private static final Format TEST_FORMAT = Format.createSampleFormat(null, null, 0); - - private FakeMediaChunk testChunk1; - private FakeMediaChunk testChunk2; - - @Before - public void setUp() { - testChunk1 = new FakeMediaChunk(TEST_FORMAT, 0, 10); - testChunk2 = new FakeMediaChunk(TEST_FORMAT, 10, 20); - } - - @Test - public void iterator_reverseOrderFalse_returnsItemsInNormalOrder() { - MediaChunkListIterator iterator = - new MediaChunkListIterator( - Arrays.asList(testChunk1, testChunk2), /* reverseOrder= */ false); - assertThat(iterator.isEnded()).isFalse(); - assertThat(iterator.next()).isTrue(); - assertEqual(iterator, testChunk1); - assertThat(iterator.next()).isTrue(); - assertEqual(iterator, testChunk2); - assertThat(iterator.next()).isFalse(); - assertThat(iterator.isEnded()).isTrue(); - } - - @Test - public void iterator_reverseOrderTrue_returnsItemsInReverseOrder() { - MediaChunkListIterator iterator = - new MediaChunkListIterator( - Arrays.asList(testChunk1, testChunk2), /* reverseOrder= */ true); - assertThat(iterator.isEnded()).isFalse(); - assertThat(iterator.next()).isTrue(); - assertEqual(iterator, testChunk2); - assertThat(iterator.next()).isTrue(); - assertEqual(iterator, testChunk1); - assertThat(iterator.next()).isFalse(); - assertThat(iterator.isEnded()).isTrue(); - } - - private static void assertEqual(MediaChunkListIterator iterator, FakeMediaChunk chunk) { - assertThat(iterator.getChunkStartTimeUs()).isEqualTo(chunk.startTimeUs); - assertThat(iterator.getChunkEndTimeUs()).isEqualTo(chunk.endTimeUs); - assertThat(iterator.getDataSpec()).isEqualTo(chunk.dataSpec); - } -} 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 c9433b9e41..74e4cb1dea 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 @@ -35,6 +35,7 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.ParsingLoadable; +import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MimeTypes; import java.io.IOException; import java.util.List; @@ -102,7 +103,9 @@ public final class DashUtil { throws IOException, InterruptedException { ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType, representation, false); - return extractorWrapper == null ? null : extractorWrapper.getSampleFormats()[0]; + return extractorWrapper == null + ? null + : Assertions.checkStateNotNull(extractorWrapper.getSampleFormats())[0]; } /**