Fix thread-safety issue using DefaultTrackOutput.
Reading the format and/or a sample needs to be done as a single operation. Else you can have a situation where the queue is initially empty, and this happens: 1) Read downstream format X 2) Read downstream format X (unchanged) 3) Write format Y 4) Write first sample 5) Read first sample The first sample then appears to be format X rather than Y. Note that readData in the SampleSource implementations always looks roughly the same. readReset is identical in all cases. isReady is identical in all cases now I've fixed them to be that way. So it should be pretty easy to get DefaultTrackOutput to implement TrackStream directly, at which point a whole load of duplication will disappear. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=120546377
This commit is contained in:
parent
90b7081824
commit
9a893e3003
@ -67,7 +67,6 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
|||||||
private TrackGroupArray trackGroups;
|
private TrackGroupArray trackGroups;
|
||||||
private boolean trackEnabled;
|
private boolean trackEnabled;
|
||||||
private boolean pendingReset;
|
private boolean pendingReset;
|
||||||
private Format downstreamSampleFormat;
|
|
||||||
|
|
||||||
private long downstreamPositionUs;
|
private long downstreamPositionUs;
|
||||||
private long lastSeekPositionUs;
|
private long lastSeekPositionUs;
|
||||||
@ -196,7 +195,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
|||||||
loadControl.register(this, bufferSizeContribution);
|
loadControl.register(this, bufferSizeContribution);
|
||||||
}
|
}
|
||||||
downstreamFormat = null;
|
downstreamFormat = null;
|
||||||
downstreamSampleFormat = null;
|
sampleQueue.needDownstreamFormat();
|
||||||
downstreamPositionUs = positionUs;
|
downstreamPositionUs = positionUs;
|
||||||
lastSeekPositionUs = positionUs;
|
lastSeekPositionUs = positionUs;
|
||||||
pendingReset = false;
|
pendingReset = false;
|
||||||
@ -265,7 +264,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isReady() {
|
public boolean isReady() {
|
||||||
return loadingFinished || !sampleQueue.isEmpty();
|
return loadingFinished || (!isPendingReset() && !sampleQueue.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -289,44 +288,33 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
|||||||
return NOTHING_READ;
|
return NOTHING_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseMediaChunk currentChunk = mediaChunks.getFirst();
|
|
||||||
while (mediaChunks.size() > 1
|
while (mediaChunks.size() > 1
|
||||||
&& mediaChunks.get(1).getFirstSampleIndex() <= sampleQueue.getReadIndex()) {
|
&& mediaChunks.get(1).getFirstSampleIndex() <= sampleQueue.getReadIndex()) {
|
||||||
mediaChunks.removeFirst();
|
mediaChunks.removeFirst();
|
||||||
currentChunk = mediaChunks.getFirst();
|
|
||||||
}
|
}
|
||||||
|
BaseMediaChunk currentChunk = mediaChunks.getFirst();
|
||||||
if (downstreamFormat == null || !downstreamFormat.equals(currentChunk.format)) {
|
Format currentFormat = currentChunk.format;
|
||||||
eventDispatcher.downstreamFormatChanged(currentChunk.format, currentChunk.trigger,
|
if (downstreamFormat == null || !downstreamFormat.equals(currentFormat)) {
|
||||||
|
eventDispatcher.downstreamFormatChanged(currentFormat, currentChunk.trigger,
|
||||||
currentChunk.startTimeUs);
|
currentChunk.startTimeUs);
|
||||||
downstreamFormat = currentChunk.format;
|
downstreamFormat = currentFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sampleQueue.isEmpty()) {
|
int result = sampleQueue.readData(formatHolder, buffer, loadingFinished);
|
||||||
if (loadingFinished) {
|
switch (result) {
|
||||||
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
case FORMAT_READ:
|
||||||
return BUFFER_READ;
|
formatHolder.drmInitData = currentChunk.getDrmInitData();
|
||||||
}
|
break;
|
||||||
return NOTHING_READ;
|
case BUFFER_READ:
|
||||||
|
if (!buffer.isEndOfStream()) {
|
||||||
|
if (buffer.timeUs < lastSeekPositionUs) {
|
||||||
|
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||||
|
}
|
||||||
|
onSampleRead(currentChunk, buffer);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
Format sampleFormat = sampleQueue.getDownstreamFormat();
|
|
||||||
if (!sampleFormat.equals(downstreamSampleFormat)) {
|
|
||||||
formatHolder.format = sampleFormat;
|
|
||||||
formatHolder.drmInitData = currentChunk.getDrmInitData();
|
|
||||||
downstreamSampleFormat = sampleFormat;
|
|
||||||
return FORMAT_READ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sampleQueue.readSample(buffer)) {
|
|
||||||
if (buffer.timeUs < lastSeekPositionUs) {
|
|
||||||
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
|
||||||
}
|
|
||||||
onSampleRead(currentChunk, buffer);
|
|
||||||
return BUFFER_READ;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NOTHING_READ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loader.Callback implementation.
|
// Loader.Callback implementation.
|
||||||
|
@ -18,6 +18,8 @@ package com.google.android.exoplayer.extractor;
|
|||||||
import com.google.android.exoplayer.C;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.DecoderInputBuffer;
|
import com.google.android.exoplayer.DecoderInputBuffer;
|
||||||
import com.google.android.exoplayer.Format;
|
import com.google.android.exoplayer.Format;
|
||||||
|
import com.google.android.exoplayer.FormatHolder;
|
||||||
|
import com.google.android.exoplayer.TrackStream;
|
||||||
import com.google.android.exoplayer.upstream.Allocation;
|
import com.google.android.exoplayer.upstream.Allocation;
|
||||||
import com.google.android.exoplayer.upstream.Allocator;
|
import com.google.android.exoplayer.upstream.Allocator;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
@ -46,6 +48,7 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
|
|
||||||
// Accessed only by the consuming thread.
|
// Accessed only by the consuming thread.
|
||||||
private long totalBytesDropped;
|
private long totalBytesDropped;
|
||||||
|
private Format downstreamFormat;
|
||||||
|
|
||||||
// Accessed only by the loading thread (or the consuming thread when there is no loading thread).
|
// Accessed only by the loading thread (or the consuming thread when there is no loading thread).
|
||||||
private long sampleOffsetUs;
|
private long sampleOffsetUs;
|
||||||
@ -55,9 +58,6 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
private boolean needKeyframe;
|
private boolean needKeyframe;
|
||||||
private boolean pendingSplice;
|
private boolean pendingSplice;
|
||||||
|
|
||||||
// Accessed by both the loading and consuming threads.
|
|
||||||
private volatile Format upstreamFormat;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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.
|
||||||
*/
|
*/
|
||||||
@ -89,6 +89,14 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
needKeyframe = true;
|
needKeyframe = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that {@link #readData(FormatHolder, DecoderInputBuffer, boolean)} should provide
|
||||||
|
* the sample format before any samples, even if it has already been provided.
|
||||||
|
*/
|
||||||
|
public void needDownstreamFormat() {
|
||||||
|
downstreamFormat = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that samples subsequently queued to the buffer should be spliced into those already
|
* Indicates that samples subsequently queued to the buffer should be spliced into those already
|
||||||
* queued.
|
* queued.
|
||||||
@ -157,18 +165,10 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current upstream {@link Format}.
|
* Returns the upstream {@link Format} in which samples are being queued.
|
||||||
*/
|
*/
|
||||||
public Format getUpstreamFormat() {
|
public Format getUpstreamFormat() {
|
||||||
return upstreamFormat;
|
return infoQueue.getUpstreamFormat();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current downstream {@link Format}.
|
|
||||||
*/
|
|
||||||
public Format getDownstreamFormat() {
|
|
||||||
Format nextSampleFormat = infoQueue.peekFormat();
|
|
||||||
return nextSampleFormat != null ? nextSampleFormat : upstreamFormat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -212,28 +212,44 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the current sample, advancing the read index to the next sample.
|
* Attempts to read from the queue.
|
||||||
*
|
*
|
||||||
* @param buffer The buffer into which the current sample should be written.
|
* @param formatHolder A {@link FormatHolder} to populate in the case of reading a format.
|
||||||
* @return True if a sample was read. False if there is no current sample.
|
* @param buffer A {@link DecoderInputBuffer} to populate in the case of reading a sample or the
|
||||||
|
* end of the stream. If the caller requires the sample data then it must ensure that
|
||||||
|
* {@link DecoderInputBuffer#data} references a valid buffer. If the end of the stream has
|
||||||
|
* been reached, the {@link C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer.
|
||||||
|
* @param loadingFinished True if an empty queue should be considered the end of the stream.
|
||||||
|
* @return The result, which can be {@link TrackStream#NOTHING_READ},
|
||||||
|
* {@link TrackStream#FORMAT_READ} or {@link TrackStream#BUFFER_READ}.
|
||||||
*/
|
*/
|
||||||
public boolean readSample(DecoderInputBuffer buffer) {
|
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
|
||||||
|
boolean loadingFinished) {
|
||||||
// Write the sample information into the buffer and extrasHolder.
|
// Write the sample information into the buffer and extrasHolder.
|
||||||
boolean haveSample = infoQueue.readSample(buffer, extrasHolder);
|
int result = infoQueue.readData(formatHolder, buffer, downstreamFormat, extrasHolder);
|
||||||
if (!haveSample) {
|
switch (result) {
|
||||||
return false;
|
case TrackStream.NOTHING_READ:
|
||||||
|
if (loadingFinished) {
|
||||||
|
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
|
||||||
|
return TrackStream.BUFFER_READ;
|
||||||
|
}
|
||||||
|
return TrackStream.NOTHING_READ;
|
||||||
|
case TrackStream.FORMAT_READ:
|
||||||
|
downstreamFormat = formatHolder.format;
|
||||||
|
break;
|
||||||
|
case TrackStream.BUFFER_READ:
|
||||||
|
// Read encryption data if the sample is encrypted.
|
||||||
|
if (buffer.isEncrypted()) {
|
||||||
|
readEncryptionData(buffer, extrasHolder);
|
||||||
|
}
|
||||||
|
// Write the sample data into the holder.
|
||||||
|
buffer.ensureSpaceForWrite(buffer.size);
|
||||||
|
readData(extrasHolder.offset, buffer.data, buffer.size);
|
||||||
|
// Advance the read head.
|
||||||
|
dropDownstreamTo(extrasHolder.nextOffset);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
// Read encryption data if the sample is encrypted.
|
|
||||||
if (buffer.isEncrypted()) {
|
|
||||||
readEncryptionData(buffer, extrasHolder);
|
|
||||||
}
|
|
||||||
// Write the sample data into the holder.
|
|
||||||
buffer.ensureSpaceForWrite(buffer.size);
|
|
||||||
readData(extrasHolder.offset, buffer.data, buffer.size);
|
|
||||||
// Advance the read head.
|
|
||||||
dropDownstreamTo(extrasHolder.nextOffset);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -392,7 +408,7 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void format(Format format) {
|
public void format(Format format) {
|
||||||
upstreamFormat = getAdjustedSampleFormat(format, sampleOffsetUs);
|
infoQueue.format(getAdjustedSampleFormat(format, sampleOffsetUs));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -444,7 +460,7 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
}
|
}
|
||||||
timeUs += sampleOffsetUs;
|
timeUs += sampleOffsetUs;
|
||||||
long absoluteOffset = totalBytesWritten - size - offset;
|
long absoluteOffset = totalBytesWritten - size - offset;
|
||||||
infoQueue.commitSample(timeUs, flags, absoluteOffset, size, encryptionKey, upstreamFormat);
|
infoQueue.commitSample(timeUs, flags, absoluteOffset, size, encryptionKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -500,6 +516,7 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
|
|
||||||
private long largestDequeuedTimestampUs;
|
private long largestDequeuedTimestampUs;
|
||||||
private long largestQueuedTimestampUs;
|
private long largestQueuedTimestampUs;
|
||||||
|
private Format upstreamFormat;
|
||||||
|
|
||||||
public InfoQueue() {
|
public InfoQueue() {
|
||||||
capacity = SAMPLE_CAPACITY_INCREMENT;
|
capacity = SAMPLE_CAPACITY_INCREMENT;
|
||||||
@ -577,18 +594,18 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
return absoluteReadIndex;
|
return absoluteReadIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the queue is empty.
|
||||||
|
*/
|
||||||
public synchronized boolean isEmpty() {
|
public synchronized boolean isEmpty() {
|
||||||
return queueSize == 0;
|
return queueSize == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link Format} of the next sample, or null of the queue is empty.
|
* Returns the upstream {@link Format} in which samples are being queued.
|
||||||
*/
|
*/
|
||||||
public synchronized Format peekFormat() {
|
public synchronized Format getUpstreamFormat() {
|
||||||
if (queueSize == 0) {
|
return upstreamFormat;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return formats[relativeReadIndex];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -606,23 +623,34 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fills {@code buffer} with information about the current sample, but does not write its data.
|
* Attempts to read from the queue.
|
||||||
* The absolute position of the sample's data in the rolling buffer is stored in
|
|
||||||
* {@code extrasHolder}, along with an encryption id if present, and the absolute position of
|
|
||||||
* the first byte that may still be required after the current sample has been read.
|
|
||||||
* <p>
|
|
||||||
* Populates {@link DecoderInputBuffer#size}, {@link DecoderInputBuffer#timeUs}, the buffer
|
|
||||||
* flags and {@code extrasHolder}.
|
|
||||||
*
|
*
|
||||||
* @param buffer The buffer into which the current sample information should be written.
|
* @param formatHolder A {@link FormatHolder} to populate in the case of reading a format.
|
||||||
|
* @param buffer A {@link DecoderInputBuffer} to populate in the case of reading a sample or the
|
||||||
|
* end of the stream. If a sample is read then the buffer is populated with information
|
||||||
|
* about the sample, but not its data. The absolute position of the data in the rolling
|
||||||
|
* buffer is stored in {@code extrasHolder}, along with an encryption id if present and the
|
||||||
|
* absolute position of the first byte that may still be required after the current sample
|
||||||
|
* has been read.
|
||||||
* @param extrasHolder The holder into which extra sample information should be written.
|
* @param extrasHolder The holder into which extra sample information should be written.
|
||||||
* @return True if the buffer and extras were filled. False if there is no current sample.
|
* @return The result, which can be {@link TrackStream#NOTHING_READ},
|
||||||
|
* {@link TrackStream#FORMAT_READ} or {@link TrackStream#BUFFER_READ}.
|
||||||
*/
|
*/
|
||||||
public synchronized boolean readSample(DecoderInputBuffer buffer,
|
public synchronized int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
|
||||||
BufferExtrasHolder extrasHolder) {
|
Format downstreamFormat, BufferExtrasHolder extrasHolder) {
|
||||||
if (queueSize == 0) {
|
if (queueSize == 0) {
|
||||||
return false;
|
if (upstreamFormat != null && !upstreamFormat.equals(downstreamFormat)) {
|
||||||
|
formatHolder.format = upstreamFormat;
|
||||||
|
return TrackStream.FORMAT_READ;
|
||||||
|
}
|
||||||
|
return TrackStream.NOTHING_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!formats[relativeReadIndex].equals(downstreamFormat)) {
|
||||||
|
formatHolder.format = formats[relativeReadIndex];
|
||||||
|
return TrackStream.FORMAT_READ;
|
||||||
|
}
|
||||||
|
|
||||||
buffer.timeUs = timesUs[relativeReadIndex];
|
buffer.timeUs = timesUs[relativeReadIndex];
|
||||||
buffer.size = sizes[relativeReadIndex];
|
buffer.size = sizes[relativeReadIndex];
|
||||||
buffer.setFlags(flags[relativeReadIndex]);
|
buffer.setFlags(flags[relativeReadIndex]);
|
||||||
@ -640,7 +668,7 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
|
|
||||||
extrasHolder.nextOffset = queueSize > 0 ? offsets[relativeReadIndex]
|
extrasHolder.nextOffset = queueSize > 0 ? offsets[relativeReadIndex]
|
||||||
: extrasHolder.offset + buffer.size;
|
: extrasHolder.offset + buffer.size;
|
||||||
return true;
|
return TrackStream.BUFFER_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -708,14 +736,18 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
|
|
||||||
// Called by the loading thread.
|
// Called by the loading thread.
|
||||||
|
|
||||||
|
public synchronized void format(Format format) {
|
||||||
|
upstreamFormat = format;
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void commitSample(long timeUs, int sampleFlags, long offset, int size,
|
public synchronized void commitSample(long timeUs, int sampleFlags, long offset, int size,
|
||||||
byte[] encryptionKey, Format format) {
|
byte[] encryptionKey) {
|
||||||
timesUs[relativeWriteIndex] = timeUs;
|
timesUs[relativeWriteIndex] = timeUs;
|
||||||
offsets[relativeWriteIndex] = offset;
|
offsets[relativeWriteIndex] = offset;
|
||||||
sizes[relativeWriteIndex] = size;
|
sizes[relativeWriteIndex] = size;
|
||||||
flags[relativeWriteIndex] = sampleFlags;
|
flags[relativeWriteIndex] = sampleFlags;
|
||||||
encryptionKeys[relativeWriteIndex] = encryptionKey;
|
encryptionKeys[relativeWriteIndex] = encryptionKey;
|
||||||
formats[relativeWriteIndex] = format;
|
formats[relativeWriteIndex] = upstreamFormat;
|
||||||
largestQueuedTimestampUs = Math.max(largestQueuedTimestampUs, timeUs);
|
largestQueuedTimestampUs = Math.max(largestQueuedTimestampUs, timeUs);
|
||||||
// Increment the write index.
|
// Increment the write index.
|
||||||
queueSize++;
|
queueSize++;
|
||||||
|
@ -212,7 +212,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
|||||||
private DefaultTrackOutput[] sampleQueues;
|
private DefaultTrackOutput[] sampleQueues;
|
||||||
private TrackGroupArray tracks;
|
private TrackGroupArray tracks;
|
||||||
private long durationUs;
|
private long durationUs;
|
||||||
private boolean[] pendingMediaFormat;
|
|
||||||
private boolean[] trackEnabledStates;
|
private boolean[] trackEnabledStates;
|
||||||
|
|
||||||
private long downstreamPositionUs;
|
private long downstreamPositionUs;
|
||||||
@ -332,7 +331,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
|||||||
int trackCount = sampleQueues.length;
|
int trackCount = sampleQueues.length;
|
||||||
TrackGroup[] trackArray = new TrackGroup[trackCount];
|
TrackGroup[] trackArray = new TrackGroup[trackCount];
|
||||||
trackEnabledStates = new boolean[trackCount];
|
trackEnabledStates = new boolean[trackCount];
|
||||||
pendingMediaFormat = new boolean[trackCount];
|
|
||||||
durationUs = seekMap.getDurationUs();
|
durationUs = seekMap.getDurationUs();
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
trackArray[i] = new TrackGroup(sampleQueues[i].getUpstreamFormat());
|
trackArray[i] = new TrackGroup(sampleQueues[i].getUpstreamFormat());
|
||||||
@ -384,7 +382,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
|||||||
Assertions.checkState(!trackEnabledStates[track]);
|
Assertions.checkState(!trackEnabledStates[track]);
|
||||||
enabledTrackCount++;
|
enabledTrackCount++;
|
||||||
trackEnabledStates[track] = true;
|
trackEnabledStates[track] = true;
|
||||||
pendingMediaFormat[track] = true;
|
sampleQueues[track].needDownstreamFormat();
|
||||||
newStreams[i] = new TrackStreamImpl(track);
|
newStreams[i] = new TrackStreamImpl(track);
|
||||||
}
|
}
|
||||||
// Cancel or start requests as necessary.
|
// Cancel or start requests as necessary.
|
||||||
@ -449,8 +447,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
|||||||
// TrackStream methods.
|
// TrackStream methods.
|
||||||
|
|
||||||
/* package */ boolean isReady(int track) {
|
/* package */ boolean isReady(int track) {
|
||||||
Assertions.checkState(trackEnabledStates[track]);
|
return loadingFinished || (!isPendingReset() && !sampleQueues[track].isEmpty());
|
||||||
return sampleQueues[track].isEmpty();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,27 +463,18 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
|||||||
return TrackStream.NOTHING_READ;
|
return TrackStream.NOTHING_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultTrackOutput sampleQueue = sampleQueues[track];
|
int result = sampleQueues[track].readData(formatHolder, buffer, loadingFinished);
|
||||||
if (pendingMediaFormat[track]) {
|
switch (result) {
|
||||||
formatHolder.format = sampleQueue.getUpstreamFormat();
|
case TrackStream.FORMAT_READ:
|
||||||
formatHolder.drmInitData = drmInitData;
|
formatHolder.drmInitData = drmInitData;
|
||||||
pendingMediaFormat[track] = false;
|
break;
|
||||||
return TrackStream.FORMAT_READ;
|
case TrackStream.BUFFER_READ:
|
||||||
|
if (!buffer.isEndOfStream() && buffer.timeUs < lastSeekPositionUs) {
|
||||||
|
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
if (sampleQueue.readSample(buffer)) {
|
|
||||||
if (buffer.timeUs < lastSeekPositionUs) {
|
|
||||||
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
|
||||||
}
|
|
||||||
return TrackStream.BUFFER_READ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loadingFinished) {
|
|
||||||
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
|
||||||
return TrackStream.BUFFER_READ;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TrackStream.NOTHING_READ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loader.Callback implementation.
|
// Loader.Callback implementation.
|
||||||
|
@ -79,7 +79,6 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
|||||||
private int primaryTrackGroupIndex;
|
private int primaryTrackGroupIndex;
|
||||||
// Indexed by group.
|
// Indexed by group.
|
||||||
private boolean[] groupEnabledStates;
|
private boolean[] groupEnabledStates;
|
||||||
private Format[] downstreamSampleFormats;
|
|
||||||
|
|
||||||
private long downstreamPositionUs;
|
private long downstreamPositionUs;
|
||||||
private long lastSeekPositionUs;
|
private long lastSeekPositionUs;
|
||||||
@ -202,7 +201,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
|||||||
int group = selection.group;
|
int group = selection.group;
|
||||||
int[] tracks = selection.getTracks();
|
int[] tracks = selection.getTracks();
|
||||||
setTrackGroupEnabledState(group, true);
|
setTrackGroupEnabledState(group, true);
|
||||||
downstreamSampleFormats[group] = null;
|
sampleQueues[group].needDownstreamFormat();
|
||||||
if (group == primaryTrackGroupIndex) {
|
if (group == primaryTrackGroupIndex) {
|
||||||
primaryTracksDeselected |= chunkSource.selectTracks(tracks);
|
primaryTracksDeselected |= chunkSource.selectTracks(tracks);
|
||||||
}
|
}
|
||||||
@ -291,7 +290,6 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
|||||||
// TrackStream implementation.
|
// TrackStream implementation.
|
||||||
|
|
||||||
/* package */ boolean isReady(int group) {
|
/* package */ boolean isReady(int group) {
|
||||||
Assertions.checkState(groupEnabledStates[group]);
|
|
||||||
return loadingFinished || (!isPendingReset() && !sampleQueues[group].isEmpty());
|
return loadingFinished || (!isPendingReset() && !sampleQueues[group].isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,6 +303,9 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
|||||||
return TrackStream.NOTHING_READ;
|
return TrackStream.NOTHING_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (mediaChunks.size() > 1 && mediaChunks.get(1).startTimeUs <= downstreamPositionUs) {
|
||||||
|
mediaChunks.removeFirst();
|
||||||
|
}
|
||||||
HlsMediaChunk currentChunk = mediaChunks.getFirst();
|
HlsMediaChunk currentChunk = mediaChunks.getFirst();
|
||||||
Format currentFormat = currentChunk.format;
|
Format currentFormat = currentChunk.format;
|
||||||
if (downstreamFormat == null || !downstreamFormat.equals(currentFormat)) {
|
if (downstreamFormat == null || !downstreamFormat.equals(currentFormat)) {
|
||||||
@ -313,34 +314,12 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
|||||||
downstreamFormat = currentFormat;
|
downstreamFormat = currentFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultTrackOutput sampleQueue = sampleQueues[group];
|
int result = sampleQueues[group].readData(formatHolder, buffer, loadingFinished);
|
||||||
if (sampleQueue.isEmpty()) {
|
if (result == TrackStream.BUFFER_READ && !buffer.isEndOfStream()
|
||||||
if (loadingFinished) {
|
&& buffer.timeUs < lastSeekPositionUs) {
|
||||||
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||||
return TrackStream.BUFFER_READ;
|
|
||||||
}
|
|
||||||
return TrackStream.NOTHING_READ;
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
Format sampleFormat = sampleQueue.getDownstreamFormat();
|
|
||||||
if (!sampleFormat.equals(downstreamSampleFormats[group])) {
|
|
||||||
formatHolder.format = sampleFormat;
|
|
||||||
downstreamSampleFormats[group] = sampleFormat;
|
|
||||||
return TrackStream.FORMAT_READ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sampleQueue.readSample(buffer)) {
|
|
||||||
long sampleTimeUs = buffer.timeUs;
|
|
||||||
while (mediaChunks.size() > 1 && mediaChunks.get(1).startTimeUs <= sampleTimeUs) {
|
|
||||||
mediaChunks.removeFirst();
|
|
||||||
}
|
|
||||||
if (sampleTimeUs < lastSeekPositionUs) {
|
|
||||||
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
|
||||||
}
|
|
||||||
return TrackStream.BUFFER_READ;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TrackStream.NOTHING_READ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loader.Callback implementation.
|
// Loader.Callback implementation.
|
||||||
@ -463,7 +442,6 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
|||||||
// Instantiate the necessary internal data-structures.
|
// Instantiate the necessary internal data-structures.
|
||||||
primaryTrackGroupIndex = -1;
|
primaryTrackGroupIndex = -1;
|
||||||
groupEnabledStates = new boolean[extractorTrackCount];
|
groupEnabledStates = new boolean[extractorTrackCount];
|
||||||
downstreamSampleFormats = new Format[extractorTrackCount];
|
|
||||||
|
|
||||||
// Construct the set of exposed track groups.
|
// Construct the set of exposed track groups.
|
||||||
TrackGroup[] trackGroups = new TrackGroup[extractorTrackCount];
|
TrackGroup[] trackGroups = new TrackGroup[extractorTrackCount];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user