Correctly handle a SampleStream ending without providing a format
I'm going to introduce an EmptySampleStream that will be used in some cases in conjunction as part of 608/EMSG support. This change avoids EmptySampleStream having to provide a dummy format. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=148383831
This commit is contained in:
parent
ef2541e654
commit
d99cb28e6a
@ -65,6 +65,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||||||
private final boolean playClearSamplesWithoutKeys;
|
private final boolean playClearSamplesWithoutKeys;
|
||||||
private final EventDispatcher eventDispatcher;
|
private final EventDispatcher eventDispatcher;
|
||||||
private final FormatHolder formatHolder;
|
private final FormatHolder formatHolder;
|
||||||
|
private final DecoderInputBuffer flagsOnlyBuffer;
|
||||||
private final DrmSessionManager<ExoMediaCrypto> drmSessionManager;
|
private final DrmSessionManager<ExoMediaCrypto> drmSessionManager;
|
||||||
|
|
||||||
private DecoderCounters decoderCounters;
|
private DecoderCounters decoderCounters;
|
||||||
@ -149,6 +150,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||||||
joiningDeadlineMs = -1;
|
joiningDeadlineMs = -1;
|
||||||
clearLastReportedVideoSize();
|
clearLastReportedVideoSize();
|
||||||
formatHolder = new FormatHolder();
|
formatHolder = new FormatHolder();
|
||||||
|
flagsOnlyBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
|
||||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||||
outputMode = VpxDecoder.OUTPUT_MODE_NONE;
|
outputMode = VpxDecoder.OUTPUT_MODE_NONE;
|
||||||
}
|
}
|
||||||
@ -165,10 +167,22 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try and read a format if we don't have one already.
|
if (format == null) {
|
||||||
if (format == null && !readFormat()) {
|
// We don't have a format yet, so try and read one.
|
||||||
// We can't make progress without one.
|
flagsOnlyBuffer.clear();
|
||||||
|
int result = readSource(formatHolder, flagsOnlyBuffer, true);
|
||||||
|
if (result == C.RESULT_FORMAT_READ) {
|
||||||
|
onInputFormatChanged(formatHolder.format);
|
||||||
|
} else if (result == C.RESULT_BUFFER_READ) {
|
||||||
|
// End of stream read having not read a format.
|
||||||
|
Assertions.checkState(flagsOnlyBuffer.isEndOfStream());
|
||||||
|
inputStreamEnded = true;
|
||||||
|
outputStreamEnded = true;
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
// We still don't have a format and can't make progress without one.
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRendererAvailable()) {
|
if (isRendererAvailable()) {
|
||||||
@ -327,7 +341,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||||||
// We've already read an encrypted sample into buffer, and are waiting for keys.
|
// We've already read an encrypted sample into buffer, and are waiting for keys.
|
||||||
result = C.RESULT_BUFFER_READ;
|
result = C.RESULT_BUFFER_READ;
|
||||||
} else {
|
} else {
|
||||||
result = readSource(formatHolder, inputBuffer);
|
result = readSource(formatHolder, inputBuffer, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == C.RESULT_NOTHING_READ) {
|
if (result == C.RESULT_NOTHING_READ) {
|
||||||
@ -485,15 +499,6 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean readFormat() throws ExoPlaybackException {
|
|
||||||
int result = readSource(formatHolder, null);
|
|
||||||
if (result == C.RESULT_FORMAT_READ) {
|
|
||||||
onInputFormatChanged(formatHolder.format);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onInputFormatChanged(Format newFormat) throws ExoPlaybackException {
|
private void onInputFormatChanged(Format newFormat) throws ExoPlaybackException {
|
||||||
Format oldFormat = format;
|
Format oldFormat = format;
|
||||||
format = newFormat;
|
format = newFormat;
|
||||||
|
@ -513,8 +513,9 @@ public final class ExoPlayerTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
|
||||||
if (buffer == null || !readFormat) {
|
boolean formatRequired) {
|
||||||
|
if (formatRequired || !readFormat) {
|
||||||
formatHolder.format = format;
|
formatHolder.format = format;
|
||||||
readFormat = true;
|
readFormat = true;
|
||||||
return C.RESULT_FORMAT_READ;
|
return C.RESULT_FORMAT_READ;
|
||||||
@ -571,7 +572,7 @@ public final class ExoPlayerTest extends TestCase {
|
|||||||
FormatHolder formatHolder = new FormatHolder();
|
FormatHolder formatHolder = new FormatHolder();
|
||||||
DecoderInputBuffer buffer =
|
DecoderInputBuffer buffer =
|
||||||
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
|
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
|
||||||
int result = readSource(formatHolder, buffer);
|
int result = readSource(formatHolder, buffer, false);
|
||||||
if (result == C.RESULT_FORMAT_READ) {
|
if (result == C.RESULT_FORMAT_READ) {
|
||||||
formatReadCount++;
|
formatReadCount++;
|
||||||
assertEquals(expectedFormat, formatHolder.format);
|
assertEquals(expectedFormat, formatHolder.format);
|
||||||
|
@ -262,13 +262,16 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
|
|||||||
* @param formatHolder A {@link FormatHolder} to populate in the case of reading a format.
|
* @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
|
* @param buffer A {@link DecoderInputBuffer} to populate in the case of reading a sample or the
|
||||||
* end of the stream. If the end of the stream has been reached, the
|
* end of the stream. If the end of the stream has been reached, the
|
||||||
* {@link C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer. May be null if the
|
* {@link C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer.
|
||||||
* caller requires that the format of the stream be read even if it's not changing.
|
* @param formatRequired Whether the caller requires that the format of the stream be read even if
|
||||||
|
* it's not changing. A sample will never be read if set to true, however it is still possible
|
||||||
|
* for the end of stream or nothing to be read.
|
||||||
* @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or
|
* @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or
|
||||||
* {@link C#RESULT_BUFFER_READ}.
|
* {@link C#RESULT_BUFFER_READ}.
|
||||||
*/
|
*/
|
||||||
protected final int readSource(FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
protected final int readSource(FormatHolder formatHolder, DecoderInputBuffer buffer,
|
||||||
int result = stream.readData(formatHolder, buffer);
|
boolean formatRequired) {
|
||||||
|
int result = stream.readData(formatHolder, buffer, formatRequired);
|
||||||
if (result == C.RESULT_BUFFER_READ) {
|
if (result == C.RESULT_BUFFER_READ) {
|
||||||
if (buffer.isEndOfStream()) {
|
if (buffer.isEndOfStream()) {
|
||||||
readEndOfStream = true;
|
readEndOfStream = true;
|
||||||
|
@ -34,6 +34,7 @@ import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
|
|||||||
import com.google.android.exoplayer2.drm.DrmSession;
|
import com.google.android.exoplayer2.drm.DrmSession;
|
||||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
||||||
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.MediaClock;
|
import com.google.android.exoplayer2.util.MediaClock;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.TraceUtil;
|
import com.google.android.exoplayer2.util.TraceUtil;
|
||||||
@ -67,12 +68,12 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
|||||||
*/
|
*/
|
||||||
private static final int REINITIALIZATION_STATE_WAIT_END_OF_STREAM = 2;
|
private static final int REINITIALIZATION_STATE_WAIT_END_OF_STREAM = 2;
|
||||||
|
|
||||||
|
private final DrmSessionManager<ExoMediaCrypto> drmSessionManager;
|
||||||
private final boolean playClearSamplesWithoutKeys;
|
private final boolean playClearSamplesWithoutKeys;
|
||||||
|
|
||||||
private final EventDispatcher eventDispatcher;
|
private final EventDispatcher eventDispatcher;
|
||||||
private final AudioTrack audioTrack;
|
private final AudioTrack audioTrack;
|
||||||
private final DrmSessionManager<ExoMediaCrypto> drmSessionManager;
|
|
||||||
private final FormatHolder formatHolder;
|
private final FormatHolder formatHolder;
|
||||||
|
private final DecoderInputBuffer flagsOnlyBuffer;
|
||||||
|
|
||||||
private DecoderCounters decoderCounters;
|
private DecoderCounters decoderCounters;
|
||||||
private Format inputFormat;
|
private Format inputFormat;
|
||||||
@ -142,11 +143,12 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
|||||||
DrmSessionManager<ExoMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys,
|
DrmSessionManager<ExoMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys,
|
||||||
BufferProcessor... bufferProcessors) {
|
BufferProcessor... bufferProcessors) {
|
||||||
super(C.TRACK_TYPE_AUDIO);
|
super(C.TRACK_TYPE_AUDIO);
|
||||||
|
this.drmSessionManager = drmSessionManager;
|
||||||
|
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
|
||||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||||
audioTrack = new AudioTrack(audioCapabilities, bufferProcessors, new AudioTrackListener());
|
audioTrack = new AudioTrack(audioCapabilities, bufferProcessors, new AudioTrackListener());
|
||||||
this.drmSessionManager = drmSessionManager;
|
|
||||||
formatHolder = new FormatHolder();
|
formatHolder = new FormatHolder();
|
||||||
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
|
flagsOnlyBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
|
||||||
decoderReinitializationState = REINITIALIZATION_STATE_NONE;
|
decoderReinitializationState = REINITIALIZATION_STATE_NONE;
|
||||||
audioTrackNeedsConfigure = true;
|
audioTrackNeedsConfigure = true;
|
||||||
}
|
}
|
||||||
@ -187,9 +189,22 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try and read a format if we don't have one already.
|
// Try and read a format if we don't have one already.
|
||||||
if (inputFormat == null && !readFormat()) {
|
if (inputFormat == null) {
|
||||||
// We can't make progress without one.
|
// We don't have a format yet, so try and read one.
|
||||||
|
flagsOnlyBuffer.clear();
|
||||||
|
int result = readSource(formatHolder, flagsOnlyBuffer, true);
|
||||||
|
if (result == C.RESULT_FORMAT_READ) {
|
||||||
|
onInputFormatChanged(formatHolder.format);
|
||||||
|
} else if (result == C.RESULT_BUFFER_READ) {
|
||||||
|
// End of stream read having not read a format.
|
||||||
|
Assertions.checkState(flagsOnlyBuffer.isEndOfStream());
|
||||||
|
inputStreamEnded = true;
|
||||||
|
processEndOfStream();
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
// We still don't have a format and can't make progress without one.
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we don't have a decoder yet, we need to instantiate one.
|
// If we don't have a decoder yet, we need to instantiate one.
|
||||||
@ -284,8 +299,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
|||||||
} else {
|
} else {
|
||||||
outputBuffer.release();
|
outputBuffer.release();
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
outputStreamEnded = true;
|
processEndOfStream();
|
||||||
audioTrack.playToEndOfStream();
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -334,7 +348,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
|||||||
// We've already read an encrypted sample into buffer, and are waiting for keys.
|
// We've already read an encrypted sample into buffer, and are waiting for keys.
|
||||||
result = C.RESULT_BUFFER_READ;
|
result = C.RESULT_BUFFER_READ;
|
||||||
} else {
|
} else {
|
||||||
result = readSource(formatHolder, inputBuffer);
|
result = readSource(formatHolder, inputBuffer, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == C.RESULT_NOTHING_READ) {
|
if (result == C.RESULT_NOTHING_READ) {
|
||||||
@ -375,6 +389,15 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
|||||||
&& (bufferEncrypted || !playClearSamplesWithoutKeys);
|
&& (bufferEncrypted || !playClearSamplesWithoutKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processEndOfStream() throws ExoPlaybackException {
|
||||||
|
outputStreamEnded = true;
|
||||||
|
try {
|
||||||
|
audioTrack.playToEndOfStream();
|
||||||
|
} catch (AudioTrack.WriteException e) {
|
||||||
|
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void flushDecoder() throws ExoPlaybackException {
|
private void flushDecoder() throws ExoPlaybackException {
|
||||||
waitingForKeys = false;
|
waitingForKeys = false;
|
||||||
if (decoderReinitializationState != REINITIALIZATION_STATE_NONE) {
|
if (decoderReinitializationState != REINITIALIZATION_STATE_NONE) {
|
||||||
@ -523,15 +546,6 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
|||||||
decoderReceivedBuffers = false;
|
decoderReceivedBuffers = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean readFormat() throws ExoPlaybackException {
|
|
||||||
int result = readSource(formatHolder, null);
|
|
||||||
if (result == C.RESULT_FORMAT_READ) {
|
|
||||||
onInputFormatChanged(formatHolder.format);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onInputFormatChanged(Format newFormat) throws ExoPlaybackException {
|
private void onInputFormatChanged(Format newFormat) throws ExoPlaybackException {
|
||||||
Format oldFormat = inputFormat;
|
Format oldFormat = inputFormat;
|
||||||
inputFormat = newFormat;
|
inputFormat = newFormat;
|
||||||
|
@ -267,27 +267,26 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
* @param formatHolder A {@link FormatHolder} to populate in the case of reading a format.
|
* @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
|
* @param buffer A {@link DecoderInputBuffer} to populate in the case of reading a sample or the
|
||||||
* end of the stream. If the end of the stream has been reached, the
|
* end of the stream. If the end of the stream has been reached, the
|
||||||
* {@link C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer. May be null if the
|
* {@link C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer.
|
||||||
* caller requires that the format of the stream be read even if it's not changing.
|
* @param formatRequired Whether the caller requires that the format of the stream be read even if
|
||||||
|
* it's not changing. A sample will never be read if set to true, however it is still possible
|
||||||
|
* for the end of stream or nothing to be read.
|
||||||
* @param loadingFinished True if an empty queue should be considered the end of the stream.
|
* @param loadingFinished True if an empty queue should be considered the end of the stream.
|
||||||
* @param decodeOnlyUntilUs If a buffer is read, the {@link C#BUFFER_FLAG_DECODE_ONLY} flag will
|
* @param decodeOnlyUntilUs If a buffer is read, the {@link C#BUFFER_FLAG_DECODE_ONLY} flag will
|
||||||
* be set if the buffer's timestamp is less than this value.
|
* be set if the buffer's timestamp is less than this value.
|
||||||
* @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or
|
* @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or
|
||||||
* {@link C#RESULT_BUFFER_READ}.
|
* {@link C#RESULT_BUFFER_READ}.
|
||||||
*/
|
*/
|
||||||
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean loadingFinished,
|
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired,
|
||||||
long decodeOnlyUntilUs) {
|
boolean loadingFinished, long decodeOnlyUntilUs) {
|
||||||
switch (infoQueue.readData(formatHolder, buffer, downstreamFormat, extrasHolder)) {
|
int result = infoQueue.readData(formatHolder, buffer, formatRequired, loadingFinished,
|
||||||
case C.RESULT_NOTHING_READ:
|
downstreamFormat, extrasHolder);
|
||||||
if (loadingFinished) {
|
switch (result) {
|
||||||
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
|
|
||||||
return C.RESULT_BUFFER_READ;
|
|
||||||
}
|
|
||||||
return C.RESULT_NOTHING_READ;
|
|
||||||
case C.RESULT_FORMAT_READ:
|
case C.RESULT_FORMAT_READ:
|
||||||
downstreamFormat = formatHolder.format;
|
downstreamFormat = formatHolder.format;
|
||||||
return C.RESULT_FORMAT_READ;
|
return C.RESULT_FORMAT_READ;
|
||||||
case C.RESULT_BUFFER_READ:
|
case C.RESULT_BUFFER_READ:
|
||||||
|
if (!buffer.isEndOfStream()) {
|
||||||
if (buffer.timeUs < decodeOnlyUntilUs) {
|
if (buffer.timeUs < decodeOnlyUntilUs) {
|
||||||
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||||
}
|
}
|
||||||
@ -300,7 +299,10 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
readData(extrasHolder.offset, buffer.data, extrasHolder.size);
|
readData(extrasHolder.offset, buffer.data, extrasHolder.size);
|
||||||
// Advance the read head.
|
// Advance the read head.
|
||||||
dropDownstreamTo(extrasHolder.nextOffset);
|
dropDownstreamTo(extrasHolder.nextOffset);
|
||||||
|
}
|
||||||
return C.RESULT_BUFFER_READ;
|
return C.RESULT_BUFFER_READ;
|
||||||
|
case C.RESULT_NOTHING_READ:
|
||||||
|
return C.RESULT_NOTHING_READ;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
@ -760,23 +762,34 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
* and the absolute position of the first byte that may still be required after the current
|
* and the absolute position of the first byte that may still be required after the current
|
||||||
* sample has been read. May be null if the caller requires that the format of the stream be
|
* sample has been read. May be null if the caller requires that the format of the stream be
|
||||||
* read even if it's not changing.
|
* read even if it's not changing.
|
||||||
|
* @param formatRequired Whether the caller requires that the format of the stream be read even
|
||||||
|
* if it's not changing. A sample will never be read if set to true, however it is still
|
||||||
|
* possible for the end of stream or nothing to be read.
|
||||||
|
* @param loadingFinished True if an empty queue should be considered the end of the stream.
|
||||||
* @param downstreamFormat The current downstream {@link Format}. If the format of the next
|
* @param downstreamFormat The current downstream {@link Format}. If the format of the next
|
||||||
* sample is different to the current downstream format then a format will be read.
|
* sample is different to the current downstream format then a format will be 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 The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ}
|
* @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ}
|
||||||
* or {@link C#RESULT_BUFFER_READ}.
|
* or {@link C#RESULT_BUFFER_READ}.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ReferenceEquality")
|
||||||
public synchronized int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
|
public synchronized int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
|
||||||
Format downstreamFormat, BufferExtrasHolder extrasHolder) {
|
boolean formatRequired, boolean loadingFinished, Format downstreamFormat,
|
||||||
|
BufferExtrasHolder extrasHolder) {
|
||||||
if (queueSize == 0) {
|
if (queueSize == 0) {
|
||||||
if (upstreamFormat != null && (buffer == null || upstreamFormat != downstreamFormat)) {
|
if (loadingFinished) {
|
||||||
|
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
|
||||||
|
return C.RESULT_BUFFER_READ;
|
||||||
|
} else if (upstreamFormat != null
|
||||||
|
&& (formatRequired || upstreamFormat != downstreamFormat)) {
|
||||||
formatHolder.format = upstreamFormat;
|
formatHolder.format = upstreamFormat;
|
||||||
return C.RESULT_FORMAT_READ;
|
return C.RESULT_FORMAT_READ;
|
||||||
}
|
} else {
|
||||||
return C.RESULT_NOTHING_READ;
|
return C.RESULT_NOTHING_READ;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (buffer == null || formats[relativeReadIndex] != downstreamFormat) {
|
if (formatRequired || formats[relativeReadIndex] != downstreamFormat) {
|
||||||
formatHolder.format = formats[relativeReadIndex];
|
formatHolder.format = formats[relativeReadIndex];
|
||||||
return C.RESULT_FORMAT_READ;
|
return C.RESULT_FORMAT_READ;
|
||||||
}
|
}
|
||||||
|
@ -484,7 +484,21 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (format == null) {
|
if (format == null) {
|
||||||
readFormat();
|
// We don't have a format yet, so try and read one.
|
||||||
|
buffer.clear();
|
||||||
|
int result = readSource(formatHolder, buffer, true);
|
||||||
|
if (result == C.RESULT_FORMAT_READ) {
|
||||||
|
onInputFormatChanged(formatHolder.format);
|
||||||
|
} else if (result == C.RESULT_BUFFER_READ) {
|
||||||
|
// End of stream read having not read a format.
|
||||||
|
Assertions.checkState(buffer.isEndOfStream());
|
||||||
|
inputStreamEnded = true;
|
||||||
|
processEndOfStream();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// We still don't have a format and can't make progress without one.
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
maybeInitCodec();
|
maybeInitCodec();
|
||||||
if (codec != null) {
|
if (codec != null) {
|
||||||
@ -498,13 +512,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
decoderCounters.ensureUpdated();
|
decoderCounters.ensureUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readFormat() throws ExoPlaybackException {
|
|
||||||
int result = readSource(formatHolder, null);
|
|
||||||
if (result == C.RESULT_FORMAT_READ) {
|
|
||||||
onInputFormatChanged(formatHolder.format);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void flushCodec() throws ExoPlaybackException {
|
protected void flushCodec() throws ExoPlaybackException {
|
||||||
codecHotswapDeadlineMs = C.TIME_UNSET;
|
codecHotswapDeadlineMs = C.TIME_UNSET;
|
||||||
inputIndex = C.INDEX_UNSET;
|
inputIndex = C.INDEX_UNSET;
|
||||||
@ -594,7 +601,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
codecReconfigurationState = RECONFIGURATION_STATE_QUEUE_PENDING;
|
codecReconfigurationState = RECONFIGURATION_STATE_QUEUE_PENDING;
|
||||||
}
|
}
|
||||||
adaptiveReconfigurationBytes = buffer.data.position();
|
adaptiveReconfigurationBytes = buffer.data.position();
|
||||||
result = readSource(formatHolder, buffer);
|
result = readSource(formatHolder, buffer, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == C.RESULT_NOTHING_READ) {
|
if (result == C.RESULT_NOTHING_READ) {
|
||||||
|
@ -109,7 +109,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
|
|||||||
public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||||
if (!inputStreamEnded && pendingMetadata == null) {
|
if (!inputStreamEnded && pendingMetadata == null) {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
int result = readSource(formatHolder, buffer);
|
int result = readSource(formatHolder, buffer, false);
|
||||||
if (result == C.RESULT_BUFFER_READ) {
|
if (result == C.RESULT_BUFFER_READ) {
|
||||||
if (buffer.isEndOfStream()) {
|
if (buffer.isEndOfStream()) {
|
||||||
inputStreamEnded = true;
|
inputStreamEnded = true;
|
||||||
|
@ -231,18 +231,16 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
|
||||||
|
boolean requireFormat) {
|
||||||
if (pendingDiscontinuity) {
|
if (pendingDiscontinuity) {
|
||||||
return C.RESULT_NOTHING_READ;
|
return C.RESULT_NOTHING_READ;
|
||||||
}
|
}
|
||||||
if (buffer == null) {
|
|
||||||
return stream.readData(formatHolder, null);
|
|
||||||
}
|
|
||||||
if (sentEos) {
|
if (sentEos) {
|
||||||
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
|
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
|
||||||
return C.RESULT_BUFFER_READ;
|
return C.RESULT_BUFFER_READ;
|
||||||
}
|
}
|
||||||
int result = stream.readData(formatHolder, buffer);
|
int result = stream.readData(formatHolder, buffer, requireFormat);
|
||||||
// TODO: Clear gapless playback metadata if a format was read (if applicable).
|
// TODO: Clear gapless playback metadata if a format was read (if applicable).
|
||||||
if (endUs != C.TIME_END_OF_SOURCE && ((result == C.RESULT_BUFFER_READ
|
if (endUs != C.TIME_END_OF_SOURCE && ((result == C.RESULT_BUFFER_READ
|
||||||
&& buffer.timeUs >= endUs) || (result == C.RESULT_NOTHING_READ
|
&& buffer.timeUs >= endUs) || (result == C.RESULT_NOTHING_READ
|
||||||
|
@ -325,13 +325,14 @@ import java.io.IOException;
|
|||||||
loader.maybeThrowError();
|
loader.maybeThrowError();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ int readData(int track, FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
/* package */ int readData(int track, FormatHolder formatHolder, DecoderInputBuffer buffer,
|
||||||
|
boolean formatRequired) {
|
||||||
if (notifyReset || isPendingReset()) {
|
if (notifyReset || isPendingReset()) {
|
||||||
return C.RESULT_NOTHING_READ;
|
return C.RESULT_NOTHING_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sampleQueues.valueAt(track).readData(formatHolder, buffer, loadingFinished,
|
return sampleQueues.valueAt(track).readData(formatHolder, buffer, formatRequired,
|
||||||
lastSeekPositionUs);
|
loadingFinished, lastSeekPositionUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loader.Callback implementation.
|
// Loader.Callback implementation.
|
||||||
@ -552,8 +553,9 @@ import java.io.IOException;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
|
||||||
return ExtractorMediaPeriod.this.readData(track, formatHolder, buffer);
|
boolean formatRequired) {
|
||||||
|
return ExtractorMediaPeriod.this.readData(track, formatHolder, buffer, formatRequired);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -45,20 +45,24 @@ public interface SampleStream {
|
|||||||
/**
|
/**
|
||||||
* Attempts to read from the stream.
|
* Attempts to read from the stream.
|
||||||
* <p>
|
* <p>
|
||||||
* If no data is available then {@link C#RESULT_NOTHING_READ} is returned. If the format of the
|
* If the stream has ended then {@link C#BUFFER_FLAG_END_OF_STREAM} flag is set on {@code buffer}
|
||||||
* media is changing or if {@code buffer == null} then {@code formatHolder} is populated and
|
* and {@link C#RESULT_BUFFER_READ} is returned. Else if no data is available then
|
||||||
|
* {@link C#RESULT_NOTHING_READ} is returned. Else if the format of the media is changing or if
|
||||||
|
* {@code formatRequired} is set then {@code formatHolder} is populated and
|
||||||
* {@link C#RESULT_FORMAT_READ} is returned. Else {@code buffer} is populated and
|
* {@link C#RESULT_FORMAT_READ} is returned. Else {@code buffer} is populated and
|
||||||
* {@link C#RESULT_BUFFER_READ} is returned.
|
* {@link C#RESULT_BUFFER_READ} is returned.
|
||||||
*
|
*
|
||||||
* @param formatHolder A {@link FormatHolder} to populate in the case of reading a format.
|
* @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
|
* @param buffer A {@link DecoderInputBuffer} to populate in the case of reading a sample or the
|
||||||
* end of the stream. If the end of the stream has been reached, the
|
* end of the stream. If the end of the stream has been reached, the
|
||||||
* {@link C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer. May be null if the
|
* {@link C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer.
|
||||||
* caller requires that the format of the stream be read even if it's not changing.
|
* @param formatRequired Whether the caller requires that the format of the stream be read even if
|
||||||
|
* it's not changing. A sample will never be read if set to true, however it is still possible
|
||||||
|
* for the end of stream or nothing to be read.
|
||||||
* @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or
|
* @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or
|
||||||
* {@link C#RESULT_BUFFER_READ}.
|
* {@link C#RESULT_BUFFER_READ}.
|
||||||
*/
|
*/
|
||||||
int readData(FormatHolder formatHolder, DecoderInputBuffer buffer);
|
int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to skip to the keyframe before the specified time.
|
* Attempts to skip to the keyframe before the specified time.
|
||||||
|
@ -205,14 +205,15 @@ import java.util.Arrays;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
|
||||||
if (buffer == null || streamState == STREAM_STATE_SEND_FORMAT) {
|
boolean requireFormat) {
|
||||||
|
if (streamState == STREAM_STATE_END_OF_STREAM) {
|
||||||
|
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
||||||
|
return C.RESULT_BUFFER_READ;
|
||||||
|
} else if (requireFormat || streamState == STREAM_STATE_SEND_FORMAT) {
|
||||||
formatHolder.format = format;
|
formatHolder.format = format;
|
||||||
streamState = STREAM_STATE_SEND_SAMPLE;
|
streamState = STREAM_STATE_SEND_SAMPLE;
|
||||||
return C.RESULT_FORMAT_READ;
|
return C.RESULT_FORMAT_READ;
|
||||||
} else if (streamState == STREAM_STATE_END_OF_STREAM) {
|
|
||||||
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
|
||||||
return C.RESULT_BUFFER_READ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Assertions.checkState(streamState == STREAM_STATE_SEND_SAMPLE);
|
Assertions.checkState(streamState == STREAM_STATE_SEND_SAMPLE);
|
||||||
|
@ -169,7 +169,8 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
|
||||||
|
boolean formatRequired) {
|
||||||
if (isPendingReset()) {
|
if (isPendingReset()) {
|
||||||
return C.RESULT_NOTHING_READ;
|
return C.RESULT_NOTHING_READ;
|
||||||
}
|
}
|
||||||
@ -187,7 +188,8 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
|
|||||||
currentChunk.startTimeUs);
|
currentChunk.startTimeUs);
|
||||||
}
|
}
|
||||||
downstreamTrackFormat = trackFormat;
|
downstreamTrackFormat = trackFormat;
|
||||||
return sampleQueue.readData(formatHolder, buffer, loadingFinished, lastSeekPositionUs);
|
return sampleQueue.readData(formatHolder, buffer, formatRequired, loadingFinished,
|
||||||
|
lastSeekPositionUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -45,8 +45,8 @@ import java.io.IOException;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) {
|
||||||
return sampleStreamWrapper.readData(group, formatHolder, buffer);
|
return sampleStreamWrapper.readData(group, formatHolder, buffer, requireFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -290,7 +290,8 @@ import java.util.LinkedList;
|
|||||||
chunkSource.maybeThrowError();
|
chunkSource.maybeThrowError();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ int readData(int group, FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
/* package */ int readData(int group, FormatHolder formatHolder, DecoderInputBuffer buffer,
|
||||||
|
boolean requireFormat) {
|
||||||
if (isPendingReset()) {
|
if (isPendingReset()) {
|
||||||
return C.RESULT_NOTHING_READ;
|
return C.RESULT_NOTHING_READ;
|
||||||
}
|
}
|
||||||
@ -307,8 +308,8 @@ import java.util.LinkedList;
|
|||||||
}
|
}
|
||||||
downstreamTrackFormat = trackFormat;
|
downstreamTrackFormat = trackFormat;
|
||||||
|
|
||||||
return sampleQueues.valueAt(group).readData(formatHolder, buffer, loadingFinished,
|
return sampleQueues.valueAt(group).readData(formatHolder, buffer, requireFormat,
|
||||||
lastSeekPositionUs);
|
loadingFinished, lastSeekPositionUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ void skipToKeyframeBefore(int group, long timeUs) {
|
/* package */ void skipToKeyframeBefore(int group, long timeUs) {
|
||||||
|
@ -189,7 +189,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Try and read the next subtitle from the source.
|
// Try and read the next subtitle from the source.
|
||||||
int result = readSource(formatHolder, nextInputBuffer);
|
int result = readSource(formatHolder, nextInputBuffer, false);
|
||||||
if (result == C.RESULT_BUFFER_READ) {
|
if (result == C.RESULT_BUFFER_READ) {
|
||||||
if (nextInputBuffer.isEndOfStream()) {
|
if (nextInputBuffer.isEndOfStream()) {
|
||||||
inputStreamEnded = true;
|
inputStreamEnded = true;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user