Add non-null by default to source.chunk

PiperOrigin-RevId: 289859343
This commit is contained in:
olly 2020-01-15 15:58:57 +00:00 committed by Oliver Woodman
parent 4a5d788fa6
commit 090aed3aca
10 changed files with 77 additions and 189 deletions

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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.

View File

@ -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<BindingTrackOutput> 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);
}
}
}

View File

@ -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<T extends ChunkSource> 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<ChunkSampleStream<T>> callback;
@ -76,7 +77,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
private final SampleQueue[] embeddedSampleQueues;
private final BaseMediaChunkOutput chunkOutput;
private Format primaryDownstreamTrackFormat;
private @MonotonicNonNull Format primaryDownstreamTrackFormat;
@Nullable private ReleaseCallback<T> releaseCallback;
private long pendingResetPositionUs;
private long lastSeekPositionUs;
@ -113,8 +114,8 @@ public class ChunkSampleStream<T extends ChunkSource> 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<T extends ChunkSource> 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<T extends ChunkSource> 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<T extends ChunkSource> 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<T extends ChunkSource> 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<T extends ChunkSource> 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<T extends ChunkSource> implements SampleStream, S
}
@Override
public void maybeThrowError() throws IOException {
public void maybeThrowError() {
// Do nothing. Errors will be thrown from the primary stream.
}

View File

@ -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.
* <p>
* This method should be used for recycling the holder only, and not for reading the data.
*
* <p>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);

View File

@ -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<? extends MediaChunk> 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<? extends MediaChunk> 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);
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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];
}
/**