mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Implement seeking via a single code path.
When a seek is performed, renderers currently perform the actions that they need to take in two places: Some changes are performed in seekTo implementations. Other changes are performed when discontinuities are read from the source. In HLS we need to perform what is effectively a seek originating in the source. To support this, this CL allows discontinuities read from the source to modify the playback position. All actions that renderers perform as a result of a seek are moved to be performed when a discontinuity is received. Best way to understand CL: - Look at SampleSource interface change and then at the concrete implementations, to make sure they've been changed properly. - Look at SampleSourceTrackRenderer change. - Look at concrete renderers. The general pattern is that code previously performed in seekTo and READ_DISCONTINUITY is merged into onDiscontinuity(). Note: This will be further untangled in V2. Issue #676 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=112720746
This commit is contained in:
parent
3f0244e214
commit
d804446b34
@ -145,19 +145,11 @@ public final class LibopusAudioTrackRenderer extends SampleSourceTrackRenderer
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs, boolean sourceIsReady)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
seekToInternal(positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||
if (outputStreamEnded) {
|
||||
return;
|
||||
}
|
||||
sourceIsReady = continueBufferingSource(positionUs);
|
||||
checkForDiscontinuity(positionUs);
|
||||
|
||||
// Try and read a format if we don't have one already.
|
||||
if (format == null && !readFormat(positionUs)) {
|
||||
@ -275,14 +267,10 @@ public final class LibopusAudioTrackRenderer extends SampleSourceTrackRenderer
|
||||
}
|
||||
}
|
||||
|
||||
int result = readSource(positionUs, formatHolder, inputBuffer.sampleHolder, false);
|
||||
int result = readSource(positionUs, formatHolder, inputBuffer.sampleHolder);
|
||||
if (result == SampleSource.NOTHING_READ) {
|
||||
return false;
|
||||
}
|
||||
if (result == SampleSource.DISCONTINUITY_READ) {
|
||||
flushDecoder();
|
||||
return true;
|
||||
}
|
||||
if (result == SampleSource.FORMAT_READ) {
|
||||
format = formatHolder.format;
|
||||
return true;
|
||||
@ -304,16 +292,6 @@ public final class LibopusAudioTrackRenderer extends SampleSourceTrackRenderer
|
||||
return true;
|
||||
}
|
||||
|
||||
private void checkForDiscontinuity(long positionUs) {
|
||||
if (decoder == null) {
|
||||
return;
|
||||
}
|
||||
int result = readSource(positionUs, formatHolder, null, true);
|
||||
if (result == SampleSource.DISCONTINUITY_READ) {
|
||||
flushDecoder();
|
||||
}
|
||||
}
|
||||
|
||||
private void flushDecoder() {
|
||||
inputBuffer = null;
|
||||
outputBuffer = null;
|
||||
@ -344,18 +322,16 @@ public final class LibopusAudioTrackRenderer extends SampleSourceTrackRenderer
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
super.seekTo(positionUs);
|
||||
seekToInternal(positionUs);
|
||||
}
|
||||
|
||||
private void seekToInternal(long positionUs) {
|
||||
protected void onDiscontinuity(long positionUs) {
|
||||
audioTrack.reset();
|
||||
currentPositionUs = positionUs;
|
||||
allowPositionDiscontinuity = true;
|
||||
inputStreamEnded = false;
|
||||
outputStreamEnded = false;
|
||||
sourceIsReady = false;
|
||||
if (decoder != null) {
|
||||
flushDecoder();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -387,7 +363,7 @@ public final class LibopusAudioTrackRenderer extends SampleSourceTrackRenderer
|
||||
}
|
||||
|
||||
private boolean readFormat(long positionUs) {
|
||||
int result = readSource(positionUs, formatHolder, null, false);
|
||||
int result = readSource(positionUs, formatHolder, null);
|
||||
if (result == SampleSource.FORMAT_READ) {
|
||||
format = formatHolder.format;
|
||||
audioTrack.configure(format.getFrameworkMediaFormatV16(), false);
|
||||
|
@ -175,12 +175,11 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs, boolean sourceIsReady)
|
||||
throws ExoPlaybackException {
|
||||
if (outputStreamEnded) {
|
||||
return;
|
||||
}
|
||||
sourceIsReady = continueBufferingSource(positionUs);
|
||||
checkForDiscontinuity(positionUs);
|
||||
|
||||
// Try and read a format if we don't have one already.
|
||||
if (format == null && !readFormat(positionUs)) {
|
||||
@ -311,15 +310,10 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
int result = readSource(positionUs, formatHolder, inputBuffer.sampleHolder,
|
||||
false);
|
||||
int result = readSource(positionUs, formatHolder, inputBuffer.sampleHolder);
|
||||
if (result == SampleSource.NOTHING_READ) {
|
||||
return false;
|
||||
}
|
||||
if (result == SampleSource.DISCONTINUITY_READ) {
|
||||
flushDecoder();
|
||||
return true;
|
||||
}
|
||||
if (result == SampleSource.FORMAT_READ) {
|
||||
format = formatHolder.format;
|
||||
return true;
|
||||
@ -339,16 +333,6 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void checkForDiscontinuity(long positionUs) {
|
||||
if (decoder == null) {
|
||||
return;
|
||||
}
|
||||
int result = readSource(positionUs, formatHolder, null, true);
|
||||
if (result == SampleSource.DISCONTINUITY_READ) {
|
||||
flushDecoder();
|
||||
}
|
||||
}
|
||||
|
||||
private void flushDecoder() {
|
||||
inputBuffer = null;
|
||||
if (outputBuffer != null) {
|
||||
@ -369,23 +353,14 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
super.seekTo(positionUs);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
private void seekToInternal() {
|
||||
protected void onDiscontinuity(long positionUs) {
|
||||
sourceIsReady = false;
|
||||
inputStreamEnded = false;
|
||||
outputStreamEnded = false;
|
||||
renderedFirstFrame = false;
|
||||
if (decoder != null) {
|
||||
flushDecoder();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -416,7 +391,7 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||
}
|
||||
|
||||
private boolean readFormat(long positionUs) {
|
||||
int result = readSource(positionUs, formatHolder, null, false);
|
||||
int result = readSource(positionUs, formatHolder, null);
|
||||
if (result == SampleSource.FORMAT_READ) {
|
||||
format = formatHolder.format;
|
||||
return true;
|
||||
|
@ -80,7 +80,8 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
|
||||
private int[] trackStates;
|
||||
private boolean[] pendingDiscontinuities;
|
||||
|
||||
private long seekPositionUs;
|
||||
private long lastSeekPositionUs;
|
||||
private long pendingSeekPositionUs;
|
||||
|
||||
/**
|
||||
* Instantiates a new sample extractor reading from the specified {@code uri}.
|
||||
@ -184,16 +185,21 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readDiscontinuity(int track) {
|
||||
if (pendingDiscontinuities[track]) {
|
||||
pendingDiscontinuities[track] = false;
|
||||
return lastSeekPositionUs;
|
||||
}
|
||||
return NO_DISCONTINUITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
|
||||
SampleHolder sampleHolder) {
|
||||
Assertions.checkState(prepared);
|
||||
Assertions.checkState(trackStates[track] != TRACK_STATE_DISABLED);
|
||||
if (pendingDiscontinuities[track]) {
|
||||
pendingDiscontinuities[track] = false;
|
||||
return DISCONTINUITY_READ;
|
||||
}
|
||||
if (onlyReadDiscontinuity) {
|
||||
return NOTHING_READ;
|
||||
}
|
||||
if (trackStates[track] != TRACK_STATE_FORMAT_SENT) {
|
||||
@ -216,7 +222,7 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
|
||||
if (sampleHolder.isEncrypted()) {
|
||||
sampleHolder.cryptoInfo.setFromExtractorV16(extractor);
|
||||
}
|
||||
seekPositionUs = C.UNKNOWN_TIME_US;
|
||||
pendingSeekPositionUs = C.UNKNOWN_TIME_US;
|
||||
extractor.advance();
|
||||
return SAMPLE_READ;
|
||||
} else {
|
||||
@ -285,8 +291,9 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
|
||||
private void seekToUsInternal(long positionUs, boolean force) {
|
||||
// Unless forced, avoid duplicate calls to the underlying extractor's seek method in the case
|
||||
// that there have been no interleaving calls to readSample.
|
||||
if (force || seekPositionUs != positionUs) {
|
||||
seekPositionUs = positionUs;
|
||||
if (force || pendingSeekPositionUs != positionUs) {
|
||||
lastSeekPositionUs = positionUs;
|
||||
pendingSeekPositionUs = positionUs;
|
||||
extractor.seekTo(positionUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
|
||||
for (int i = 0; i < trackStates.length; ++i) {
|
||||
if (trackStates[i] != TRACK_STATE_DISABLED) {
|
||||
|
@ -239,13 +239,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
seekToInternal(positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOutputFormatChanged(android.media.MediaFormat outputFormat) {
|
||||
boolean passthrough = passthroughMediaFormat != null;
|
||||
@ -312,13 +305,8 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
super.seekTo(positionUs);
|
||||
seekToInternal(positionUs);
|
||||
}
|
||||
|
||||
private void seekToInternal(long positionUs) {
|
||||
// TODO: Try and re-use the same AudioTrack instance once [Internal: b/7941810] is fixed.
|
||||
protected void onDiscontinuity(long positionUs) throws ExoPlaybackException {
|
||||
super.onDiscontinuity(positionUs);
|
||||
audioTrack.reset();
|
||||
currentPositionUs = positionUs;
|
||||
allowPositionDiscontinuity = true;
|
||||
@ -376,7 +364,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
|
||||
// If we are out of sync, allow currentPositionUs to jump backwards.
|
||||
if ((handleBufferResult & AudioTrack.RESULT_POSITION_DISCONTINUITY) != 0) {
|
||||
handleDiscontinuity();
|
||||
handleAudioTrackDiscontinuity();
|
||||
allowPositionDiscontinuity = true;
|
||||
}
|
||||
|
||||
@ -395,7 +383,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
audioTrack.handleEndOfStream();
|
||||
}
|
||||
|
||||
protected void handleDiscontinuity() {
|
||||
protected void handleAudioTrackDiscontinuity() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
|
@ -262,13 +262,6 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
|
||||
codecReinitializationState = REINITIALIZATION_STATE_NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean handlesTrack(MediaFormat mediaFormat) throws DecoderQueryException {
|
||||
return handlesTrack(mediaCodecSelector, mediaFormat);
|
||||
@ -458,15 +451,13 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
super.seekTo(positionUs);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
private void seekToInternal() {
|
||||
protected void onDiscontinuity(long positionUs) throws ExoPlaybackException {
|
||||
sourceState = SOURCE_STATE_NOT_READY;
|
||||
inputStreamEnded = false;
|
||||
outputStreamEnded = false;
|
||||
if (codec != null) {
|
||||
flushCodec();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -480,11 +471,11 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||
sourceState = continueBufferingSource(positionUs)
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs, boolean sourceIsReady)
|
||||
throws ExoPlaybackException {
|
||||
sourceState = sourceIsReady
|
||||
? (sourceState == SOURCE_STATE_NOT_READY ? SOURCE_STATE_READY : sourceState)
|
||||
: SOURCE_STATE_NOT_READY;
|
||||
checkForDiscontinuity(positionUs);
|
||||
if (format == null) {
|
||||
readFormat(positionUs);
|
||||
}
|
||||
@ -501,22 +492,12 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
|
||||
}
|
||||
|
||||
private void readFormat(long positionUs) throws ExoPlaybackException {
|
||||
int result = readSource(positionUs, formatHolder, sampleHolder, false);
|
||||
int result = readSource(positionUs, formatHolder, null);
|
||||
if (result == SampleSource.FORMAT_READ) {
|
||||
onInputFormatChanged(formatHolder);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForDiscontinuity(long positionUs) throws ExoPlaybackException {
|
||||
if (codec == null) {
|
||||
return;
|
||||
}
|
||||
int result = readSource(positionUs, formatHolder, sampleHolder, true);
|
||||
if (result == SampleSource.DISCONTINUITY_READ) {
|
||||
flushCodec();
|
||||
}
|
||||
}
|
||||
|
||||
private void flushCodec() throws ExoPlaybackException {
|
||||
codecHotswapTimeMs = -1;
|
||||
inputIndex = -1;
|
||||
@ -598,7 +579,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
|
||||
}
|
||||
codecReconfigurationState = RECONFIGURATION_STATE_QUEUE_PENDING;
|
||||
}
|
||||
result = readSource(positionUs, formatHolder, sampleHolder, false);
|
||||
result = readSource(positionUs, formatHolder, sampleHolder);
|
||||
if (firstFeed && sourceState == SOURCE_STATE_READY && result == SampleSource.NOTHING_READ) {
|
||||
sourceState = SOURCE_STATE_READY_READ_MAY_FAIL;
|
||||
}
|
||||
@ -607,10 +588,6 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
|
||||
if (result == SampleSource.NOTHING_READ) {
|
||||
return false;
|
||||
}
|
||||
if (result == SampleSource.DISCONTINUITY_READ) {
|
||||
flushCodec();
|
||||
return true;
|
||||
}
|
||||
if (result == SampleSource.FORMAT_READ) {
|
||||
if (codecReconfigurationState == RECONFIGURATION_STATE_QUEUE_PENDING) {
|
||||
// We received two formats in a row. Clear the current buffer of any reconfiguration data
|
||||
|
@ -227,8 +227,6 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
renderedFirstFrame = false;
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
if (joining && allowedJoiningTimeUs > 0) {
|
||||
joiningDeadlineUs = SystemClock.elapsedRealtime() * 1000L + allowedJoiningTimeUs;
|
||||
}
|
||||
@ -236,8 +234,8 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
super.seekTo(positionUs);
|
||||
protected void onDiscontinuity(long positionUs) throws ExoPlaybackException {
|
||||
super.onDiscontinuity(positionUs);
|
||||
renderedFirstFrame = false;
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
joiningDeadlineUs = -1;
|
||||
|
@ -46,9 +46,9 @@ public interface SampleSource {
|
||||
*/
|
||||
public static final int FORMAT_READ = -4;
|
||||
/**
|
||||
* A discontinuity in the sample stream.
|
||||
* Returned from {@link SampleSourceReader#readDiscontinuity(int)} to indicate no discontinuity.
|
||||
*/
|
||||
public static final int DISCONTINUITY_READ = -5;
|
||||
public static final long NO_DISCONTINUITY = Long.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* A consumer of samples should call this method to register themselves and gain access to the
|
||||
@ -104,7 +104,7 @@ public interface SampleSource {
|
||||
* (i.e. @link {@link MediaFormat#adaptive} is true). Hence the track formats returned through
|
||||
* this method should not be used to configure decoders. Decoder configuration should be
|
||||
* performed using the formats obtained when reading the media stream through calls to
|
||||
* {@link #readData(int, long, MediaFormatHolder, SampleHolder, boolean)}.
|
||||
* {@link #readData(int, long, MediaFormatHolder, SampleHolder)}.
|
||||
* <p>
|
||||
* This method should only be called after the source has been prepared.
|
||||
*
|
||||
@ -115,7 +115,7 @@ public interface SampleSource {
|
||||
|
||||
/**
|
||||
* Enable the specified track. This allows the track's format and samples to be read from
|
||||
* {@link #readData(int, long, MediaFormatHolder, SampleHolder, boolean)}.
|
||||
* {@link #readData(int, long, MediaFormatHolder, SampleHolder)}.
|
||||
* <p>
|
||||
* This method should only be called after the source has been prepared, and when the specified
|
||||
* track is disabled.
|
||||
@ -138,13 +138,27 @@ public interface SampleSource {
|
||||
public boolean continueBuffering(int track, long positionUs);
|
||||
|
||||
/**
|
||||
* Attempts to read either a sample, a new format or or a discontinuity from the source.
|
||||
* Attempts to read a pending discontinuity from the source.
|
||||
* <p>
|
||||
* This method should only be called when the specified track is enabled.
|
||||
*
|
||||
* @param track The track from which to read.
|
||||
* @return If a discontinuity was read then the playback position after the discontinuity. Else
|
||||
* {@link #NO_DISCONTINUITY}.
|
||||
*/
|
||||
public long readDiscontinuity(int track);
|
||||
|
||||
/**
|
||||
* Attempts to read a sample or a new format from the source.
|
||||
* <p>
|
||||
* This method should only be called when the specified track is enabled.
|
||||
* <p>
|
||||
* Note that where multiple tracks are enabled, {@link #NOTHING_READ} may be returned if the
|
||||
* next piece of data to be read from the {@link SampleSource} corresponds to a different track
|
||||
* than the one for which data was requested.
|
||||
* <p>
|
||||
* This method will always return {@link #NOTHING_READ} in the case that there's a pending
|
||||
* discontinuity to be read from {@link #readDiscontinuity(int)} for the specified track.
|
||||
*
|
||||
* @param track The track from which to read.
|
||||
* @param positionUs The current playback position.
|
||||
@ -153,13 +167,11 @@ public interface SampleSource {
|
||||
* @param sampleHolder A {@link SampleHolder} object to populate in the case of a new sample.
|
||||
* If the caller requires the sample data then it must ensure that {@link SampleHolder#data}
|
||||
* references a valid output buffer.
|
||||
* @param onlyReadDiscontinuity Whether to only read a discontinuity. If true, only
|
||||
* {@link #DISCONTINUITY_READ} or {@link #NOTHING_READ} can be returned.
|
||||
* @return The result, which can be {@link #SAMPLE_READ}, {@link #FORMAT_READ},
|
||||
* {@link #DISCONTINUITY_READ}, {@link #NOTHING_READ} or {@link #END_OF_STREAM}.
|
||||
* {@link #NOTHING_READ} or {@link #END_OF_STREAM}.
|
||||
*/
|
||||
public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder, boolean onlyReadDiscontinuity);
|
||||
SampleHolder sampleHolder);
|
||||
|
||||
/**
|
||||
* Seeks to the specified time in microseconds.
|
||||
|
@ -48,7 +48,7 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doPrepare(long positionUs) throws ExoPlaybackException {
|
||||
protected final boolean doPrepare(long positionUs) throws ExoPlaybackException {
|
||||
boolean allSourcesPrepared = true;
|
||||
for (int i = 0; i < sources.length; i++) {
|
||||
allSourcesPrepared &= sources[i].prepare(positionUs);
|
||||
@ -103,26 +103,30 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this renderer is capable of handling the provided track.
|
||||
*
|
||||
* @param mediaFormat The format of the track.
|
||||
* @return True if the renderer can handle the track, false otherwise.
|
||||
* @throws DecoderQueryException Thrown if there was an error querying decoders.
|
||||
*/
|
||||
protected abstract boolean handlesTrack(MediaFormat mediaFormat) throws DecoderQueryException;
|
||||
|
||||
@Override
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
positionUs = shiftInputPosition(positionUs);
|
||||
enabledSource = sources[handledSourceIndices[track]];
|
||||
enabledSourceTrackIndex = handledSourceTrackIndices[track];
|
||||
enabledSource.enable(enabledSourceTrackIndex, positionUs);
|
||||
onDiscontinuity(positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
protected final void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
positionUs = shiftInputPosition(positionUs);
|
||||
enabledSource.seekToUs(positionUs);
|
||||
checkForDiscontinuity(positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void doSomeWork(long positionUs, long elapsedRealtimeUs)
|
||||
throws ExoPlaybackException {
|
||||
positionUs = shiftInputPosition(positionUs);
|
||||
boolean sourceIsReady = enabledSource.continueBuffering(enabledSourceTrackIndex, positionUs);
|
||||
positionUs = checkForDiscontinuity(positionUs);
|
||||
doSomeWork(positionUs, elapsedRealtimeUs, sourceIsReady);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -147,14 +151,6 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeThrowError(SampleSourceReader source) throws ExoPlaybackException {
|
||||
try {
|
||||
source.maybeThrowError();
|
||||
} catch (IOException e) {
|
||||
throw new ExoPlaybackException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDisabled() throws ExoPlaybackException {
|
||||
enabledSource.disable(enabledSourceTrackIndex);
|
||||
@ -169,16 +165,6 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
protected final boolean continueBufferingSource(long positionUs) {
|
||||
return enabledSource.continueBuffering(enabledSourceTrackIndex, positionUs);
|
||||
}
|
||||
|
||||
protected final int readSource(long positionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
|
||||
return enabledSource.readData(enabledSourceTrackIndex, positionUs, formatHolder, sampleHolder,
|
||||
onlyReadDiscontinuity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final int getTrackCount() {
|
||||
return handledSourceTrackIndices.length;
|
||||
@ -190,4 +176,92 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
|
||||
return source.getFormat(handledSourceTrackIndices[track]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts positions passed to {@link #onEnabled(int, long, boolean)}, {@link #seekTo(long)} and
|
||||
* {@link #doSomeWork(long, long)}.
|
||||
* <p>
|
||||
* The default implementation does not modify the position. Except in very specific cases,
|
||||
* subclasses should not override this method.
|
||||
*
|
||||
* @param positionUs The position in microseconds.
|
||||
* @return The adjusted position in microseconds.
|
||||
*/
|
||||
protected long shiftInputPosition(long positionUs) {
|
||||
return positionUs;
|
||||
}
|
||||
|
||||
// Methods to be called by subclasses.
|
||||
|
||||
/**
|
||||
* Reads from the enabled upstream source.
|
||||
*
|
||||
* @param positionUs The current playback position.
|
||||
* @param formatHolder A {@link MediaFormatHolder} object to populate in the case of a new format.
|
||||
* @param sampleHolder A {@link SampleHolder} object to populate in the case of a new sample.
|
||||
* If the caller requires the sample data then it must ensure that {@link SampleHolder#data}
|
||||
* references a valid output buffer.
|
||||
* @return The result, which can be {@link SampleSource#SAMPLE_READ},
|
||||
* {@link SampleSource#FORMAT_READ}, {@link SampleSource#NOTHING_READ} or
|
||||
* {@link SampleSource#END_OF_STREAM}.
|
||||
*/
|
||||
protected final int readSource(long positionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder) {
|
||||
return enabledSource.readData(enabledSourceTrackIndex, positionUs, formatHolder, sampleHolder);
|
||||
}
|
||||
|
||||
// Abstract methods.
|
||||
|
||||
/**
|
||||
* Returns whether this renderer is capable of handling the provided track.
|
||||
*
|
||||
* @param mediaFormat The format of the track.
|
||||
* @return True if the renderer can handle the track, false otherwise.
|
||||
* @throws DecoderQueryException Thrown if there was an error querying decoders.
|
||||
*/
|
||||
protected abstract boolean handlesTrack(MediaFormat mediaFormat) throws DecoderQueryException;
|
||||
|
||||
/**
|
||||
* Invoked when a discontinuity is encountered. Also invoked when the renderer is enabled, for
|
||||
* convenience.
|
||||
*
|
||||
* @param positionUs The playback position after the discontinuity, or the position at which
|
||||
* the renderer is being enabled.
|
||||
* @throws ExoPlaybackException If an error occurs handling the discontinuity.
|
||||
*/
|
||||
protected abstract void onDiscontinuity(long positionUs) throws ExoPlaybackException;
|
||||
|
||||
/**
|
||||
* Called by {@link #doSomeWork(long, long)}.
|
||||
*
|
||||
* @param positionUs The current media time in microseconds, measured at the start of the
|
||||
* current iteration of the rendering loop.
|
||||
* @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds,
|
||||
* measured at the start of the current iteration of the rendering loop.
|
||||
* @param sourceIsReady The result of the most recent call to
|
||||
* {@link SampleSourceReader#continueBuffering(int, long)}.
|
||||
* @throws ExoPlaybackException If an error occurs.
|
||||
* @throws ExoPlaybackException
|
||||
*/
|
||||
protected abstract void doSomeWork(long positionUs, long elapsedRealtimeUs, boolean sourceIsReady)
|
||||
throws ExoPlaybackException;
|
||||
|
||||
// Private methods.
|
||||
|
||||
private long checkForDiscontinuity(long positionUs) throws ExoPlaybackException {
|
||||
long discontinuityPositionUs = enabledSource.readDiscontinuity(enabledSourceTrackIndex);
|
||||
if (discontinuityPositionUs != SampleSource.NO_DISCONTINUITY) {
|
||||
onDiscontinuity(discontinuityPositionUs);
|
||||
return discontinuityPositionUs;
|
||||
}
|
||||
return positionUs;
|
||||
}
|
||||
|
||||
private void maybeThrowError(SampleSourceReader source) throws ExoPlaybackException {
|
||||
try {
|
||||
source.maybeThrowError();
|
||||
} catch (IOException e) {
|
||||
throw new ExoPlaybackException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -119,12 +119,15 @@ public final class SingleSampleSource implements SampleSource, SampleSourceReade
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readDiscontinuity(int track) {
|
||||
return NO_DISCONTINUITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
|
||||
if (onlyReadDiscontinuity) {
|
||||
return NOTHING_READ;
|
||||
} else if (state == STATE_END_OF_STREAM) {
|
||||
SampleHolder sampleHolder) {
|
||||
if (state == STATE_END_OF_STREAM) {
|
||||
return END_OF_STREAM;
|
||||
} else if (state == STATE_SEND_FORMAT) {
|
||||
formatHolder.format = format;
|
||||
|
@ -222,22 +222,22 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
|
||||
return loadingFinished || !sampleQueue.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readDiscontinuity(int track) {
|
||||
if (pendingDiscontinuity) {
|
||||
pendingDiscontinuity = false;
|
||||
return lastSeekPositionUs;
|
||||
}
|
||||
return NO_DISCONTINUITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
|
||||
SampleHolder sampleHolder) {
|
||||
Assertions.checkState(state == STATE_ENABLED);
|
||||
downstreamPositionUs = positionUs;
|
||||
|
||||
if (pendingDiscontinuity) {
|
||||
pendingDiscontinuity = false;
|
||||
return DISCONTINUITY_READ;
|
||||
}
|
||||
|
||||
if (onlyReadDiscontinuity) {
|
||||
return NOTHING_READ;
|
||||
}
|
||||
|
||||
if (isPendingReset()) {
|
||||
if (pendingDiscontinuity || isPendingReset()) {
|
||||
return NOTHING_READ;
|
||||
}
|
||||
|
||||
|
@ -384,16 +384,20 @@ public final class ExtractorSampleSource implements SampleSource, SampleSourceRe
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readData(int track, long playbackPositionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
|
||||
downstreamPositionUs = playbackPositionUs;
|
||||
|
||||
public long readDiscontinuity(int track) {
|
||||
if (pendingDiscontinuities[track]) {
|
||||
pendingDiscontinuities[track] = false;
|
||||
return DISCONTINUITY_READ;
|
||||
return lastSeekPositionUs;
|
||||
}
|
||||
return NO_DISCONTINUITY;
|
||||
}
|
||||
|
||||
if (onlyReadDiscontinuity || isPendingReset()) {
|
||||
@Override
|
||||
public int readData(int track, long playbackPositionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder) {
|
||||
downstreamPositionUs = playbackPositionUs;
|
||||
|
||||
if (pendingDiscontinuities[track] || isPendingReset()) {
|
||||
return NOTHING_READ;
|
||||
}
|
||||
|
||||
|
@ -281,22 +281,22 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readDiscontinuity(int track) {
|
||||
if (pendingDiscontinuities[track]) {
|
||||
pendingDiscontinuities[track] = false;
|
||||
return lastSeekPositionUs;
|
||||
}
|
||||
return NO_DISCONTINUITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readData(int track, long playbackPositionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
|
||||
SampleHolder sampleHolder) {
|
||||
Assertions.checkState(prepared);
|
||||
downstreamPositionUs = playbackPositionUs;
|
||||
|
||||
if (pendingDiscontinuities[track]) {
|
||||
pendingDiscontinuities[track] = false;
|
||||
return DISCONTINUITY_READ;
|
||||
}
|
||||
|
||||
if (onlyReadDiscontinuity) {
|
||||
return NOTHING_READ;
|
||||
}
|
||||
|
||||
if (isPendingReset()) {
|
||||
if (pendingDiscontinuities[track] || isPendingReset()) {
|
||||
return NOTHING_READ;
|
||||
}
|
||||
|
||||
|
@ -93,29 +93,17 @@ public final class MetadataTrackRenderer<T> extends SampleSourceTrackRenderer im
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
super.seekTo(positionUs);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
private void seekToInternal() {
|
||||
protected void onDiscontinuity(long positionUs) {
|
||||
pendingMetadata = null;
|
||||
inputStreamEnded = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||
continueBufferingSource(positionUs);
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs, boolean sourceIsReady)
|
||||
throws ExoPlaybackException {
|
||||
if (!inputStreamEnded && pendingMetadata == null) {
|
||||
sampleHolder.clearData();
|
||||
int result = readSource(positionUs, formatHolder, sampleHolder, false);
|
||||
int result = readSource(positionUs, formatHolder, sampleHolder);
|
||||
if (result == SampleSource.SAMPLE_READ) {
|
||||
pendingMetadataTimestamp = sampleHolder.timeUs;
|
||||
try {
|
||||
|
@ -185,26 +185,22 @@ public final class TextTrackRenderer extends SampleSourceTrackRenderer implement
|
||||
parserThread = new HandlerThread("textParser");
|
||||
parserThread.start();
|
||||
parserHelper = new SubtitleParserHelper(parserThread.getLooper(), subtitleParsers[parserIndex]);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
super.seekTo(positionUs);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
private void seekToInternal() {
|
||||
protected void onDiscontinuity(long positionUs) {
|
||||
inputStreamEnded = false;
|
||||
subtitle = null;
|
||||
nextSubtitle = null;
|
||||
parserHelper.flush();
|
||||
clearTextRenderer();
|
||||
if (parserHelper != null) {
|
||||
parserHelper.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||
continueBufferingSource(positionUs);
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs, boolean sourceIsReady)
|
||||
throws ExoPlaybackException {
|
||||
if (nextSubtitle == null) {
|
||||
try {
|
||||
nextSubtitle = parserHelper.getAndClearResult();
|
||||
@ -247,7 +243,7 @@ public final class TextTrackRenderer extends SampleSourceTrackRenderer implement
|
||||
// Try and read the next subtitle from the source.
|
||||
SampleHolder sampleHolder = parserHelper.getSampleHolder();
|
||||
sampleHolder.clearData();
|
||||
int result = readSource(positionUs, formatHolder, sampleHolder, false);
|
||||
int result = readSource(positionUs, formatHolder, sampleHolder);
|
||||
if (result == SampleSource.FORMAT_READ) {
|
||||
parserHelper.setFormat(formatHolder.format);
|
||||
} else if (result == SampleSource.SAMPLE_READ) {
|
||||
|
@ -97,16 +97,10 @@ public final class Eia608TrackRenderer extends SampleSourceTrackRenderer impleme
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
super.seekTo(positionUs);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
private void seekToInternal() {
|
||||
protected void onDiscontinuity(long positionUs) {
|
||||
inputStreamEnded = false;
|
||||
pendingCaptionLists.clear();
|
||||
clearPendingSample();
|
||||
@ -116,15 +110,15 @@ public final class Eia608TrackRenderer extends SampleSourceTrackRenderer impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||
continueBufferingSource(positionUs);
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs, boolean sourceIsReady)
|
||||
throws ExoPlaybackException {
|
||||
if (isSamplePending()) {
|
||||
maybeParsePendingSample(positionUs);
|
||||
}
|
||||
|
||||
int result = inputStreamEnded ? SampleSource.END_OF_STREAM : SampleSource.SAMPLE_READ;
|
||||
while (!isSamplePending() && result == SampleSource.SAMPLE_READ) {
|
||||
result = readSource(positionUs, formatHolder, sampleHolder, false);
|
||||
result = readSource(positionUs, formatHolder, sampleHolder);
|
||||
if (result == SampleSource.SAMPLE_READ) {
|
||||
maybeParsePendingSample(positionUs);
|
||||
} else if (result == SampleSource.END_OF_STREAM) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user