mirror of
https://github.com/androidx/media.git
synced 2025-05-06 15:10:34 +08:00
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.Format;
|
||||||
import com.google.android.exoplayer.drm.DrmInitData;
|
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.DataSource;
|
||||||
import com.google.android.exoplayer.upstream.DataSpec;
|
import com.google.android.exoplayer.upstream.DataSpec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base implementation of {@link MediaChunk}, for chunks that contain a single track.
|
* A base implementation of {@link MediaChunk}, for chunks that contain a single track.
|
||||||
* <p>
|
* <p>
|
||||||
* Loaded samples are output to a {@link DefaultTrackOutput}.
|
* Loaded samples are output to a {@link RollingSampleBuffer}.
|
||||||
*/
|
*/
|
||||||
public abstract class BaseMediaChunk extends MediaChunk {
|
public abstract class BaseMediaChunk extends MediaChunk {
|
||||||
|
|
||||||
private DefaultTrackOutput trackOutput;
|
private RollingSampleBuffer trackOutput;
|
||||||
private int firstSampleIndex;
|
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.
|
* samples as they are loaded.
|
||||||
*
|
*
|
||||||
* @param trackOutput The output that will receive the loaded samples.
|
* @param trackOutput The output that will receive the loaded samples.
|
||||||
*/
|
*/
|
||||||
public void init(DefaultTrackOutput trackOutput) {
|
public void init(RollingSampleBuffer trackOutput) {
|
||||||
this.trackOutput = trackOutput;
|
this.trackOutput = trackOutput;
|
||||||
this.firstSampleIndex = trackOutput.getWriteIndex();
|
this.firstSampleIndex = trackOutput.getWriteIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the index of the first sample in the output that was passed to
|
* 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() {
|
public final int getFirstSampleIndex() {
|
||||||
return firstSampleIndex;
|
return firstSampleIndex;
|
||||||
@ -72,9 +72,9 @@ public abstract class BaseMediaChunk extends MediaChunk {
|
|||||||
public abstract DrmInitData getDrmInitData();
|
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;
|
return trackOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ import com.google.android.exoplayer.TrackGroupArray;
|
|||||||
import com.google.android.exoplayer.TrackSelection;
|
import com.google.android.exoplayer.TrackSelection;
|
||||||
import com.google.android.exoplayer.TrackStream;
|
import com.google.android.exoplayer.TrackStream;
|
||||||
import com.google.android.exoplayer.chunk.ChunkSampleSourceEventListener.EventDispatcher;
|
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;
|
||||||
import com.google.android.exoplayer.upstream.Loader.Loadable;
|
import com.google.android.exoplayer.upstream.Loader.Loadable;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
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 ChunkHolder nextChunkHolder;
|
||||||
private final LinkedList<BaseMediaChunk> mediaChunks;
|
private final LinkedList<BaseMediaChunk> mediaChunks;
|
||||||
private final List<BaseMediaChunk> readOnlyMediaChunks;
|
private final List<BaseMediaChunk> readOnlyMediaChunks;
|
||||||
private final DefaultTrackOutput sampleQueue;
|
private final RollingSampleBuffer sampleQueue;
|
||||||
private final int bufferSizeContribution;
|
private final int bufferSizeContribution;
|
||||||
private final EventDispatcher eventDispatcher;
|
private final EventDispatcher eventDispatcher;
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
|||||||
nextChunkHolder = new ChunkHolder();
|
nextChunkHolder = new ChunkHolder();
|
||||||
mediaChunks = new LinkedList<>();
|
mediaChunks = new LinkedList<>();
|
||||||
readOnlyMediaChunks = Collections.unmodifiableList(mediaChunks);
|
readOnlyMediaChunks = Collections.unmodifiableList(mediaChunks);
|
||||||
sampleQueue = new DefaultTrackOutput(loadControl.getAllocator());
|
sampleQueue = new RollingSampleBuffer(loadControl.getAllocator());
|
||||||
pendingResetPositionUs = C.UNSET_TIME_US;
|
pendingResetPositionUs = C.UNSET_TIME_US;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,9 +222,9 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
|||||||
} else if (isPendingReset()) {
|
} else if (isPendingReset()) {
|
||||||
return pendingResetPositionUs;
|
return pendingResetPositionUs;
|
||||||
} else {
|
} else {
|
||||||
long largestParsedTimestampUs = sampleQueue.getLargestParsedTimestampUs();
|
long largestQueuedTimestampUs = sampleQueue.getLargestQueuedTimestampUs();
|
||||||
return largestParsedTimestampUs == Long.MIN_VALUE ? downstreamPositionUs
|
return largestQueuedTimestampUs == Long.MIN_VALUE ? downstreamPositionUs
|
||||||
: largestParsedTimestampUs;
|
: largestQueuedTimestampUs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,7 +316,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
|||||||
return NOTHING_READ;
|
return NOTHING_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sampleQueue.getSample(buffer)) {
|
if (sampleQueue.readSample(buffer)) {
|
||||||
if (buffer.timeUs < lastSeekPositionUs) {
|
if (buffer.timeUs < lastSeekPositionUs) {
|
||||||
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
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.chunk.ChunkExtractorWrapper.SingleTrackMetadataOutput;
|
||||||
import com.google.android.exoplayer.drm.DrmInitData;
|
import com.google.android.exoplayer.drm.DrmInitData;
|
||||||
import com.google.android.exoplayer.extractor.DefaultExtractorInput;
|
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.Extractor;
|
||||||
import com.google.android.exoplayer.extractor.ExtractorInput;
|
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.extractor.SeekMap;
|
||||||
import com.google.android.exoplayer.upstream.DataSource;
|
import com.google.android.exoplayer.upstream.DataSource;
|
||||||
import com.google.android.exoplayer.upstream.DataSpec;
|
import com.google.android.exoplayer.upstream.DataSpec;
|
||||||
@ -111,7 +111,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe
|
|||||||
loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
|
loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
|
||||||
if (bytesLoaded == 0) {
|
if (bytesLoaded == 0) {
|
||||||
// Set the target to ourselves.
|
// Set the target to ourselves.
|
||||||
DefaultTrackOutput trackOutput = getTrackOutput();
|
RollingSampleBuffer trackOutput = getTrackOutput();
|
||||||
trackOutput.formatWithOffset(sampleFormat, sampleOffsetUs);
|
trackOutput.formatWithOffset(sampleFormat, sampleOffsetUs);
|
||||||
extractorWrapper.init(this, trackOutput);
|
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.Format;
|
||||||
import com.google.android.exoplayer.drm.DrmInitData;
|
import com.google.android.exoplayer.drm.DrmInitData;
|
||||||
import com.google.android.exoplayer.extractor.DefaultExtractorInput;
|
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.ExtractorInput;
|
||||||
|
import com.google.android.exoplayer.extractor.RollingSampleBuffer;
|
||||||
import com.google.android.exoplayer.upstream.DataSource;
|
import com.google.android.exoplayer.upstream.DataSource;
|
||||||
import com.google.android.exoplayer.upstream.DataSpec;
|
import com.google.android.exoplayer.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer.util.Util;
|
import com.google.android.exoplayer.util.Util;
|
||||||
@ -91,7 +91,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
|
|||||||
length += bytesLoaded;
|
length += bytesLoaded;
|
||||||
}
|
}
|
||||||
ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length);
|
ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length);
|
||||||
DefaultTrackOutput trackOutput = getTrackOutput();
|
RollingSampleBuffer trackOutput = getTrackOutput();
|
||||||
trackOutput.formatWithOffset(sampleFormat, 0);
|
trackOutput.formatWithOffset(sampleFormat, 0);
|
||||||
// Load the sample data.
|
// Load the sample data.
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
@ -36,9 +36,6 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
private long lastReadTimeUs;
|
private long lastReadTimeUs;
|
||||||
private long spliceOutTimeUs;
|
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.
|
* @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;
|
needKeyframe = true;
|
||||||
lastReadTimeUs = Long.MIN_VALUE;
|
lastReadTimeUs = Long.MIN_VALUE;
|
||||||
spliceOutTimeUs = Long.MIN_VALUE;
|
spliceOutTimeUs = Long.MIN_VALUE;
|
||||||
largestParsedTimestampUs = Long.MIN_VALUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by the consuming thread, but only when there is no loading thread.
|
// 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;
|
needKeyframe = true;
|
||||||
lastReadTimeUs = Long.MIN_VALUE;
|
lastReadTimeUs = Long.MIN_VALUE;
|
||||||
spliceOutTimeUs = 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.
|
// Called by the consuming thread.
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current absolute read index.
|
|
||||||
*/
|
|
||||||
public int getReadIndex() {
|
|
||||||
return rollingBuffer.getReadIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current upstream {@link Format}.
|
* Returns the current upstream {@link Format}.
|
||||||
*/
|
*/
|
||||||
@ -98,19 +68,12 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
return rollingBuffer.getUpstreamFormat();
|
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
|
* The largest timestamp of any sample received by the output, or {@link Long#MIN_VALUE} if a
|
||||||
* sample has yet to be received.
|
* sample has yet to be received.
|
||||||
*/
|
*/
|
||||||
public long getLargestParsedTimestampUs() {
|
public long getLargestParsedTimestampUs() {
|
||||||
return largestParsedTimestampUs;
|
return rollingBuffer.getLargestQueuedTimestampUs();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,16 +111,6 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
rollingBuffer.skipAllSamples();
|
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.
|
* 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.
|
// 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
|
@Override
|
||||||
public void format(Format format) {
|
public void format(Format format) {
|
||||||
rollingBuffer.format(format);
|
rollingBuffer.format(format);
|
||||||
@ -247,7 +187,6 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) {
|
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);
|
rollingBuffer.sampleMetadata(timeUs, flags, size, offset, encryptionKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
|||||||
private final ExtractorHolder extractorHolder;
|
private final ExtractorHolder extractorHolder;
|
||||||
private final Allocator allocator;
|
private final Allocator allocator;
|
||||||
private final int requestedBufferSize;
|
private final int requestedBufferSize;
|
||||||
private final SparseArray<DefaultTrackOutput> sampleQueues;
|
private final SparseArray<RollingSampleBuffer> sampleQueues;
|
||||||
private final int minLoadableRetryCount;
|
private final int minLoadableRetryCount;
|
||||||
private final Uri uri;
|
private final Uri uri;
|
||||||
private final DataSource dataSource;
|
private final DataSource dataSource;
|
||||||
@ -425,13 +425,13 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
|||||||
} else if (isPendingReset()) {
|
} else if (isPendingReset()) {
|
||||||
return pendingResetPositionUs;
|
return pendingResetPositionUs;
|
||||||
} else {
|
} else {
|
||||||
long largestParsedTimestampUs = Long.MIN_VALUE;
|
long largestQueuedTimestampUs = Long.MIN_VALUE;
|
||||||
for (int i = 0; i < sampleQueues.size(); i++) {
|
for (int i = 0; i < sampleQueues.size(); i++) {
|
||||||
largestParsedTimestampUs = Math.max(largestParsedTimestampUs,
|
largestQueuedTimestampUs = Math.max(largestQueuedTimestampUs,
|
||||||
sampleQueues.valueAt(i).getLargestParsedTimestampUs());
|
sampleQueues.valueAt(i).getLargestQueuedTimestampUs());
|
||||||
}
|
}
|
||||||
return largestParsedTimestampUs == Long.MIN_VALUE ? downstreamPositionUs
|
return largestQueuedTimestampUs == Long.MIN_VALUE ? downstreamPositionUs
|
||||||
: largestParsedTimestampUs;
|
: largestQueuedTimestampUs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,7 +474,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
|||||||
return TrackStream.NOTHING_READ;
|
return TrackStream.NOTHING_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultTrackOutput sampleQueue = sampleQueues.valueAt(track);
|
RollingSampleBuffer sampleQueue = sampleQueues.valueAt(track);
|
||||||
if (pendingMediaFormat[track]) {
|
if (pendingMediaFormat[track]) {
|
||||||
formatHolder.format = sampleQueue.getUpstreamFormat();
|
formatHolder.format = sampleQueue.getUpstreamFormat();
|
||||||
formatHolder.drmInitData = drmInitData;
|
formatHolder.drmInitData = drmInitData;
|
||||||
@ -482,7 +482,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
|||||||
return TrackStream.FORMAT_READ;
|
return TrackStream.FORMAT_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sampleQueue.getSample(buffer)) {
|
if (sampleQueue.readSample(buffer)) {
|
||||||
if (buffer.timeUs < lastSeekPositionUs) {
|
if (buffer.timeUs < lastSeekPositionUs) {
|
||||||
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||||
}
|
}
|
||||||
@ -538,9 +538,9 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackOutput track(int id) {
|
public TrackOutput track(int id) {
|
||||||
DefaultTrackOutput sampleQueue = sampleQueues.get(id);
|
RollingSampleBuffer sampleQueue = sampleQueues.get(id);
|
||||||
if (sampleQueue == null) {
|
if (sampleQueue == null) {
|
||||||
sampleQueue = new DefaultTrackOutput(allocator);
|
sampleQueue = new RollingSampleBuffer(allocator);
|
||||||
sampleQueues.put(id, sampleQueue);
|
sampleQueues.put(id, sampleQueue);
|
||||||
}
|
}
|
||||||
return sampleQueue;
|
return sampleQueue;
|
||||||
|
@ -31,7 +31,7 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
/**
|
/**
|
||||||
* A rolling buffer of sample data and corresponding sample information.
|
* 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;
|
private static final int INITIAL_SCRATCH_SIZE = 32;
|
||||||
|
|
||||||
@ -129,6 +129,13 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
|
|
||||||
// Called by the consuming thread.
|
// Called by the consuming thread.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the buffer is empty.
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return infoQueue.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current absolute read index.
|
* Returns the current absolute read index.
|
||||||
*/
|
*/
|
||||||
@ -151,6 +158,20 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
return nextSampleFormat != null ? nextSampleFormat : upstreamFormat;
|
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.
|
* Fills {@code buffer} with information about the current sample, but does not write its data.
|
||||||
* <p>
|
* <p>
|
||||||
@ -470,6 +491,9 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
private int relativeReadIndex;
|
private int relativeReadIndex;
|
||||||
private int relativeWriteIndex;
|
private int relativeWriteIndex;
|
||||||
|
|
||||||
|
private long largestDequeuedTimestampUs;
|
||||||
|
private long largestQueuedTimestampUs;
|
||||||
|
|
||||||
public InfoQueue() {
|
public InfoQueue() {
|
||||||
capacity = SAMPLE_CAPACITY_INCREMENT;
|
capacity = SAMPLE_CAPACITY_INCREMENT;
|
||||||
offsets = new long[capacity];
|
offsets = new long[capacity];
|
||||||
@ -478,6 +502,8 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
sizes = new int[capacity];
|
sizes = new int[capacity];
|
||||||
encryptionKeys = new byte[capacity][];
|
encryptionKeys = new byte[capacity][];
|
||||||
formats = new Format[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.
|
// Called by the consuming thread, but only when there is no loading thread.
|
||||||
@ -490,6 +516,8 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
relativeReadIndex = 0;
|
relativeReadIndex = 0;
|
||||||
relativeWriteIndex = 0;
|
relativeWriteIndex = 0;
|
||||||
queueSize = 0;
|
queueSize = 0;
|
||||||
|
largestDequeuedTimestampUs = Long.MIN_VALUE;
|
||||||
|
largestQueuedTimestampUs = Long.MIN_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -520,6 +548,16 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
|
|
||||||
queueSize -= discardCount;
|
queueSize -= discardCount;
|
||||||
relativeWriteIndex = (relativeWriteIndex + capacity - discardCount) % capacity;
|
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];
|
return offsets[relativeWriteIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -532,6 +570,10 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
return absoluteReadIndex;
|
return absoluteReadIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isEmpty() {
|
||||||
|
return queueSize == 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link Format} of the next sample, or null of the queue is empty.
|
* 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];
|
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.
|
* 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
|
* 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() {
|
public synchronized long moveToNextSample() {
|
||||||
queueSize--;
|
queueSize--;
|
||||||
|
largestDequeuedTimestampUs = Math.max(largestDequeuedTimestampUs, timesUs[relativeReadIndex]);
|
||||||
int lastReadIndex = relativeReadIndex++;
|
int lastReadIndex = relativeReadIndex++;
|
||||||
absoluteReadIndex++;
|
absoluteReadIndex++;
|
||||||
if (relativeReadIndex == capacity) {
|
if (relativeReadIndex == capacity) {
|
||||||
@ -658,6 +715,7 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
flags[relativeWriteIndex] = sampleFlags;
|
flags[relativeWriteIndex] = sampleFlags;
|
||||||
encryptionKeys[relativeWriteIndex] = encryptionKey;
|
encryptionKeys[relativeWriteIndex] = encryptionKey;
|
||||||
formats[relativeWriteIndex] = format;
|
formats[relativeWriteIndex] = format;
|
||||||
|
largestQueuedTimestampUs = Math.max(largestQueuedTimestampUs, timeUs);
|
||||||
// Increment the write index.
|
// Increment the write index.
|
||||||
queueSize++;
|
queueSize++;
|
||||||
if (queueSize == capacity) {
|
if (queueSize == capacity) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user