mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Fix NPE in ExtractorMediaPeriod
Also turn on nullity checks for ExtractorMediaPeriod. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=208467644
This commit is contained in:
parent
1c09af02f7
commit
32035e1bf3
3 changed files with 111 additions and 44 deletions
|
|
@ -49,6 +49,7 @@ import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link MediaPeriod} that extracts data using an {@link Extractor}.
|
* A {@link MediaPeriod} that extracts data using an {@link Extractor}.
|
||||||
|
|
@ -84,7 +85,7 @@ import java.util.Arrays;
|
||||||
private final EventDispatcher eventDispatcher;
|
private final EventDispatcher eventDispatcher;
|
||||||
private final Listener listener;
|
private final Listener listener;
|
||||||
private final Allocator allocator;
|
private final Allocator allocator;
|
||||||
@Nullable private final String customCacheKey;
|
private final @Nullable String customCacheKey;
|
||||||
private final long continueLoadingCheckIntervalBytes;
|
private final long continueLoadingCheckIntervalBytes;
|
||||||
private final Loader loader;
|
private final Loader loader;
|
||||||
private final ExtractorHolder extractorHolder;
|
private final ExtractorHolder extractorHolder;
|
||||||
|
|
@ -94,23 +95,21 @@ import java.util.Arrays;
|
||||||
private final Handler handler;
|
private final Handler handler;
|
||||||
|
|
||||||
private @Nullable Callback callback;
|
private @Nullable Callback callback;
|
||||||
private SeekMap seekMap;
|
private @Nullable SeekMap seekMap;
|
||||||
private SampleQueue[] sampleQueues;
|
private SampleQueue[] sampleQueues;
|
||||||
private int[] sampleQueueTrackIds;
|
private int[] sampleQueueTrackIds;
|
||||||
private boolean sampleQueuesBuilt;
|
private boolean sampleQueuesBuilt;
|
||||||
private boolean prepared;
|
private boolean prepared;
|
||||||
|
|
||||||
|
private @Nullable PreparedState preparedState;
|
||||||
|
private boolean haveAudioVideoTracks;
|
||||||
private int dataType;
|
private int dataType;
|
||||||
|
|
||||||
private boolean seenFirstTrackSelection;
|
private boolean seenFirstTrackSelection;
|
||||||
private boolean notifyDiscontinuity;
|
private boolean notifyDiscontinuity;
|
||||||
private boolean notifiedReadingStarted;
|
private boolean notifiedReadingStarted;
|
||||||
private int enabledTrackCount;
|
private int enabledTrackCount;
|
||||||
private TrackGroupArray tracks;
|
|
||||||
private long durationUs;
|
private long durationUs;
|
||||||
private boolean[] trackEnabledStates;
|
|
||||||
private boolean[] trackIsAudioVideoFlags;
|
|
||||||
private boolean[] trackFormatNotificationSent;
|
|
||||||
private boolean haveAudioVideoTracks;
|
|
||||||
private long length;
|
private long length;
|
||||||
|
|
||||||
private long lastSeekPositionUs;
|
private long lastSeekPositionUs;
|
||||||
|
|
@ -134,6 +133,8 @@ import java.util.Arrays;
|
||||||
* @param continueLoadingCheckIntervalBytes The number of bytes that should be loaded between each
|
* @param continueLoadingCheckIntervalBytes The number of bytes that should be loaded between each
|
||||||
* invocation of {@link Callback#onContinueLoadingRequested(SequenceableLoader)}.
|
* invocation of {@link Callback#onContinueLoadingRequested(SequenceableLoader)}.
|
||||||
*/
|
*/
|
||||||
|
// maybeFinishPrepare is not posted to the handler until initialization completes.
|
||||||
|
@SuppressWarnings("nullness:methodref.receiver.bound.invalid")
|
||||||
public ExtractorMediaPeriod(
|
public ExtractorMediaPeriod(
|
||||||
Uri uri,
|
Uri uri,
|
||||||
DataSource dataSource,
|
DataSource dataSource,
|
||||||
|
|
@ -153,11 +154,15 @@ import java.util.Arrays;
|
||||||
this.customCacheKey = customCacheKey;
|
this.customCacheKey = customCacheKey;
|
||||||
this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes;
|
this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes;
|
||||||
loader = new Loader("Loader:ExtractorMediaPeriod");
|
loader = new Loader("Loader:ExtractorMediaPeriod");
|
||||||
extractorHolder = new ExtractorHolder(extractors, this);
|
extractorHolder = new ExtractorHolder(extractors);
|
||||||
loadCondition = new ConditionVariable();
|
loadCondition = new ConditionVariable();
|
||||||
maybeFinishPrepareRunnable = this::maybeFinishPrepare;
|
maybeFinishPrepareRunnable = this::maybeFinishPrepare;
|
||||||
onContinueLoadingRequestedRunnable =
|
onContinueLoadingRequestedRunnable =
|
||||||
() -> callback.onContinueLoadingRequested(ExtractorMediaPeriod.this);
|
() -> {
|
||||||
|
if (!released) {
|
||||||
|
Assertions.checkNotNull(callback).onContinueLoadingRequested(ExtractorMediaPeriod.this);
|
||||||
|
}
|
||||||
|
};
|
||||||
handler = new Handler();
|
handler = new Handler();
|
||||||
sampleQueueTrackIds = new int[0];
|
sampleQueueTrackIds = new int[0];
|
||||||
sampleQueues = new SampleQueue[0];
|
sampleQueues = new SampleQueue[0];
|
||||||
|
|
@ -176,7 +181,7 @@ import java.util.Arrays;
|
||||||
sampleQueue.discardToEnd();
|
sampleQueue.discardToEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loader.release(this);
|
loader.release(/* callback= */ this);
|
||||||
handler.removeCallbacksAndMessages(null);
|
handler.removeCallbacksAndMessages(null);
|
||||||
callback = null;
|
callback = null;
|
||||||
released = true;
|
released = true;
|
||||||
|
|
@ -205,13 +210,19 @@ import java.util.Arrays;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackGroupArray getTrackGroups() {
|
public TrackGroupArray getTrackGroups() {
|
||||||
return tracks;
|
return getPreparedState().tracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
|
public long selectTracks(
|
||||||
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
|
TrackSelection[] selections,
|
||||||
Assertions.checkState(prepared);
|
boolean[] mayRetainStreamFlags,
|
||||||
|
@NullableType SampleStream[] streams,
|
||||||
|
boolean[] streamResetFlags,
|
||||||
|
long positionUs) {
|
||||||
|
PreparedState preparedState = getPreparedState();
|
||||||
|
TrackGroupArray tracks = preparedState.tracks;
|
||||||
|
boolean[] trackEnabledStates = preparedState.trackEnabledStates;
|
||||||
int oldEnabledTrackCount = enabledTrackCount;
|
int oldEnabledTrackCount = enabledTrackCount;
|
||||||
// Deselect old tracks.
|
// Deselect old tracks.
|
||||||
for (int i = 0; i < selections.length; i++) {
|
for (int i = 0; i < selections.length; i++) {
|
||||||
|
|
@ -280,6 +291,7 @@ import java.util.Arrays;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void discardBuffer(long positionUs, boolean toKeyframe) {
|
public void discardBuffer(long positionUs, boolean toKeyframe) {
|
||||||
|
boolean[] trackEnabledStates = getPreparedState().trackEnabledStates;
|
||||||
int trackCount = sampleQueues.length;
|
int trackCount = sampleQueues.length;
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
sampleQueues[i].discardTo(positionUs, toKeyframe, trackEnabledStates[i]);
|
sampleQueues[i].discardTo(positionUs, toKeyframe, trackEnabledStates[i]);
|
||||||
|
|
@ -325,6 +337,7 @@ import java.util.Arrays;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getBufferedPositionUs() {
|
public long getBufferedPositionUs() {
|
||||||
|
boolean[] trackIsAudioVideoFlags = getPreparedState().trackIsAudioVideoFlags;
|
||||||
if (loadingFinished) {
|
if (loadingFinished) {
|
||||||
return C.TIME_END_OF_SOURCE;
|
return C.TIME_END_OF_SOURCE;
|
||||||
} else if (isPendingReset()) {
|
} else if (isPendingReset()) {
|
||||||
|
|
@ -350,12 +363,15 @@ import java.util.Arrays;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long seekToUs(long positionUs) {
|
public long seekToUs(long positionUs) {
|
||||||
|
PreparedState preparedState = getPreparedState();
|
||||||
|
SeekMap seekMap = preparedState.seekMap;
|
||||||
|
boolean[] trackIsAudioVideoFlags = preparedState.trackIsAudioVideoFlags;
|
||||||
// Treat all seeks into non-seekable media as being to t=0.
|
// Treat all seeks into non-seekable media as being to t=0.
|
||||||
positionUs = seekMap.isSeekable() ? positionUs : 0;
|
positionUs = seekMap.isSeekable() ? positionUs : 0;
|
||||||
lastSeekPositionUs = positionUs;
|
lastSeekPositionUs = positionUs;
|
||||||
notifyDiscontinuity = false;
|
notifyDiscontinuity = false;
|
||||||
// If we're not pending a reset, see if we can seek within the buffer.
|
// If we're not pending a reset, see if we can seek within the buffer.
|
||||||
if (!isPendingReset() && seekInsideBufferUs(positionUs)) {
|
if (!isPendingReset() && seekInsideBufferUs(trackIsAudioVideoFlags, positionUs)) {
|
||||||
return positionUs;
|
return positionUs;
|
||||||
}
|
}
|
||||||
// We were unable to seek within the buffer, so need to reset.
|
// We were unable to seek within the buffer, so need to reset.
|
||||||
|
|
@ -374,6 +390,7 @@ import java.util.Arrays;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) {
|
public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) {
|
||||||
|
SeekMap seekMap = getPreparedState().seekMap;
|
||||||
if (!seekMap.isSeekable()) {
|
if (!seekMap.isSeekable()) {
|
||||||
// Treat all seeks into non-seekable media as being to t=0.
|
// Treat all seeks into non-seekable media as being to t=0.
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -432,6 +449,9 @@ import java.util.Arrays;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeNotifyTrackFormat(int track) {
|
private void maybeNotifyTrackFormat(int track) {
|
||||||
|
PreparedState preparedState = getPreparedState();
|
||||||
|
boolean[] trackFormatNotificationSent = preparedState.trackFormatNotificationSent;
|
||||||
|
TrackGroupArray tracks = preparedState.tracks;
|
||||||
if (!trackFormatNotificationSent[track]) {
|
if (!trackFormatNotificationSent[track]) {
|
||||||
Format trackFormat = tracks.get(track).getFormat(0);
|
Format trackFormat = tracks.get(track).getFormat(0);
|
||||||
eventDispatcher.downstreamFormatChanged(
|
eventDispatcher.downstreamFormatChanged(
|
||||||
|
|
@ -445,6 +465,7 @@ import java.util.Arrays;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeStartDeferredRetry(int track) {
|
private void maybeStartDeferredRetry(int track) {
|
||||||
|
boolean[] trackIsAudioVideoFlags = getPreparedState().trackIsAudioVideoFlags;
|
||||||
if (!pendingDeferredRetry
|
if (!pendingDeferredRetry
|
||||||
|| !trackIsAudioVideoFlags[track]
|
|| !trackIsAudioVideoFlags[track]
|
||||||
|| sampleQueues[track].hasNextSample()) {
|
|| sampleQueues[track].hasNextSample()) {
|
||||||
|
|
@ -458,7 +479,7 @@ import java.util.Arrays;
|
||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (SampleQueue sampleQueue : sampleQueues) {
|
||||||
sampleQueue.reset();
|
sampleQueue.reset();
|
||||||
}
|
}
|
||||||
callback.onContinueLoadingRequested(this);
|
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean suppressRead() {
|
private boolean suppressRead() {
|
||||||
|
|
@ -471,6 +492,7 @@ import java.util.Arrays;
|
||||||
public void onLoadCompleted(ExtractingLoadable loadable, long elapsedRealtimeMs,
|
public void onLoadCompleted(ExtractingLoadable loadable, long elapsedRealtimeMs,
|
||||||
long loadDurationMs) {
|
long loadDurationMs) {
|
||||||
if (durationUs == C.TIME_UNSET) {
|
if (durationUs == C.TIME_UNSET) {
|
||||||
|
SeekMap seekMap = Assertions.checkNotNull(this.seekMap);
|
||||||
long largestQueuedTimestampUs = getLargestQueuedTimestampUs();
|
long largestQueuedTimestampUs = getLargestQueuedTimestampUs();
|
||||||
durationUs = largestQueuedTimestampUs == Long.MIN_VALUE ? 0
|
durationUs = largestQueuedTimestampUs == Long.MIN_VALUE ? 0
|
||||||
: largestQueuedTimestampUs + DEFAULT_LAST_SAMPLE_DURATION_US;
|
: largestQueuedTimestampUs + DEFAULT_LAST_SAMPLE_DURATION_US;
|
||||||
|
|
@ -491,7 +513,7 @@ import java.util.Arrays;
|
||||||
loadable.dataSource.getBytesRead());
|
loadable.dataSource.getBytesRead());
|
||||||
copyLengthFromLoader(loadable);
|
copyLengthFromLoader(loadable);
|
||||||
loadingFinished = true;
|
loadingFinished = true;
|
||||||
callback.onContinueLoadingRequested(this);
|
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -516,7 +538,7 @@ import java.util.Arrays;
|
||||||
sampleQueue.reset();
|
sampleQueue.reset();
|
||||||
}
|
}
|
||||||
if (enabledTrackCount > 0) {
|
if (enabledTrackCount > 0) {
|
||||||
callback.onContinueLoadingRequested(this);
|
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -575,8 +597,9 @@ import java.util.Arrays;
|
||||||
trackOutput.setUpstreamFormatChangeListener(this);
|
trackOutput.setUpstreamFormatChangeListener(this);
|
||||||
sampleQueueTrackIds = Arrays.copyOf(sampleQueueTrackIds, trackCount + 1);
|
sampleQueueTrackIds = Arrays.copyOf(sampleQueueTrackIds, trackCount + 1);
|
||||||
sampleQueueTrackIds[trackCount] = id;
|
sampleQueueTrackIds[trackCount] = id;
|
||||||
sampleQueues = Arrays.copyOf(sampleQueues, trackCount + 1);
|
@NullableType SampleQueue[] sampleQueues = Arrays.copyOf(this.sampleQueues, trackCount + 1);
|
||||||
sampleQueues[trackCount] = trackOutput;
|
sampleQueues[trackCount] = trackOutput;
|
||||||
|
this.sampleQueues = Util.castNonNullTypeArray(sampleQueues);
|
||||||
return trackOutput;
|
return trackOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -602,7 +625,8 @@ import java.util.Arrays;
|
||||||
// Internal methods.
|
// Internal methods.
|
||||||
|
|
||||||
private void maybeFinishPrepare() {
|
private void maybeFinishPrepare() {
|
||||||
if (released || prepared || seekMap == null || !sampleQueuesBuilt) {
|
SeekMap seekMap = this.seekMap;
|
||||||
|
if (released || prepared || !sampleQueuesBuilt || seekMap == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (SampleQueue sampleQueue : sampleQueues) {
|
||||||
|
|
@ -613,9 +637,7 @@ import java.util.Arrays;
|
||||||
loadCondition.close();
|
loadCondition.close();
|
||||||
int trackCount = sampleQueues.length;
|
int trackCount = sampleQueues.length;
|
||||||
TrackGroup[] trackArray = new TrackGroup[trackCount];
|
TrackGroup[] trackArray = new TrackGroup[trackCount];
|
||||||
trackIsAudioVideoFlags = new boolean[trackCount];
|
boolean[] trackIsAudioVideoFlags = new boolean[trackCount];
|
||||||
trackEnabledStates = new boolean[trackCount];
|
|
||||||
trackFormatNotificationSent = new boolean[trackCount];
|
|
||||||
durationUs = seekMap.getDurationUs();
|
durationUs = seekMap.getDurationUs();
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
Format trackFormat = sampleQueues[i].getUpstreamFormat();
|
Format trackFormat = sampleQueues[i].getUpstreamFormat();
|
||||||
|
|
@ -625,14 +647,24 @@ import java.util.Arrays;
|
||||||
trackIsAudioVideoFlags[i] = isAudioVideo;
|
trackIsAudioVideoFlags[i] = isAudioVideo;
|
||||||
haveAudioVideoTracks |= isAudioVideo;
|
haveAudioVideoTracks |= isAudioVideo;
|
||||||
}
|
}
|
||||||
tracks = new TrackGroupArray(trackArray);
|
|
||||||
dataType =
|
dataType =
|
||||||
length == C.LENGTH_UNSET && seekMap.getDurationUs() == C.TIME_UNSET
|
length == C.LENGTH_UNSET && seekMap.getDurationUs() == C.TIME_UNSET
|
||||||
? C.DATA_TYPE_MEDIA_PROGRESSIVE_LIVE
|
? C.DATA_TYPE_MEDIA_PROGRESSIVE_LIVE
|
||||||
: C.DATA_TYPE_MEDIA;
|
: C.DATA_TYPE_MEDIA;
|
||||||
|
preparedState =
|
||||||
|
new PreparedState(
|
||||||
|
new TrackGroupArray(trackArray),
|
||||||
|
/* trackEnabledStates= */ new boolean[trackCount],
|
||||||
|
trackIsAudioVideoFlags,
|
||||||
|
/* trackFormatNotificationSent= */ new boolean[trackCount],
|
||||||
|
seekMap);
|
||||||
prepared = true;
|
prepared = true;
|
||||||
listener.onSourceInfoRefreshed(durationUs, seekMap.isSeekable());
|
listener.onSourceInfoRefreshed(durationUs, seekMap.isSeekable());
|
||||||
callback.onPrepared(this);
|
Assertions.checkNotNull(callback).onPrepared(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PreparedState getPreparedState() {
|
||||||
|
return Assertions.checkNotNull(preparedState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyLengthFromLoader(ExtractingLoadable loadable) {
|
private void copyLengthFromLoader(ExtractingLoadable loadable) {
|
||||||
|
|
@ -643,8 +675,10 @@ import java.util.Arrays;
|
||||||
|
|
||||||
private void startLoading() {
|
private void startLoading() {
|
||||||
ExtractingLoadable loadable =
|
ExtractingLoadable loadable =
|
||||||
new ExtractingLoadable(uri, dataSource, extractorHolder, loadCondition);
|
new ExtractingLoadable(
|
||||||
|
uri, dataSource, extractorHolder, /* extractorOutput= */ this, loadCondition);
|
||||||
if (prepared) {
|
if (prepared) {
|
||||||
|
SeekMap seekMap = getPreparedState().seekMap;
|
||||||
Assertions.checkState(isPendingReset());
|
Assertions.checkState(isPendingReset());
|
||||||
if (durationUs != C.TIME_UNSET && pendingResetPositionUs >= durationUs) {
|
if (durationUs != C.TIME_UNSET && pendingResetPositionUs >= durationUs) {
|
||||||
loadingFinished = true;
|
loadingFinished = true;
|
||||||
|
|
@ -719,10 +753,11 @@ import java.util.Arrays;
|
||||||
/**
|
/**
|
||||||
* Attempts to seek to the specified position within the sample queues.
|
* Attempts to seek to the specified position within the sample queues.
|
||||||
*
|
*
|
||||||
|
* @param trackIsAudioVideoFlags Whether each track is audio/video.
|
||||||
* @param positionUs The seek position in microseconds.
|
* @param positionUs The seek position in microseconds.
|
||||||
* @return Whether the in-buffer seek was successful.
|
* @return Whether the in-buffer seek was successful.
|
||||||
*/
|
*/
|
||||||
private boolean seekInsideBufferUs(long positionUs) {
|
private boolean seekInsideBufferUs(boolean[] trackIsAudioVideoFlags, long positionUs) {
|
||||||
int trackCount = sampleQueues.length;
|
int trackCount = sampleQueues.length;
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
SampleQueue sampleQueue = sampleQueues[i];
|
SampleQueue sampleQueue = sampleQueues[i];
|
||||||
|
|
@ -798,6 +833,7 @@ import java.util.Arrays;
|
||||||
private final Uri uri;
|
private final Uri uri;
|
||||||
private final StatsDataSource dataSource;
|
private final StatsDataSource dataSource;
|
||||||
private final ExtractorHolder extractorHolder;
|
private final ExtractorHolder extractorHolder;
|
||||||
|
private final ExtractorOutput extractorOutput;
|
||||||
private final ConditionVariable loadCondition;
|
private final ConditionVariable loadCondition;
|
||||||
private final PositionHolder positionHolder;
|
private final PositionHolder positionHolder;
|
||||||
|
|
||||||
|
|
@ -812,10 +848,12 @@ import java.util.Arrays;
|
||||||
Uri uri,
|
Uri uri,
|
||||||
DataSource dataSource,
|
DataSource dataSource,
|
||||||
ExtractorHolder extractorHolder,
|
ExtractorHolder extractorHolder,
|
||||||
|
ExtractorOutput extractorOutput,
|
||||||
ConditionVariable loadCondition) {
|
ConditionVariable loadCondition) {
|
||||||
this.uri = Assertions.checkNotNull(uri);
|
this.uri = uri;
|
||||||
this.dataSource = new StatsDataSource(dataSource);
|
this.dataSource = new StatsDataSource(dataSource);
|
||||||
this.extractorHolder = Assertions.checkNotNull(extractorHolder);
|
this.extractorHolder = extractorHolder;
|
||||||
|
this.extractorOutput = extractorOutput;
|
||||||
this.loadCondition = loadCondition;
|
this.loadCondition = loadCondition;
|
||||||
this.positionHolder = new PositionHolder();
|
this.positionHolder = new PositionHolder();
|
||||||
this.pendingExtractorSeek = true;
|
this.pendingExtractorSeek = true;
|
||||||
|
|
@ -842,8 +880,9 @@ import java.util.Arrays;
|
||||||
if (length != C.LENGTH_UNSET) {
|
if (length != C.LENGTH_UNSET) {
|
||||||
length += position;
|
length += position;
|
||||||
}
|
}
|
||||||
|
Uri uri = Assertions.checkNotNull(dataSource.getUri());
|
||||||
input = new DefaultExtractorInput(dataSource, position, length);
|
input = new DefaultExtractorInput(dataSource, position, length);
|
||||||
Extractor extractor = extractorHolder.selectExtractor(input, dataSource.getUri());
|
Extractor extractor = extractorHolder.selectExtractor(input, extractorOutput, uri);
|
||||||
if (pendingExtractorSeek) {
|
if (pendingExtractorSeek) {
|
||||||
extractor.seek(position, seekTimeUs);
|
extractor.seek(position, seekTimeUs);
|
||||||
pendingExtractorSeek = false;
|
pendingExtractorSeek = false;
|
||||||
|
|
@ -877,24 +916,20 @@ import java.util.Arrays;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Stores a list of extractors and a selected extractor when the format has been detected. */
|
||||||
* Stores a list of extractors and a selected extractor when the format has been detected.
|
|
||||||
*/
|
|
||||||
private static final class ExtractorHolder {
|
private static final class ExtractorHolder {
|
||||||
|
|
||||||
private final Extractor[] extractors;
|
private final Extractor[] extractors;
|
||||||
private final ExtractorOutput extractorOutput;
|
|
||||||
private Extractor extractor;
|
private @Nullable Extractor extractor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a holder that will select an extractor and initialize it using the specified output.
|
* Creates a holder that will select an extractor and initialize it using the specified output.
|
||||||
*
|
*
|
||||||
* @param extractors One or more extractors to choose from.
|
* @param extractors One or more extractors to choose from.
|
||||||
* @param extractorOutput The output that will be used to initialize the selected extractor.
|
|
||||||
*/
|
*/
|
||||||
public ExtractorHolder(Extractor[] extractors, ExtractorOutput extractorOutput) {
|
public ExtractorHolder(Extractor[] extractors) {
|
||||||
this.extractors = extractors;
|
this.extractors = extractors;
|
||||||
this.extractorOutput = extractorOutput;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -902,13 +937,15 @@ import java.util.Arrays;
|
||||||
* later calls.
|
* later calls.
|
||||||
*
|
*
|
||||||
* @param input The {@link ExtractorInput} from which data should be read.
|
* @param input The {@link ExtractorInput} from which data should be read.
|
||||||
|
* @param output The {@link ExtractorOutput} that will be used to initialize the selected
|
||||||
|
* extractor.
|
||||||
* @param uri The {@link Uri} of the data.
|
* @param uri The {@link Uri} of the data.
|
||||||
* @return An initialized extractor for reading {@code input}.
|
* @return An initialized extractor for reading {@code input}.
|
||||||
* @throws UnrecognizedInputFormatException Thrown if the input format could not be detected.
|
* @throws UnrecognizedInputFormatException Thrown if the input format could not be detected.
|
||||||
* @throws IOException Thrown if the input could not be read.
|
* @throws IOException Thrown if the input could not be read.
|
||||||
* @throws InterruptedException Thrown if the thread was interrupted.
|
* @throws InterruptedException Thrown if the thread was interrupted.
|
||||||
*/
|
*/
|
||||||
public Extractor selectExtractor(ExtractorInput input, Uri uri)
|
public Extractor selectExtractor(ExtractorInput input, ExtractorOutput output, Uri uri)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
if (extractor != null) {
|
if (extractor != null) {
|
||||||
return extractor;
|
return extractor;
|
||||||
|
|
@ -929,7 +966,7 @@ import java.util.Arrays;
|
||||||
throw new UnrecognizedInputFormatException("None of the available extractors ("
|
throw new UnrecognizedInputFormatException("None of the available extractors ("
|
||||||
+ Util.getCommaDelimitedSimpleClassNames(extractors) + ") could read the stream.", uri);
|
+ Util.getCommaDelimitedSimpleClassNames(extractors) + ") could read the stream.", uri);
|
||||||
}
|
}
|
||||||
extractor.init(extractorOutput);
|
extractor.init(output);
|
||||||
return extractor;
|
return extractor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -940,4 +977,26 @@ import java.util.Arrays;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Stores state that is initialized when preparation completes. */
|
||||||
|
private static final class PreparedState {
|
||||||
|
public final TrackGroupArray tracks;
|
||||||
|
public final boolean[] trackEnabledStates;
|
||||||
|
public final boolean[] trackIsAudioVideoFlags;
|
||||||
|
public final boolean[] trackFormatNotificationSent;
|
||||||
|
public final SeekMap seekMap;
|
||||||
|
|
||||||
|
public PreparedState(
|
||||||
|
TrackGroupArray tracks,
|
||||||
|
boolean[] trackEnabledStates,
|
||||||
|
boolean[] trackIsAudioVideoFlags,
|
||||||
|
boolean[] trackFormatNotificationSent,
|
||||||
|
SeekMap seekMap) {
|
||||||
|
this.tracks = tracks;
|
||||||
|
this.trackEnabledStates = trackEnabledStates;
|
||||||
|
this.trackIsAudioVideoFlags = trackIsAudioVideoFlags;
|
||||||
|
this.trackFormatNotificationSent = trackFormatNotificationSent;
|
||||||
|
this.seekMap = seekMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ public final class MimeTypes {
|
||||||
* @param mimeType The mimeType to test.
|
* @param mimeType The mimeType to test.
|
||||||
* @return Whether the top level type is audio.
|
* @return Whether the top level type is audio.
|
||||||
*/
|
*/
|
||||||
public static boolean isAudio(String mimeType) {
|
public static boolean isAudio(@Nullable String mimeType) {
|
||||||
return BASE_TYPE_AUDIO.equals(getTopLevelType(mimeType));
|
return BASE_TYPE_AUDIO.equals(getTopLevelType(mimeType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ public final class MimeTypes {
|
||||||
* @param mimeType The mimeType to test.
|
* @param mimeType The mimeType to test.
|
||||||
* @return Whether the top level type is video.
|
* @return Whether the top level type is video.
|
||||||
*/
|
*/
|
||||||
public static boolean isVideo(String mimeType) {
|
public static boolean isVideo(@Nullable String mimeType) {
|
||||||
return BASE_TYPE_VIDEO.equals(getTopLevelType(mimeType));
|
return BASE_TYPE_VIDEO.equals(getTopLevelType(mimeType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +143,7 @@ public final class MimeTypes {
|
||||||
* @param mimeType The mimeType to test.
|
* @param mimeType The mimeType to test.
|
||||||
* @return Whether the top level type is text.
|
* @return Whether the top level type is text.
|
||||||
*/
|
*/
|
||||||
public static boolean isText(String mimeType) {
|
public static boolean isText(@Nullable String mimeType) {
|
||||||
return BASE_TYPE_TEXT.equals(getTopLevelType(mimeType));
|
return BASE_TYPE_TEXT.equals(getTopLevelType(mimeType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,7 +153,7 @@ public final class MimeTypes {
|
||||||
* @param mimeType The mimeType to test.
|
* @param mimeType The mimeType to test.
|
||||||
* @return Whether the top level type is application.
|
* @return Whether the top level type is application.
|
||||||
*/
|
*/
|
||||||
public static boolean isApplication(String mimeType) {
|
public static boolean isApplication(@Nullable String mimeType) {
|
||||||
return BASE_TYPE_APPLICATION.equals(getTopLevelType(mimeType));
|
return BASE_TYPE_APPLICATION.equals(getTopLevelType(mimeType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ import java.util.regex.Pattern;
|
||||||
import java.util.zip.DataFormatException;
|
import java.util.zip.DataFormatException;
|
||||||
import java.util.zip.Inflater;
|
import java.util.zip.Inflater;
|
||||||
import org.checkerframework.checker.initialization.qual.UnknownInitialization;
|
import org.checkerframework.checker.initialization.qual.UnknownInitialization;
|
||||||
|
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.PolyNull;
|
import org.checkerframework.checker.nullness.qual.PolyNull;
|
||||||
|
|
||||||
|
|
@ -271,6 +272,13 @@ public final class Util {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Casts a nullable type array to a non-null type array without runtime null check. */
|
||||||
|
@SuppressWarnings({"contracts.postcondition.not.satisfied", "return.type.incompatible"})
|
||||||
|
@EnsuresNonNull("#1")
|
||||||
|
public static <T> T[] castNonNullTypeArray(@NullableType T[] value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies and optionally truncates an array. Prevents null array elements created by {@link
|
* 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.
|
* Arrays#copyOf(Object[], int)} by ensuring the new length does not exceed the current length.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue