mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Don't support upstream discard from spliced-in chunks.
We can't restore the previous state of the remaining chunk, so we can't support discarding from spliced-in chunks. Mark this explicitly instead of attempting to discard from the previous chunk. PiperOrigin-RevId: 318983628
This commit is contained in:
parent
311d21bf8d
commit
2e749f70ae
@ -25,7 +25,6 @@ import com.google.android.exoplayer2.extractor.ExtractorInput;
|
||||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
|
||||
import com.google.android.exoplayer2.metadata.id3.PrivFrame;
|
||||
import com.google.android.exoplayer2.source.SampleQueue;
|
||||
import com.google.android.exoplayer2.source.chunk.MediaChunk;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
@ -35,7 +34,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer2.util.TimestampAdjuster;
|
||||
import com.google.android.exoplayer2.util.UriUtil;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
@ -132,7 +131,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
Id3Decoder id3Decoder;
|
||||
ParsableByteArray scratchId3Data;
|
||||
boolean shouldSpliceIn;
|
||||
ImmutableMap<SampleQueue, Integer> sampleQueueDiscardFromIndices = ImmutableMap.of();
|
||||
if (previousChunk != null) {
|
||||
boolean isFollowingChunk =
|
||||
playlistUrl.equals(previousChunk.playlistUrl) && previousChunk.loadCompleted;
|
||||
@ -143,9 +141,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
|| (mediaPlaylist.hasIndependentSegments
|
||||
&& segmentStartTimeInPeriodUs >= previousChunk.endTimeUs);
|
||||
shouldSpliceIn = !canContinueWithoutSplice;
|
||||
if (shouldSpliceIn) {
|
||||
sampleQueueDiscardFromIndices = previousChunk.sampleQueueDiscardFromIndices;
|
||||
}
|
||||
previousExtractor =
|
||||
isFollowingChunk
|
||||
&& previousChunk.discontinuitySequenceNumber == discontinuitySequenceNumber
|
||||
@ -181,8 +176,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
previousExtractor,
|
||||
id3Decoder,
|
||||
scratchId3Data,
|
||||
shouldSpliceIn,
|
||||
sampleQueueDiscardFromIndices);
|
||||
shouldSpliceIn);
|
||||
}
|
||||
|
||||
public static final String PRIV_TIMESTAMP_FRAME_OWNER =
|
||||
@ -203,6 +197,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
/** The url of the playlist from which this chunk was obtained. */
|
||||
public final Uri playlistUrl;
|
||||
|
||||
/** Whether samples for this chunk should be spliced into existing samples. */
|
||||
public final boolean shouldSpliceIn;
|
||||
|
||||
@Nullable private final DataSource initDataSource;
|
||||
@Nullable private final DataSpec initDataSpec;
|
||||
@Nullable private final HlsMediaChunkExtractor previousExtractor;
|
||||
@ -217,7 +214,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
private final ParsableByteArray scratchId3Data;
|
||||
private final boolean mediaSegmentEncrypted;
|
||||
private final boolean initSegmentEncrypted;
|
||||
private final boolean shouldSpliceIn;
|
||||
|
||||
private @MonotonicNonNull HlsMediaChunkExtractor extractor;
|
||||
private @MonotonicNonNull HlsSampleStreamWrapper output;
|
||||
@ -227,7 +223,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
private boolean initDataLoadRequired;
|
||||
private volatile boolean loadCanceled;
|
||||
private boolean loadCompleted;
|
||||
private ImmutableMap<SampleQueue, Integer> sampleQueueDiscardFromIndices;
|
||||
private ImmutableList<Integer> sampleQueueFirstSampleIndices;
|
||||
|
||||
private HlsMediaChunk(
|
||||
HlsExtractorFactory extractorFactory,
|
||||
@ -253,8 +249,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
@Nullable HlsMediaChunkExtractor previousExtractor,
|
||||
Id3Decoder id3Decoder,
|
||||
ParsableByteArray scratchId3Data,
|
||||
boolean shouldSpliceIn,
|
||||
ImmutableMap<SampleQueue, Integer> sampleQueueDiscardFromIndices) {
|
||||
boolean shouldSpliceIn) {
|
||||
super(
|
||||
mediaDataSource,
|
||||
dataSpec,
|
||||
@ -281,7 +276,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
this.id3Decoder = id3Decoder;
|
||||
this.scratchId3Data = scratchId3Data;
|
||||
this.shouldSpliceIn = shouldSpliceIn;
|
||||
this.sampleQueueDiscardFromIndices = sampleQueueDiscardFromIndices;
|
||||
sampleQueueFirstSampleIndices = ImmutableList.of();
|
||||
uid = uidSource.getAndIncrement();
|
||||
}
|
||||
|
||||
@ -289,35 +284,29 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
* Initializes the chunk for loading.
|
||||
*
|
||||
* @param output The {@link HlsSampleStreamWrapper} that will receive the loaded samples.
|
||||
* @param sampleQueues The {@link SampleQueue sampleQueues} with already loaded samples.
|
||||
* @param sampleQueueWriteIndices The current write indices in the existing sample queues of the
|
||||
* output.
|
||||
*/
|
||||
public void init(HlsSampleStreamWrapper output, SampleQueue[] sampleQueues) {
|
||||
public void init(HlsSampleStreamWrapper output, ImmutableList<Integer> sampleQueueWriteIndices) {
|
||||
this.output = output;
|
||||
if (shouldSpliceIn) {
|
||||
for (SampleQueue sampleQueue : sampleQueues) {
|
||||
sampleQueue.splice();
|
||||
}
|
||||
// sampleQueueDiscardFromIndices already set to values of previous chunk in constructor.
|
||||
} else {
|
||||
ImmutableMap.Builder<SampleQueue, Integer> mapBuilder = ImmutableMap.builder();
|
||||
for (SampleQueue sampleQueue : sampleQueues) {
|
||||
mapBuilder.put(sampleQueue, sampleQueue.getWriteIndex());
|
||||
}
|
||||
sampleQueueDiscardFromIndices = mapBuilder.build();
|
||||
}
|
||||
this.sampleQueueFirstSampleIndices = sampleQueueWriteIndices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute index from which samples need to be discarded in the given {@link
|
||||
* SampleQueue} when this media chunk is discarded.
|
||||
* Returns the first sample index of this chunk in the specified sample queue in the output.
|
||||
*
|
||||
* @param sampleQueue The {@link SampleQueue}.
|
||||
* @return The absolute index from which samples need to be discarded.
|
||||
* <p>Must not be used if {@link #shouldSpliceIn} is true.
|
||||
*
|
||||
* @param sampleQueueIndex The index of the sample queue in the output.
|
||||
* @return The first sample index of this chunk in the specified sample queue.
|
||||
*/
|
||||
int getSampleQueueDiscardFromIndex(SampleQueue sampleQueue) {
|
||||
// If the sample queue was created by this chunk or a later chunk, return 0 to discard the whole
|
||||
// stream from the beginning.
|
||||
return sampleQueueDiscardFromIndices.getOrDefault(sampleQueue, /* defaultValue= */ 0);
|
||||
int getFirstSampleIndex(int sampleQueueIndex) {
|
||||
Assertions.checkState(!shouldSpliceIn);
|
||||
if (sampleQueueIndex >= sampleQueueFirstSampleIndices.size()) {
|
||||
// The sample queue was created by this chunk or a later chunk.
|
||||
return 0;
|
||||
}
|
||||
return sampleQueueFirstSampleIndices.get(sampleQueueIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,6 +61,7 @@ import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -873,9 +874,16 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
upstreamTrackFormat = chunk.trackFormat;
|
||||
pendingResetPositionUs = C.TIME_UNSET;
|
||||
mediaChunks.add(chunk);
|
||||
chunk.init(/* output= */ this, sampleQueues);
|
||||
ImmutableList.Builder<Integer> sampleQueueWriteIndicesBuilder = ImmutableList.builder();
|
||||
for (SampleQueue sampleQueue : sampleQueues) {
|
||||
sampleQueueWriteIndicesBuilder.add(sampleQueue.getWriteIndex());
|
||||
}
|
||||
chunk.init(/* output= */ this, sampleQueueWriteIndicesBuilder.build());
|
||||
for (HlsSampleQueue sampleQueue : sampleQueues) {
|
||||
sampleQueue.setSourceChunk(chunk);
|
||||
if (chunk.shouldSpliceIn) {
|
||||
sampleQueue.splice();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -884,7 +892,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
|
||||
int newQueueSize = C.LENGTH_UNSET;
|
||||
for (int i = preferredQueueSize; i < mediaChunks.size(); i++) {
|
||||
if (!haveReadFromMediaChunkDiscardRange(i)) {
|
||||
if (canDiscardUpstreamMediaChunksFromIndex(i)) {
|
||||
newQueueSize = i;
|
||||
break;
|
||||
}
|
||||
@ -1102,23 +1110,32 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean haveReadFromMediaChunkDiscardRange(int mediaChunkIndex) {
|
||||
HlsMediaChunk mediaChunk = mediaChunks.get(mediaChunkIndex);
|
||||
for (SampleQueue sampleQueue : sampleQueues) {
|
||||
int discardFromIndex = mediaChunk.getSampleQueueDiscardFromIndex(sampleQueue);
|
||||
if (sampleQueue.getReadIndex() > discardFromIndex) {
|
||||
return true;
|
||||
private boolean canDiscardUpstreamMediaChunksFromIndex(int mediaChunkIndex) {
|
||||
for (int i = mediaChunkIndex; i < mediaChunks.size(); i++) {
|
||||
if (mediaChunks.get(i).shouldSpliceIn) {
|
||||
// Discarding not possible because a spliced-in chunk potentially removed sample metadata
|
||||
// from the previous chunks.
|
||||
// TODO: Keep sample metadata to allow restoring these chunks [internal b/159904763].
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
HlsMediaChunk mediaChunk = mediaChunks.get(mediaChunkIndex);
|
||||
for (int i = 0; i < sampleQueues.length; i++) {
|
||||
int discardFromIndex = mediaChunk.getFirstSampleIndex(/* sampleQueueIndex= */ i);
|
||||
if (sampleQueues[i].getReadIndex() > discardFromIndex) {
|
||||
// Discarding not possible because we already read from the chunk.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private HlsMediaChunk discardUpstreamMediaChunksFromIndex(int chunkIndex) {
|
||||
HlsMediaChunk firstRemovedChunk = mediaChunks.get(chunkIndex);
|
||||
Util.removeRange(mediaChunks, /* fromIndex= */ chunkIndex, /* toIndex= */ mediaChunks.size());
|
||||
for (SampleQueue sampleQueue : sampleQueues) {
|
||||
int discardFromIndex = firstRemovedChunk.getSampleQueueDiscardFromIndex(sampleQueue);
|
||||
sampleQueue.discardUpstreamSamples(discardFromIndex);
|
||||
for (int i = 0; i < sampleQueues.length; i++) {
|
||||
int discardFromIndex = firstRemovedChunk.getFirstSampleIndex(/* sampleQueueIndex= */ i);
|
||||
sampleQueues[i].discardUpstreamSamples(discardFromIndex);
|
||||
}
|
||||
return firstRemovedChunk;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user