mirror of
https://github.com/androidx/media.git
synced 2025-05-14 19:19:58 +08:00
Migrate HLS over to new SampleQueue methods
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161391296
This commit is contained in:
parent
5ebbb6ef45
commit
06b3b3ca8d
@ -219,6 +219,10 @@ import java.util.Arrays;
|
|||||||
if (!seekRequired) {
|
if (!seekRequired) {
|
||||||
SampleQueue sampleQueue = sampleQueues[track];
|
SampleQueue sampleQueue = sampleQueues[track];
|
||||||
sampleQueue.rewind();
|
sampleQueue.rewind();
|
||||||
|
// A seek can be avoided if we're able to advance to the current playback position in the
|
||||||
|
// sample queue, or if we haven't read anything from the queue since the previous seek
|
||||||
|
// (this case is common for sparse tracks such as metadata tracks). In all other cases a
|
||||||
|
// seek is required.
|
||||||
seekRequired = !sampleQueue.advanceTo(positionUs, true, true)
|
seekRequired = !sampleQueue.advanceTo(positionUs, true, true)
|
||||||
&& sampleQueue.getReadIndex() != 0;
|
&& sampleQueue.getReadIndex() != 0;
|
||||||
}
|
}
|
||||||
|
@ -83,11 +83,6 @@ import com.google.android.exoplayer2.util.Util;
|
|||||||
relativeStartIndex = 0;
|
relativeStartIndex = 0;
|
||||||
readPosition = 0;
|
readPosition = 0;
|
||||||
upstreamKeyframeRequired = true;
|
upstreamKeyframeRequired = true;
|
||||||
}
|
|
||||||
|
|
||||||
// Called by the consuming thread, but only when there is no loading thread.
|
|
||||||
|
|
||||||
public void resetLargestParsedTimestamps() {
|
|
||||||
largestDiscardedTimestampUs = Long.MIN_VALUE;
|
largestDiscardedTimestampUs = Long.MIN_VALUE;
|
||||||
largestQueuedTimestampUs = Long.MIN_VALUE;
|
largestQueuedTimestampUs = Long.MIN_VALUE;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
|||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A queue of media samples.
|
* A queue of media samples.
|
||||||
@ -52,16 +51,11 @@ public final class SampleQueue implements TrackOutput {
|
|||||||
|
|
||||||
private static final int INITIAL_SCRATCH_SIZE = 32;
|
private static final int INITIAL_SCRATCH_SIZE = 32;
|
||||||
|
|
||||||
private static final int STATE_ENABLED = 0;
|
|
||||||
private static final int STATE_ENABLED_WRITING = 1;
|
|
||||||
private static final int STATE_DISABLED = 2;
|
|
||||||
|
|
||||||
private final Allocator allocator;
|
private final Allocator allocator;
|
||||||
private final int allocationLength;
|
private final int allocationLength;
|
||||||
private final SampleMetadataQueue metadataQueue;
|
private final SampleMetadataQueue metadataQueue;
|
||||||
private final SampleExtrasHolder extrasHolder;
|
private final SampleExtrasHolder extrasHolder;
|
||||||
private final ParsableByteArray scratch;
|
private final ParsableByteArray scratch;
|
||||||
private final AtomicInteger state;
|
|
||||||
|
|
||||||
// References into the linked list of allocations.
|
// References into the linked list of allocations.
|
||||||
private AllocationNode firstAllocationNode;
|
private AllocationNode firstAllocationNode;
|
||||||
@ -88,7 +82,6 @@ public final class SampleQueue implements TrackOutput {
|
|||||||
metadataQueue = new SampleMetadataQueue();
|
metadataQueue = new SampleMetadataQueue();
|
||||||
extrasHolder = new SampleExtrasHolder();
|
extrasHolder = new SampleExtrasHolder();
|
||||||
scratch = new ParsableByteArray(INITIAL_SCRATCH_SIZE);
|
scratch = new ParsableByteArray(INITIAL_SCRATCH_SIZE);
|
||||||
state = new AtomicInteger();
|
|
||||||
firstAllocationNode = new AllocationNode(0, allocationLength);
|
firstAllocationNode = new AllocationNode(0, allocationLength);
|
||||||
readAllocationNode = firstAllocationNode;
|
readAllocationNode = firstAllocationNode;
|
||||||
writeAllocationNode = firstAllocationNode;
|
writeAllocationNode = firstAllocationNode;
|
||||||
@ -100,20 +93,13 @@ public final class SampleQueue implements TrackOutput {
|
|||||||
* Resets the output.
|
* Resets the output.
|
||||||
*/
|
*/
|
||||||
public void reset() {
|
public void reset() {
|
||||||
reset(true);
|
metadataQueue.clearSampleData();
|
||||||
}
|
clearAllocationNodes(firstAllocationNode);
|
||||||
|
firstAllocationNode = new AllocationNode(0, allocationLength);
|
||||||
/**
|
readAllocationNode = firstAllocationNode;
|
||||||
* @deprecated Use {@link #reset()}. Don't disable sample queues.
|
writeAllocationNode = firstAllocationNode;
|
||||||
*/
|
totalBytesWritten = 0;
|
||||||
@Deprecated
|
allocator.trim();
|
||||||
public void reset(boolean enable) {
|
|
||||||
int previousState = state.getAndSet(enable ? STATE_ENABLED : STATE_DISABLED);
|
|
||||||
clearSampleData();
|
|
||||||
metadataQueue.resetLargestParsedTimestamps();
|
|
||||||
if (previousState == STATE_DISABLED) {
|
|
||||||
downstreamFormat = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -174,16 +160,6 @@ public final class SampleQueue implements TrackOutput {
|
|||||||
|
|
||||||
// Called by the consuming thread.
|
// Called by the consuming thread.
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Don't disable sample queues.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void disable() {
|
|
||||||
if (state.getAndSet(STATE_DISABLED) == STATE_ENABLED) {
|
|
||||||
clearSampleData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether a sample is available to be read.
|
* Returns whether a sample is available to be read.
|
||||||
*/
|
*/
|
||||||
@ -265,15 +241,6 @@ public final class SampleQueue implements TrackOutput {
|
|||||||
discardDownstreamTo(metadataQueue.discardToEnd());
|
discardDownstreamTo(metadataQueue.discardToEnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link #advanceToEnd()} followed by {@link #discardToRead()}.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void skipAll() {
|
|
||||||
advanceToEnd();
|
|
||||||
discardToRead();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Advances the read position to the end of the queue.
|
* Advances the read position to the end of the queue.
|
||||||
*/
|
*/
|
||||||
@ -281,17 +248,6 @@ public final class SampleQueue implements TrackOutput {
|
|||||||
metadataQueue.advanceToEnd();
|
metadataQueue.advanceToEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link #advanceTo(long, boolean, boolean)} followed by
|
|
||||||
* {@link #discardToRead()}.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public boolean skipToKeyframeBefore(long timeUs, boolean allowTimeBeyondBuffer) {
|
|
||||||
boolean success = advanceTo(timeUs, true, allowTimeBeyondBuffer);
|
|
||||||
discardToRead();
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to advance the read position to the sample before or at the specified time.
|
* Attempts to advance the read position to the sample before or at the specified time.
|
||||||
*
|
*
|
||||||
@ -307,19 +263,6 @@ public final class SampleQueue implements TrackOutput {
|
|||||||
return metadataQueue.advanceTo(timeUs, toKeyframe, allowTimeBeyondBuffer);
|
return metadataQueue.advanceTo(timeUs, toKeyframe, allowTimeBeyondBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link #read(FormatHolder, DecoderInputBuffer, boolean, boolean, long)}
|
|
||||||
* followed by {@link #discardToRead()}.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired,
|
|
||||||
boolean loadingFinished, long decodeOnlyUntilUs) {
|
|
||||||
int result = read(formatHolder, buffer, formatRequired, loadingFinished,
|
|
||||||
decodeOnlyUntilUs);
|
|
||||||
discardToRead();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to read from the queue.
|
* Attempts to read from the queue.
|
||||||
*
|
*
|
||||||
@ -558,39 +501,21 @@ public final class SampleQueue implements TrackOutput {
|
|||||||
@Override
|
@Override
|
||||||
public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
|
public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
if (!startWriteOperation()) {
|
length = preAppend(length);
|
||||||
int bytesSkipped = input.skip(length);
|
int bytesAppended = input.read(writeAllocationNode.allocation.data,
|
||||||
if (bytesSkipped == C.RESULT_END_OF_INPUT) {
|
writeAllocationNode.translateOffset(totalBytesWritten), length);
|
||||||
if (allowEndOfInput) {
|
if (bytesAppended == C.RESULT_END_OF_INPUT) {
|
||||||
return C.RESULT_END_OF_INPUT;
|
if (allowEndOfInput) {
|
||||||
}
|
return C.RESULT_END_OF_INPUT;
|
||||||
throw new EOFException();
|
|
||||||
}
|
}
|
||||||
return bytesSkipped;
|
throw new EOFException();
|
||||||
}
|
|
||||||
try {
|
|
||||||
length = preAppend(length);
|
|
||||||
int bytesAppended = input.read(writeAllocationNode.allocation.data,
|
|
||||||
writeAllocationNode.translateOffset(totalBytesWritten), length);
|
|
||||||
if (bytesAppended == C.RESULT_END_OF_INPUT) {
|
|
||||||
if (allowEndOfInput) {
|
|
||||||
return C.RESULT_END_OF_INPUT;
|
|
||||||
}
|
|
||||||
throw new EOFException();
|
|
||||||
}
|
|
||||||
postAppend(bytesAppended);
|
|
||||||
return bytesAppended;
|
|
||||||
} finally {
|
|
||||||
endWriteOperation();
|
|
||||||
}
|
}
|
||||||
|
postAppend(bytesAppended);
|
||||||
|
return bytesAppended;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sampleData(ParsableByteArray buffer, int length) {
|
public void sampleData(ParsableByteArray buffer, int length) {
|
||||||
if (!startWriteOperation()) {
|
|
||||||
buffer.skipBytes(length);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
int bytesAppended = preAppend(length);
|
int bytesAppended = preAppend(length);
|
||||||
buffer.readBytes(writeAllocationNode.allocation.data,
|
buffer.readBytes(writeAllocationNode.allocation.data,
|
||||||
@ -598,7 +523,6 @@ public final class SampleQueue implements TrackOutput {
|
|||||||
length -= bytesAppended;
|
length -= bytesAppended;
|
||||||
postAppend(bytesAppended);
|
postAppend(bytesAppended);
|
||||||
}
|
}
|
||||||
endWriteOperation();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -607,47 +531,19 @@ public final class SampleQueue implements TrackOutput {
|
|||||||
if (pendingFormatAdjustment) {
|
if (pendingFormatAdjustment) {
|
||||||
format(lastUnadjustedFormat);
|
format(lastUnadjustedFormat);
|
||||||
}
|
}
|
||||||
if (!startWriteOperation()) {
|
if (pendingSplice) {
|
||||||
metadataQueue.commitSampleTimestamp(timeUs);
|
if ((flags & C.BUFFER_FLAG_KEY_FRAME) == 0 || !metadataQueue.attemptSplice(timeUs)) {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (pendingSplice) {
|
|
||||||
if ((flags & C.BUFFER_FLAG_KEY_FRAME) == 0 || !metadataQueue.attemptSplice(timeUs)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pendingSplice = false;
|
|
||||||
}
|
}
|
||||||
timeUs += sampleOffsetUs;
|
pendingSplice = false;
|
||||||
long absoluteOffset = totalBytesWritten - size - offset;
|
|
||||||
metadataQueue.commitSample(timeUs, flags, absoluteOffset, size, cryptoData);
|
|
||||||
} finally {
|
|
||||||
endWriteOperation();
|
|
||||||
}
|
}
|
||||||
|
timeUs += sampleOffsetUs;
|
||||||
|
long absoluteOffset = totalBytesWritten - size - offset;
|
||||||
|
metadataQueue.commitSample(timeUs, flags, absoluteOffset, size, cryptoData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private methods.
|
// Private methods.
|
||||||
|
|
||||||
private boolean startWriteOperation() {
|
|
||||||
return state.compareAndSet(STATE_ENABLED, STATE_ENABLED_WRITING);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void endWriteOperation() {
|
|
||||||
if (!state.compareAndSet(STATE_ENABLED_WRITING, STATE_ENABLED)) {
|
|
||||||
clearSampleData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearSampleData() {
|
|
||||||
metadataQueue.clearSampleData();
|
|
||||||
clearAllocationNodes(firstAllocationNode);
|
|
||||||
firstAllocationNode = new AllocationNode(0, allocationLength);
|
|
||||||
readAllocationNode = firstAllocationNode;
|
|
||||||
writeAllocationNode = firstAllocationNode;
|
|
||||||
totalBytesWritten = 0;
|
|
||||||
allocator.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears allocation nodes starting from {@code fromNode}.
|
* Clears allocation nodes starting from {@code fromNode}.
|
||||||
*
|
*
|
||||||
|
@ -53,6 +53,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
|||||||
private final Handler continueLoadingHandler;
|
private final Handler continueLoadingHandler;
|
||||||
|
|
||||||
private Callback callback;
|
private Callback callback;
|
||||||
|
private long preparePositionUs;
|
||||||
private int pendingPrepareCount;
|
private int pendingPrepareCount;
|
||||||
private boolean seenFirstTrackSelection;
|
private boolean seenFirstTrackSelection;
|
||||||
private TrackGroupArray trackGroups;
|
private TrackGroupArray trackGroups;
|
||||||
@ -84,8 +85,9 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepare(Callback callback, long positionUs) {
|
public void prepare(Callback callback, long positionUs) {
|
||||||
playlistTracker.addListener(this);
|
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
playlistTracker.addListener(this);
|
||||||
|
preparePositionUs = positionUs;
|
||||||
buildAndPrepareSampleStreamWrappers(positionUs);
|
buildAndPrepareSampleStreamWrappers(positionUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +125,9 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean selectedNewTracks = false;
|
// We'll always need to seek if this is a first selection to a position other than the prepare
|
||||||
|
// position.
|
||||||
|
boolean seekRequired = !seenFirstTrackSelection && positionUs != preparePositionUs;
|
||||||
streamWrapperIndices.clear();
|
streamWrapperIndices.clear();
|
||||||
// Select tracks for each child, copying the resulting streams back into a new streams array.
|
// Select tracks for each child, copying the resulting streams back into a new streams array.
|
||||||
SampleStream[] newStreams = new SampleStream[selections.length];
|
SampleStream[] newStreams = new SampleStream[selections.length];
|
||||||
@ -136,8 +140,8 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
|||||||
childStreams[j] = streamChildIndices[j] == i ? streams[j] : null;
|
childStreams[j] = streamChildIndices[j] == i ? streams[j] : null;
|
||||||
childSelections[j] = selectionChildIndices[j] == i ? selections[j] : null;
|
childSelections[j] = selectionChildIndices[j] == i ? selections[j] : null;
|
||||||
}
|
}
|
||||||
selectedNewTracks |= sampleStreamWrappers[i].selectTracks(childSelections,
|
seekRequired |= sampleStreamWrappers[i].selectTracks(childSelections, mayRetainStreamFlags,
|
||||||
mayRetainStreamFlags, childStreams, streamResetFlags, !seenFirstTrackSelection);
|
childStreams, streamResetFlags, positionUs, seenFirstTrackSelection, seekRequired);
|
||||||
boolean wrapperEnabled = false;
|
boolean wrapperEnabled = false;
|
||||||
for (int j = 0; j < selections.length; j++) {
|
for (int j = 0; j < selections.length; j++) {
|
||||||
if (selectionChildIndices[j] == i) {
|
if (selectionChildIndices[j] == i) {
|
||||||
@ -173,7 +177,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
|||||||
}
|
}
|
||||||
|
|
||||||
sequenceableLoader = new CompositeSequenceableLoader(enabledSampleStreamWrappers);
|
sequenceableLoader = new CompositeSequenceableLoader(enabledSampleStreamWrappers);
|
||||||
if (seenFirstTrackSelection && selectedNewTracks) {
|
if (seekRequired) {
|
||||||
seekToUs(positionUs);
|
seekToUs(positionUs);
|
||||||
// We'll need to reset renderers consuming from all streams due to the seek.
|
// We'll need to reset renderers consuming from all streams due to the seek.
|
||||||
for (int i = 0; i < selections.length; i++) {
|
for (int i = 0; i < selections.length; i++) {
|
||||||
@ -188,7 +192,9 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void discardBuffer(long positionUs) {
|
public void discardBuffer(long positionUs) {
|
||||||
// Do nothing.
|
for (HlsSampleStreamWrapper sampleStreamWrapper : enabledSampleStreamWrappers) {
|
||||||
|
sampleStreamWrapper.discardBuffer(positionUs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -47,7 +47,7 @@ import java.util.LinkedList;
|
|||||||
* {@link SampleStream}s from which the loaded media can be consumed.
|
* {@link SampleStream}s from which the loaded media can be consumed.
|
||||||
*/
|
*/
|
||||||
/* package */ final class HlsSampleStreamWrapper implements Loader.Callback<Chunk>,
|
/* package */ final class HlsSampleStreamWrapper implements Loader.Callback<Chunk>,
|
||||||
SequenceableLoader, ExtractorOutput, UpstreamFormatChangedListener {
|
Loader.ReleaseCallback, SequenceableLoader, ExtractorOutput, UpstreamFormatChangedListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A callback to be notified of events.
|
* A callback to be notified of events.
|
||||||
@ -165,21 +165,42 @@ import java.util.LinkedList;
|
|||||||
return trackGroups;
|
return trackGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the parent {@link HlsMediaPeriod} when a track selection occurs.
|
||||||
|
*
|
||||||
|
* @param selections The renderer track selections.
|
||||||
|
* @param mayRetainStreamFlags Flags indicating whether the existing sample stream can be retained
|
||||||
|
* for each selection. A {@code true} value indicates that the selection is unchanged, and
|
||||||
|
* that the caller does not require that the sample stream be recreated.
|
||||||
|
* @param streams The existing sample streams, which will be updated to reflect the provided
|
||||||
|
* selections.
|
||||||
|
* @param streamResetFlags Will be updated to indicate new sample streams, and sample streams that
|
||||||
|
* have been retained but with the requirement that the consuming renderer be reset.
|
||||||
|
* @param positionUs The current playback position in microseconds.
|
||||||
|
* @param seenFirstTrackSelection Whether we've already had the first track selection, meaning
|
||||||
|
* this is a subsequent selection.
|
||||||
|
* @param seekRequired Whether the parent {@link HlsMediaPeriod} is already guaranteed to perform
|
||||||
|
* a seek as part of the track selection
|
||||||
|
* @return Whether this wrapper requires the parent {@link HlsMediaPeriod} to perform a seek as
|
||||||
|
* part of the track selection.
|
||||||
|
*/
|
||||||
public boolean selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
|
public boolean selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
|
||||||
SampleStream[] streams, boolean[] streamResetFlags, boolean isFirstTrackSelection) {
|
SampleStream[] streams, boolean[] streamResetFlags, long positionUs,
|
||||||
|
boolean seenFirstTrackSelection, boolean seekRequired) {
|
||||||
Assertions.checkState(prepared);
|
Assertions.checkState(prepared);
|
||||||
// Disable old tracks.
|
int oldEnabledTrackCount = enabledTrackCount;
|
||||||
|
// Deselect old tracks.
|
||||||
for (int i = 0; i < selections.length; i++) {
|
for (int i = 0; i < selections.length; i++) {
|
||||||
if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
|
if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
|
||||||
int group = ((HlsSampleStream) streams[i]).group;
|
int group = ((HlsSampleStream) streams[i]).group;
|
||||||
setTrackGroupEnabledState(group, false);
|
setTrackGroupEnabledState(group, false);
|
||||||
sampleQueues.valueAt(group).disable();
|
|
||||||
streams[i] = null;
|
streams[i] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Enable new tracks.
|
// We'll always need to seek if we're making a selection having previously disabled all tracks.
|
||||||
|
seekRequired |= seenFirstTrackSelection && oldEnabledTrackCount == 0;
|
||||||
|
// Select new tracks.
|
||||||
TrackSelection primaryTrackSelection = null;
|
TrackSelection primaryTrackSelection = null;
|
||||||
boolean selectedNewTracks = false;
|
|
||||||
for (int i = 0; i < selections.length; i++) {
|
for (int i = 0; i < selections.length; i++) {
|
||||||
if (streams[i] == null && selections[i] != null) {
|
if (streams[i] == null && selections[i] != null) {
|
||||||
TrackSelection selection = selections[i];
|
TrackSelection selection = selections[i];
|
||||||
@ -191,37 +212,60 @@ import java.util.LinkedList;
|
|||||||
}
|
}
|
||||||
streams[i] = new HlsSampleStream(this, group);
|
streams[i] = new HlsSampleStream(this, group);
|
||||||
streamResetFlags[i] = true;
|
streamResetFlags[i] = true;
|
||||||
selectedNewTracks = true;
|
// If there's still a chance of avoiding a seek, try and seek within the sample queue.
|
||||||
}
|
if (!seekRequired) {
|
||||||
}
|
SampleQueue sampleQueue = sampleQueues.valueAt(group);
|
||||||
if (isFirstTrackSelection) {
|
sampleQueue.rewind();
|
||||||
// At the time of the first track selection all queues will be enabled, so we need to disable
|
// A seek can be avoided if we're able to advance to the current playback position in the
|
||||||
// any that are no longer required.
|
// sample queue, or if we haven't read anything from the queue since the previous seek
|
||||||
int sampleQueueCount = sampleQueues.size();
|
// (this case is common for sparse tracks such as metadata tracks). In all other cases a
|
||||||
for (int i = 0; i < sampleQueueCount; i++) {
|
// seek is required.
|
||||||
if (!groupEnabledStates[i]) {
|
seekRequired = !sampleQueue.advanceTo(positionUs, true, true)
|
||||||
sampleQueues.valueAt(i).disable();
|
&& sampleQueue.getReadIndex() != 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
if (primaryTrackSelection != null && !mediaChunks.isEmpty()) {
|
|
||||||
primaryTrackSelection.updateSelectedTrack(0);
|
|
||||||
int chunkIndex = chunkSource.getTrackGroup().indexOf(mediaChunks.getLast().trackFormat);
|
|
||||||
if (primaryTrackSelection.getSelectedIndexInTrackGroup() != chunkIndex) {
|
|
||||||
// The loaded preparation chunk does match the selection. We discard it.
|
|
||||||
seekTo(lastSeekPositionUs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cancel requests if necessary.
|
|
||||||
if (enabledTrackCount == 0) {
|
if (enabledTrackCount == 0) {
|
||||||
chunkSource.reset();
|
chunkSource.reset();
|
||||||
downstreamTrackFormat = null;
|
downstreamTrackFormat = null;
|
||||||
mediaChunks.clear();
|
mediaChunks.clear();
|
||||||
|
int sampleQueueCount = sampleQueues.size();
|
||||||
if (loader.isLoading()) {
|
if (loader.isLoading()) {
|
||||||
|
// Discard as much as we can synchronously.
|
||||||
|
for (int i = 0; i < sampleQueueCount; i++) {
|
||||||
|
sampleQueues.valueAt(i).discardToEnd();
|
||||||
|
}
|
||||||
loader.cancelLoading();
|
loader.cancelLoading();
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < sampleQueueCount; i++) {
|
||||||
|
sampleQueues.valueAt(i).reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the first selection and the chunk loaded during preparation does not match the
|
||||||
|
// selection, we call seekTo to discard it. Note that if seekRequired is true then the wrapping
|
||||||
|
// HlsMediaPeriod will call seekTo regardless, and so we do not need to perform the selection
|
||||||
|
// check here.
|
||||||
|
if (!seekRequired && !seenFirstTrackSelection && primaryTrackSelection != null
|
||||||
|
&& !mediaChunks.isEmpty()) {
|
||||||
|
primaryTrackSelection.updateSelectedTrack(0);
|
||||||
|
int chunkIndex = chunkSource.getTrackGroup().indexOf(mediaChunks.getLast().trackFormat);
|
||||||
|
if (primaryTrackSelection.getSelectedIndexInTrackGroup() != chunkIndex) {
|
||||||
|
// The loaded preparation chunk does not match the selection, so discard it.
|
||||||
|
seekTo(positionUs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return selectedNewTracks;
|
return seekRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void discardBuffer(long positionUs) {
|
||||||
|
int sampleQueueCount = sampleQueues.size();
|
||||||
|
for (int i = 0; i < sampleQueueCount; i++) {
|
||||||
|
sampleQueues.valueAt(i).discardTo(positionUs, false, groupEnabledStates[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void seekTo(long positionUs) {
|
public void seekTo(long positionUs) {
|
||||||
@ -234,7 +278,7 @@ import java.util.LinkedList;
|
|||||||
} else {
|
} else {
|
||||||
int sampleQueueCount = sampleQueues.size();
|
int sampleQueueCount = sampleQueues.size();
|
||||||
for (int i = 0; i < sampleQueueCount; i++) {
|
for (int i = 0; i < sampleQueueCount; i++) {
|
||||||
sampleQueues.valueAt(i).reset(groupEnabledStates[i]);
|
sampleQueues.valueAt(i).reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,15 +306,27 @@ import java.util.LinkedList;
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void release() {
|
public void release() {
|
||||||
int sampleQueueCount = sampleQueues.size();
|
boolean releasedSynchronously = loader.release(this);
|
||||||
for (int i = 0; i < sampleQueueCount; i++) {
|
if (prepared && !releasedSynchronously) {
|
||||||
sampleQueues.valueAt(i).disable();
|
// Discard as much as we can synchronously. We only do this if we're prepared, since otherwise
|
||||||
|
// sampleQueues may still be being modified by the loading thread.
|
||||||
|
int sampleQueueCount = sampleQueues.size();
|
||||||
|
for (int i = 0; i < sampleQueueCount; i++) {
|
||||||
|
sampleQueues.valueAt(i).discardToEnd();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
loader.release();
|
|
||||||
handler.removeCallbacksAndMessages(null);
|
handler.removeCallbacksAndMessages(null);
|
||||||
released = true;
|
released = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReleased() {
|
||||||
|
int sampleQueueCount = sampleQueues.size();
|
||||||
|
for (int i = 0; i < sampleQueueCount; i++) {
|
||||||
|
sampleQueues.valueAt(i).reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setIsTimestampMaster(boolean isTimestampMaster) {
|
public void setIsTimestampMaster(boolean isTimestampMaster) {
|
||||||
chunkSource.setIsTimestampMaster(isTimestampMaster);
|
chunkSource.setIsTimestampMaster(isTimestampMaster);
|
||||||
}
|
}
|
||||||
@ -310,16 +366,16 @@ import java.util.LinkedList;
|
|||||||
downstreamTrackFormat = trackFormat;
|
downstreamTrackFormat = trackFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sampleQueues.valueAt(group).readData(formatHolder, buffer, requireFormat,
|
return sampleQueues.valueAt(group).read(formatHolder, buffer, requireFormat, loadingFinished,
|
||||||
loadingFinished, lastSeekPositionUs);
|
lastSeekPositionUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ void skipData(int group, long positionUs) {
|
/* package */ void skipData(int group, long positionUs) {
|
||||||
SampleQueue sampleQueue = sampleQueues.valueAt(group);
|
SampleQueue sampleQueue = sampleQueues.valueAt(group);
|
||||||
if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) {
|
if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) {
|
||||||
sampleQueue.skipAll();
|
sampleQueue.advanceToEnd();
|
||||||
} else {
|
} else {
|
||||||
sampleQueue.skipToKeyframeBefore(positionUs, true);
|
sampleQueue.advanceTo(positionUs, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,9 +464,11 @@ import java.util.LinkedList;
|
|||||||
if (!released) {
|
if (!released) {
|
||||||
int sampleQueueCount = sampleQueues.size();
|
int sampleQueueCount = sampleQueues.size();
|
||||||
for (int i = 0; i < sampleQueueCount; i++) {
|
for (int i = 0; i < sampleQueueCount; i++) {
|
||||||
sampleQueues.valueAt(i).reset(groupEnabledStates[i]);
|
sampleQueues.valueAt(i).reset();
|
||||||
|
}
|
||||||
|
if (enabledTrackCount > 0) {
|
||||||
|
callback.onContinueLoadingRequested(this);
|
||||||
}
|
}
|
||||||
callback.onContinueLoadingRequested(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user