mirror of
https://github.com/samsonjs/media.git
synced 2026-04-01 10:35:48 +00:00
add support SequenceableLoader.reevaluateBuffer() for HLS
DASH implements this feature, extend the feature for HLS as well. First change just drops video samples. For demuxed audio the audio samples will continue to play out to match the dropped video, so need to keep indexes in all the sample queues related to a chunk and discard them all.
This commit is contained in:
parent
bab8975438
commit
57d5160161
3 changed files with 97 additions and 1 deletions
|
|
@ -451,6 +451,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
return chunkIterators;
|
||||
}
|
||||
|
||||
public int getPreferredQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) {
|
||||
if (fatalError != null || trackSelection.length() < 2) {
|
||||
return queue.size();
|
||||
}
|
||||
return trackSelection.evaluateQueueSize(playbackPositionUs, queue);
|
||||
}
|
||||
|
||||
// Private methods.
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import com.google.android.exoplayer2.extractor.PositionHolder;
|
|||
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;
|
||||
|
|
@ -222,6 +223,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||
private volatile boolean loadCanceled;
|
||||
private boolean loadCompleted;
|
||||
|
||||
/**
|
||||
* Index of first sample written to the SampleQueue for the primary track from
|
||||
* this segment.
|
||||
*/
|
||||
private int firstSampleIndex = C.INDEX_UNSET;
|
||||
|
||||
private HlsMediaChunk(
|
||||
HlsExtractorFactory extractorFactory,
|
||||
DataSource mediaDataSource,
|
||||
|
|
@ -291,6 +298,15 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||
return loadCompleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index of the first sample from the primary sample stream for this media chunk
|
||||
*
|
||||
* @return sample index {@link SampleQueue#getWriteIndex()}
|
||||
*/
|
||||
public int getFirstPrimarySampleIndex() {
|
||||
return firstSampleIndex;
|
||||
}
|
||||
|
||||
// Loadable implementation
|
||||
|
||||
@Override
|
||||
|
|
@ -308,6 +324,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||
initDataLoadRequired = false;
|
||||
output.init(uid, shouldSpliceIn, /* reusingExtractor= */ true);
|
||||
}
|
||||
firstSampleIndex = output.getPrimaryTrackWritePosition();
|
||||
maybeLoadInitData();
|
||||
if (!loadCanceled) {
|
||||
if (!hasGapTag) {
|
||||
|
|
|
|||
|
|
@ -696,9 +696,65 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||
|
||||
@Override
|
||||
public void reevaluateBuffer(long positionUs) {
|
||||
// Do nothing.
|
||||
if (loader.isLoading() || isPendingReset()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int currentQueueSize = mediaChunks.size();
|
||||
int preferredQueueSize = chunkSource.getPreferredQueueSize(positionUs, readOnlyMediaChunks);
|
||||
if (currentQueueSize > preferredQueueSize) {
|
||||
Log.d(TAG, "reevaluateBuffer() - position: " + positionUs + " preferredQueueSize: "
|
||||
+ preferredQueueSize + " current size: "+ currentQueueSize);
|
||||
|
||||
long firstRemovedStartTimeUs = discardMediaChunks(preferredQueueSize);
|
||||
long endTimeUs = getLastMediaChunk().endTimeUs;
|
||||
eventDispatcher.upstreamDiscarded(primarySampleQueueType, firstRemovedStartTimeUs, endTimeUs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Discards HlsMediaChunks, after currently playing chunk {@see #haveReadFromMediaChunk}, that have
|
||||
* not yet started to play to allow (hopefully) higher quality chunks to replace them
|
||||
*
|
||||
* @param preferredQueueSize - desired media chunk queue size (always < mediaChunks.size())
|
||||
* @return endTimeUs of first chunk removed
|
||||
*/
|
||||
private long discardMediaChunks(int preferredQueueSize) {
|
||||
Log.d(TAG, "discardChunksToIndex() - preferredQueueSize " + preferredQueueSize
|
||||
+ " currentSize " + mediaChunks.size()
|
||||
+ " write: "+sampleQueues[primarySampleQueueIndex].getWriteIndex()
|
||||
+ " read: "+sampleQueues[primarySampleQueueIndex].getReadIndex()
|
||||
);
|
||||
for (int i=0; i<mediaChunks.size(); i++) {
|
||||
HlsMediaChunk chunk = mediaChunks.get(i);
|
||||
Log.d(TAG, "chunk " + chunk.uid + " reading: " + haveReadFromMediaChunk(i)
|
||||
+ " start/end: " + chunk.startTimeUs + "/" + chunk.endTimeUs + " sample index: "
|
||||
+ chunk.getFirstPrimarySampleIndex() + " format: "+chunk.trackFormat);
|
||||
}
|
||||
|
||||
// Preserve current playing chunk and/or enough following it to reach the desired queue size
|
||||
int firstRemovedChunkIndex = 0;
|
||||
while (haveReadFromMediaChunk(firstRemovedChunkIndex) || preferredQueueSize > 0) {
|
||||
firstRemovedChunkIndex++;
|
||||
preferredQueueSize--;
|
||||
}
|
||||
|
||||
HlsMediaChunk firstRemovedChunk = mediaChunks.get(firstRemovedChunkIndex);
|
||||
Util.removeRange(mediaChunks, firstRemovedChunkIndex, mediaChunks.size() - 1);
|
||||
Log.d(TAG, "discardChunksToIndex() - discard from: " + firstRemovedChunk.getFirstPrimarySampleIndex());
|
||||
|
||||
sampleQueues[primarySampleQueueIndex].discardUpstreamSamples(firstRemovedChunk.getFirstPrimarySampleIndex());
|
||||
|
||||
return firstRemovedChunk.endTimeUs;
|
||||
}
|
||||
|
||||
|
||||
/** Returns whether samples have been read from primary sample queue of the indicated chunk */
|
||||
private boolean haveReadFromMediaChunk(int mediaChunkIndex) {
|
||||
HlsMediaChunk mediaChunk = mediaChunks.get(mediaChunkIndex);
|
||||
return sampleQueues[primarySampleQueueIndex].getReadIndex() > mediaChunk.getFirstPrimarySampleIndex();
|
||||
}
|
||||
// Loader.Callback implementation.
|
||||
|
||||
@Override
|
||||
|
|
@ -959,6 +1015,22 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SampleQueue write position index. Used to associate group of
|
||||
* samples with a MediaChunk.
|
||||
*
|
||||
* @return write position {@link SampleQueue#getWriteIndex()}, or 0 (safe bet) if sample queues not created
|
||||
*/
|
||||
int getPrimaryTrackWritePosition() {
|
||||
int indexValue = 0;
|
||||
|
||||
if (primaryTrackGroupIndex != C.INDEX_UNSET && prepared) {
|
||||
int sampleQueueIndex = trackGroupToSampleQueueIndex[primaryTrackGroupIndex];
|
||||
indexValue = sampleQueues[sampleQueueIndex].getWriteIndex();
|
||||
}
|
||||
return indexValue;
|
||||
}
|
||||
|
||||
// Internal methods.
|
||||
|
||||
private void updateSampleStreams(@NullableType SampleStream[] streams) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue