Use RollingSampleBuffer directly for non-HLS.
This also fixes the largest queued timestamp to be the correct value if upstream samples are discarded. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=120207054
This commit is contained in:
parent
5ce210e374
commit
a760c9bfd9
@ -17,18 +17,18 @@ package com.google.android.exoplayer.chunk;
|
||||
|
||||
import com.google.android.exoplayer.Format;
|
||||
import com.google.android.exoplayer.drm.DrmInitData;
|
||||
import com.google.android.exoplayer.extractor.DefaultTrackOutput;
|
||||
import com.google.android.exoplayer.extractor.RollingSampleBuffer;
|
||||
import com.google.android.exoplayer.upstream.DataSource;
|
||||
import com.google.android.exoplayer.upstream.DataSpec;
|
||||
|
||||
/**
|
||||
* A base implementation of {@link MediaChunk}, for chunks that contain a single track.
|
||||
* <p>
|
||||
* Loaded samples are output to a {@link DefaultTrackOutput}.
|
||||
* Loaded samples are output to a {@link RollingSampleBuffer}.
|
||||
*/
|
||||
public abstract class BaseMediaChunk extends MediaChunk {
|
||||
|
||||
private DefaultTrackOutput trackOutput;
|
||||
private RollingSampleBuffer trackOutput;
|
||||
private int firstSampleIndex;
|
||||
|
||||
/**
|
||||
@ -46,19 +46,19 @@ public abstract class BaseMediaChunk extends MediaChunk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the chunk for loading, setting the {@link DefaultTrackOutput} that will receive
|
||||
* Initializes the chunk for loading, setting the {@link RollingSampleBuffer} that will receive
|
||||
* samples as they are loaded.
|
||||
*
|
||||
* @param trackOutput The output that will receive the loaded samples.
|
||||
*/
|
||||
public void init(DefaultTrackOutput trackOutput) {
|
||||
public void init(RollingSampleBuffer trackOutput) {
|
||||
this.trackOutput = trackOutput;
|
||||
this.firstSampleIndex = trackOutput.getWriteIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the first sample in the output that was passed to
|
||||
* {@link #init(DefaultTrackOutput)} that will originate from this chunk.
|
||||
* {@link #init(RollingSampleBuffer)} that will originate from this chunk.
|
||||
*/
|
||||
public final int getFirstSampleIndex() {
|
||||
return firstSampleIndex;
|
||||
@ -72,9 +72,9 @@ public abstract class BaseMediaChunk extends MediaChunk {
|
||||
public abstract DrmInitData getDrmInitData();
|
||||
|
||||
/**
|
||||
* Returns the track output most recently passed to {@link #init(DefaultTrackOutput)}.
|
||||
* Returns the track output most recently passed to {@link #init(RollingSampleBuffer)}.
|
||||
*/
|
||||
protected final DefaultTrackOutput getTrackOutput() {
|
||||
protected final RollingSampleBuffer getTrackOutput() {
|
||||
return trackOutput;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ import com.google.android.exoplayer.TrackGroupArray;
|
||||
import com.google.android.exoplayer.TrackSelection;
|
||||
import com.google.android.exoplayer.TrackStream;
|
||||
import com.google.android.exoplayer.chunk.ChunkSampleSourceEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer.extractor.DefaultTrackOutput;
|
||||
import com.google.android.exoplayer.extractor.RollingSampleBuffer;
|
||||
import com.google.android.exoplayer.upstream.Loader;
|
||||
import com.google.android.exoplayer.upstream.Loader.Loadable;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
@ -56,7 +56,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
private final ChunkHolder nextChunkHolder;
|
||||
private final LinkedList<BaseMediaChunk> mediaChunks;
|
||||
private final List<BaseMediaChunk> readOnlyMediaChunks;
|
||||
private final DefaultTrackOutput sampleQueue;
|
||||
private final RollingSampleBuffer sampleQueue;
|
||||
private final int bufferSizeContribution;
|
||||
private final EventDispatcher eventDispatcher;
|
||||
|
||||
@ -125,7 +125,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
nextChunkHolder = new ChunkHolder();
|
||||
mediaChunks = new LinkedList<>();
|
||||
readOnlyMediaChunks = Collections.unmodifiableList(mediaChunks);
|
||||
sampleQueue = new DefaultTrackOutput(loadControl.getAllocator());
|
||||
sampleQueue = new RollingSampleBuffer(loadControl.getAllocator());
|
||||
pendingResetPositionUs = C.UNSET_TIME_US;
|
||||
}
|
||||
|
||||
@ -222,9 +222,9 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
} else if (isPendingReset()) {
|
||||
return pendingResetPositionUs;
|
||||
} else {
|
||||
long largestParsedTimestampUs = sampleQueue.getLargestParsedTimestampUs();
|
||||
return largestParsedTimestampUs == Long.MIN_VALUE ? downstreamPositionUs
|
||||
: largestParsedTimestampUs;
|
||||
long largestQueuedTimestampUs = sampleQueue.getLargestQueuedTimestampUs();
|
||||
return largestQueuedTimestampUs == Long.MIN_VALUE ? downstreamPositionUs
|
||||
: largestQueuedTimestampUs;
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,7 +316,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
return NOTHING_READ;
|
||||
}
|
||||
|
||||
if (sampleQueue.getSample(buffer)) {
|
||||
if (sampleQueue.readSample(buffer)) {
|
||||
if (buffer.timeUs < lastSeekPositionUs) {
|
||||
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||
}
|
||||
|
@ -19,9 +19,9 @@ import com.google.android.exoplayer.Format;
|
||||
import com.google.android.exoplayer.chunk.ChunkExtractorWrapper.SingleTrackMetadataOutput;
|
||||
import com.google.android.exoplayer.drm.DrmInitData;
|
||||
import com.google.android.exoplayer.extractor.DefaultExtractorInput;
|
||||
import com.google.android.exoplayer.extractor.DefaultTrackOutput;
|
||||
import com.google.android.exoplayer.extractor.Extractor;
|
||||
import com.google.android.exoplayer.extractor.ExtractorInput;
|
||||
import com.google.android.exoplayer.extractor.RollingSampleBuffer;
|
||||
import com.google.android.exoplayer.extractor.SeekMap;
|
||||
import com.google.android.exoplayer.upstream.DataSource;
|
||||
import com.google.android.exoplayer.upstream.DataSpec;
|
||||
@ -111,7 +111,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe
|
||||
loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
|
||||
if (bytesLoaded == 0) {
|
||||
// Set the target to ourselves.
|
||||
DefaultTrackOutput trackOutput = getTrackOutput();
|
||||
RollingSampleBuffer trackOutput = getTrackOutput();
|
||||
trackOutput.formatWithOffset(sampleFormat, sampleOffsetUs);
|
||||
extractorWrapper.init(this, trackOutput);
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.Format;
|
||||
import com.google.android.exoplayer.drm.DrmInitData;
|
||||
import com.google.android.exoplayer.extractor.DefaultExtractorInput;
|
||||
import com.google.android.exoplayer.extractor.DefaultTrackOutput;
|
||||
import com.google.android.exoplayer.extractor.ExtractorInput;
|
||||
import com.google.android.exoplayer.extractor.RollingSampleBuffer;
|
||||
import com.google.android.exoplayer.upstream.DataSource;
|
||||
import com.google.android.exoplayer.upstream.DataSpec;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
@ -91,7 +91,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
|
||||
length += bytesLoaded;
|
||||
}
|
||||
ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length);
|
||||
DefaultTrackOutput trackOutput = getTrackOutput();
|
||||
RollingSampleBuffer trackOutput = getTrackOutput();
|
||||
trackOutput.formatWithOffset(sampleFormat, 0);
|
||||
// Load the sample data.
|
||||
int result = 0;
|
||||
|
@ -36,9 +36,6 @@ public final class DefaultTrackOutput implements TrackOutput {
|
||||
private long lastReadTimeUs;
|
||||
private long spliceOutTimeUs;
|
||||
|
||||
// Accessed by both the loading and consuming threads.
|
||||
private volatile long largestParsedTimestampUs;
|
||||
|
||||
/**
|
||||
* @param allocator An {@link Allocator} from which allocations for sample data can be obtained.
|
||||
*/
|
||||
@ -48,7 +45,6 @@ public final class DefaultTrackOutput implements TrackOutput {
|
||||
needKeyframe = true;
|
||||
lastReadTimeUs = Long.MIN_VALUE;
|
||||
spliceOutTimeUs = Long.MIN_VALUE;
|
||||
largestParsedTimestampUs = Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
// Called by the consuming thread, but only when there is no loading thread.
|
||||
@ -61,36 +57,10 @@ public final class DefaultTrackOutput implements TrackOutput {
|
||||
needKeyframe = true;
|
||||
lastReadTimeUs = Long.MIN_VALUE;
|
||||
spliceOutTimeUs = Long.MIN_VALUE;
|
||||
largestParsedTimestampUs = Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current absolute write index.
|
||||
*/
|
||||
public int getWriteIndex() {
|
||||
return rollingBuffer.getWriteIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Discards samples from the write side of the queue.
|
||||
*
|
||||
* @param discardFromIndex The absolute index of the first sample to be discarded.
|
||||
*/
|
||||
public void discardUpstreamSamples(int discardFromIndex) {
|
||||
rollingBuffer.discardUpstreamSamples(discardFromIndex);
|
||||
largestParsedTimestampUs = rollingBuffer.peekSample(sampleBuffer) ? sampleBuffer.timeUs
|
||||
: Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
// Called by the consuming thread.
|
||||
|
||||
/**
|
||||
* Returns the current absolute read index.
|
||||
*/
|
||||
public int getReadIndex() {
|
||||
return rollingBuffer.getReadIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current upstream {@link Format}.
|
||||
*/
|
||||
@ -98,19 +68,12 @@ public final class DefaultTrackOutput implements TrackOutput {
|
||||
return rollingBuffer.getUpstreamFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current downstream {@link Format}.
|
||||
*/
|
||||
public Format getDownstreamFormat() {
|
||||
return rollingBuffer.getDownstreamFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* The largest timestamp of any sample received by the output, or {@link Long#MIN_VALUE} if a
|
||||
* sample has yet to be received.
|
||||
*/
|
||||
public long getLargestParsedTimestampUs() {
|
||||
return largestParsedTimestampUs;
|
||||
return rollingBuffer.getLargestQueuedTimestampUs();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,16 +111,6 @@ public final class DefaultTrackOutput implements TrackOutput {
|
||||
rollingBuffer.skipAllSamples();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to skip to the keyframe before the specified time, if it's present in the buffer.
|
||||
*
|
||||
* @param timeUs The seek time.
|
||||
* @return True if the skip was successful. False otherwise.
|
||||
*/
|
||||
public boolean skipToKeyframeBefore(long timeUs) {
|
||||
return rollingBuffer.skipToKeyframeBefore(timeUs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to configure a splice from this queue to the next.
|
||||
*
|
||||
@ -216,19 +169,6 @@ public final class DefaultTrackOutput implements TrackOutput {
|
||||
|
||||
// Called by the loading thread.
|
||||
|
||||
/**
|
||||
* Like {@link #format(Format)}, but with an offset that will be added to the timestamps of
|
||||
* samples subsequently queued to the buffer. The offset is also used to adjust
|
||||
* {@link Format#subsampleOffsetUs} for both the {@link Format} passed and those subsequently
|
||||
* passed to {@link #format(Format)}.
|
||||
*
|
||||
* @param format The format.
|
||||
* @param sampleOffsetUs The offset in microseconds.
|
||||
*/
|
||||
public void formatWithOffset(Format format, long sampleOffsetUs) {
|
||||
rollingBuffer.formatWithOffset(format, sampleOffsetUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void format(Format format) {
|
||||
rollingBuffer.format(format);
|
||||
@ -247,7 +187,6 @@ public final class DefaultTrackOutput implements TrackOutput {
|
||||
|
||||
@Override
|
||||
public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) {
|
||||
largestParsedTimestampUs = Math.max(largestParsedTimestampUs, timeUs);
|
||||
rollingBuffer.sampleMetadata(timeUs, flags, size, offset, encryptionKey);
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||
private final ExtractorHolder extractorHolder;
|
||||
private final Allocator allocator;
|
||||
private final int requestedBufferSize;
|
||||
private final SparseArray<DefaultTrackOutput> sampleQueues;
|
||||
private final SparseArray<RollingSampleBuffer> sampleQueues;
|
||||
private final int minLoadableRetryCount;
|
||||
private final Uri uri;
|
||||
private final DataSource dataSource;
|
||||
@ -425,13 +425,13 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||
} else if (isPendingReset()) {
|
||||
return pendingResetPositionUs;
|
||||
} else {
|
||||
long largestParsedTimestampUs = Long.MIN_VALUE;
|
||||
long largestQueuedTimestampUs = Long.MIN_VALUE;
|
||||
for (int i = 0; i < sampleQueues.size(); i++) {
|
||||
largestParsedTimestampUs = Math.max(largestParsedTimestampUs,
|
||||
sampleQueues.valueAt(i).getLargestParsedTimestampUs());
|
||||
largestQueuedTimestampUs = Math.max(largestQueuedTimestampUs,
|
||||
sampleQueues.valueAt(i).getLargestQueuedTimestampUs());
|
||||
}
|
||||
return largestParsedTimestampUs == Long.MIN_VALUE ? downstreamPositionUs
|
||||
: largestParsedTimestampUs;
|
||||
return largestQueuedTimestampUs == Long.MIN_VALUE ? downstreamPositionUs
|
||||
: largestQueuedTimestampUs;
|
||||
}
|
||||
}
|
||||
|
||||
@ -474,7 +474,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||
return TrackStream.NOTHING_READ;
|
||||
}
|
||||
|
||||
DefaultTrackOutput sampleQueue = sampleQueues.valueAt(track);
|
||||
RollingSampleBuffer sampleQueue = sampleQueues.valueAt(track);
|
||||
if (pendingMediaFormat[track]) {
|
||||
formatHolder.format = sampleQueue.getUpstreamFormat();
|
||||
formatHolder.drmInitData = drmInitData;
|
||||
@ -482,7 +482,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||
return TrackStream.FORMAT_READ;
|
||||
}
|
||||
|
||||
if (sampleQueue.getSample(buffer)) {
|
||||
if (sampleQueue.readSample(buffer)) {
|
||||
if (buffer.timeUs < lastSeekPositionUs) {
|
||||
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||
}
|
||||
@ -538,9 +538,9 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||
|
||||
@Override
|
||||
public TrackOutput track(int id) {
|
||||
DefaultTrackOutput sampleQueue = sampleQueues.get(id);
|
||||
RollingSampleBuffer sampleQueue = sampleQueues.get(id);
|
||||
if (sampleQueue == null) {
|
||||
sampleQueue = new DefaultTrackOutput(allocator);
|
||||
sampleQueue = new RollingSampleBuffer(allocator);
|
||||
sampleQueues.put(id, sampleQueue);
|
||||
}
|
||||
return sampleQueue;
|
||||
|
@ -31,7 +31,7 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||
/**
|
||||
* A rolling buffer of sample data and corresponding sample information.
|
||||
*/
|
||||
/* package */ final class RollingSampleBuffer implements TrackOutput {
|
||||
public final class RollingSampleBuffer implements TrackOutput {
|
||||
|
||||
private static final int INITIAL_SCRATCH_SIZE = 32;
|
||||
|
||||
@ -129,6 +129,13 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
// Called by the consuming thread.
|
||||
|
||||
/**
|
||||
* Returns whether the buffer is empty.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return infoQueue.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current absolute read index.
|
||||
*/
|
||||
@ -151,6 +158,20 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||
return nextSampleFormat != null ? nextSampleFormat : upstreamFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the largest sample timestamp that has been queued since the last {@link #clear()}.
|
||||
* <p>
|
||||
* Samples that were discarded by calling {@link #discardUpstreamSamples(int)} are not
|
||||
* considered as having been queued. Samples that were dequeued from the front of the queue are
|
||||
* considered as having been queued.
|
||||
*
|
||||
* @return The largest sample timestamp that has been queued, or {@link Long#MIN_VALUE} if no
|
||||
* samples have been queued.
|
||||
*/
|
||||
public long getLargestQueuedTimestampUs() {
|
||||
return infoQueue.getLargestQueuedTimestampUs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills {@code buffer} with information about the current sample, but does not write its data.
|
||||
* <p>
|
||||
@ -470,6 +491,9 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||
private int relativeReadIndex;
|
||||
private int relativeWriteIndex;
|
||||
|
||||
private long largestDequeuedTimestampUs;
|
||||
private long largestQueuedTimestampUs;
|
||||
|
||||
public InfoQueue() {
|
||||
capacity = SAMPLE_CAPACITY_INCREMENT;
|
||||
offsets = new long[capacity];
|
||||
@ -478,6 +502,8 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||
sizes = new int[capacity];
|
||||
encryptionKeys = new byte[capacity][];
|
||||
formats = new Format[capacity];
|
||||
largestDequeuedTimestampUs = Long.MIN_VALUE;
|
||||
largestQueuedTimestampUs = Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
// Called by the consuming thread, but only when there is no loading thread.
|
||||
@ -490,6 +516,8 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||
relativeReadIndex = 0;
|
||||
relativeWriteIndex = 0;
|
||||
queueSize = 0;
|
||||
largestDequeuedTimestampUs = Long.MIN_VALUE;
|
||||
largestQueuedTimestampUs = Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -520,6 +548,16 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
queueSize -= discardCount;
|
||||
relativeWriteIndex = (relativeWriteIndex + capacity - discardCount) % capacity;
|
||||
// Update the largest queued timestamp, assuming that the timestamps prior to a keyframe are
|
||||
// always less than the timestamp of the keyframe itself, and of subsequent frames.
|
||||
largestQueuedTimestampUs = Long.MIN_VALUE;
|
||||
for (int i = queueSize - 1; i >= 0; i--) {
|
||||
int sampleIndex = (relativeReadIndex + i) % capacity;
|
||||
largestQueuedTimestampUs = Math.max(largestQueuedTimestampUs, timesUs[sampleIndex]);
|
||||
if ((flags[sampleIndex] & C.BUFFER_FLAG_KEY_FRAME) != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return offsets[relativeWriteIndex];
|
||||
}
|
||||
|
||||
@ -532,6 +570,10 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||
return absoluteReadIndex;
|
||||
}
|
||||
|
||||
public synchronized boolean isEmpty() {
|
||||
return queueSize == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Format} of the next sample, or null of the queue is empty.
|
||||
*/
|
||||
@ -542,6 +584,20 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||
return formats[relativeReadIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the largest sample timestamp that has been queued since the last {@link #clear()}.
|
||||
* <p>
|
||||
* Samples that were discarded by calling {@link #discardUpstreamSamples(int)} are not
|
||||
* considered as having been queued. Samples that were dequeued from the front of the queue are
|
||||
* considered as having been queued.
|
||||
*
|
||||
* @return The largest sample timestamp that has been queued, or {@link Long#MIN_VALUE} if no
|
||||
* samples have been queued.
|
||||
*/
|
||||
public synchronized long getLargestQueuedTimestampUs() {
|
||||
return Math.max(largestDequeuedTimestampUs, largestQueuedTimestampUs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills {@code buffer} with information about the current sample, but does not write its data.
|
||||
* The absolute position of the sample's data in the rolling buffer is stored in
|
||||
@ -575,6 +631,7 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||
*/
|
||||
public synchronized long moveToNextSample() {
|
||||
queueSize--;
|
||||
largestDequeuedTimestampUs = Math.max(largestDequeuedTimestampUs, timesUs[relativeReadIndex]);
|
||||
int lastReadIndex = relativeReadIndex++;
|
||||
absoluteReadIndex++;
|
||||
if (relativeReadIndex == capacity) {
|
||||
@ -658,6 +715,7 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||
flags[relativeWriteIndex] = sampleFlags;
|
||||
encryptionKeys[relativeWriteIndex] = encryptionKey;
|
||||
formats[relativeWriteIndex] = format;
|
||||
largestQueuedTimestampUs = Math.max(largestQueuedTimestampUs, timeUs);
|
||||
// Increment the write index.
|
||||
queueSize++;
|
||||
if (queueSize == capacity) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user