mirror of
https://github.com/androidx/media.git
synced 2025-05-21 23:56:32 +08:00
Smoother playback #1.
Propagate elapsedRealtimeUs to the video renderer. This allows the renderer to calculate and adjust for the elapsed time since the start of the current rendering loop. Typically this is <2ms, but there situations where it can go higher (normally when the video renderer ends up processing more than 1 output buffer in a single loop). Also made variable naming more consistent throughout the package.
This commit is contained in:
parent
3b4409ae0b
commit
027d9eefbd
@ -68,10 +68,10 @@ import android.widget.TextView;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSomeWork(long timeUs) throws ExoPlaybackException {
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||
maybeFail();
|
||||
if (timeUs < currentPositionUs || timeUs > currentPositionUs + 1000000) {
|
||||
currentPositionUs = timeUs;
|
||||
if (positionUs < currentPositionUs || positionUs > currentPositionUs + 1000000) {
|
||||
currentPositionUs = positionUs;
|
||||
textView.post(this);
|
||||
}
|
||||
}
|
||||
|
@ -40,12 +40,12 @@ public class DummyTrackRenderer extends TrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long timeUs) {
|
||||
protected void seekTo(long positionUs) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSomeWork(long timeUs) {
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,7 @@ import java.util.List;
|
||||
private int state;
|
||||
private int customMessagesSent = 0;
|
||||
private int customMessagesProcessed = 0;
|
||||
private long elapsedRealtimeUs;
|
||||
|
||||
private volatile long durationUs;
|
||||
private volatile long positionUs;
|
||||
@ -383,7 +384,8 @@ import java.util.List;
|
||||
positionUs = timeSourceTrackRenderer != null &&
|
||||
enabledRenderers.contains(timeSourceTrackRenderer) ?
|
||||
timeSourceTrackRenderer.getCurrentPositionUs() :
|
||||
mediaClock.getTimeUs();
|
||||
mediaClock.getPositionUs();
|
||||
elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
|
||||
}
|
||||
|
||||
private void doSomeWork() throws ExoPlaybackException {
|
||||
@ -399,7 +401,7 @@ import java.util.List;
|
||||
// TODO: Each renderer should return the maximum delay before which it wishes to be
|
||||
// invoked again. The minimum of these values should then be used as the delay before the next
|
||||
// invocation of this method.
|
||||
renderer.doSomeWork(positionUs);
|
||||
renderer.doSomeWork(positionUs, elapsedRealtimeUs);
|
||||
isEnded = isEnded && renderer.isEnded();
|
||||
allRenderersReadyOrEnded = allRenderersReadyOrEnded && rendererReadyOrEnded(renderer);
|
||||
|
||||
@ -462,7 +464,7 @@ import java.util.List;
|
||||
rebuffering = false;
|
||||
positionUs = positionMs * 1000L;
|
||||
mediaClock.stop();
|
||||
mediaClock.setTimeUs(positionUs);
|
||||
mediaClock.setPositionUs(positionUs);
|
||||
if (state == ExoPlayer.STATE_IDLE || state == ExoPlayer.STATE_PREPARING) {
|
||||
return;
|
||||
}
|
||||
@ -582,7 +584,7 @@ import java.util.List;
|
||||
if (renderer == timeSourceTrackRenderer) {
|
||||
// We've been using timeSourceTrackRenderer to advance the current position, but it's
|
||||
// being disabled. Sync mediaClock so that it can take over timing responsibilities.
|
||||
mediaClock.setTimeUs(renderer.getCurrentPositionUs());
|
||||
mediaClock.setPositionUs(renderer.getCurrentPositionUs());
|
||||
}
|
||||
ensureStopped(renderer);
|
||||
enabledRenderers.remove(renderer);
|
||||
|
@ -50,7 +50,7 @@ public final class FrameworkSampleSource implements SampleSource {
|
||||
private int[] trackStates;
|
||||
private boolean[] pendingDiscontinuities;
|
||||
|
||||
private long seekTimeUs;
|
||||
private long seekPositionUs;
|
||||
|
||||
public FrameworkSampleSource(Context context, Uri uri, Map<String, String> headers,
|
||||
int downstreamRendererCount) {
|
||||
@ -94,16 +94,16 @@ public final class FrameworkSampleSource implements SampleSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable(int track, long timeUs) {
|
||||
public void enable(int track, long positionUs) {
|
||||
Assertions.checkState(prepared);
|
||||
Assertions.checkState(trackStates[track] == TRACK_STATE_DISABLED);
|
||||
trackStates[track] = TRACK_STATE_ENABLED;
|
||||
extractor.selectTrack(track);
|
||||
seekToUs(timeUs);
|
||||
seekToUs(positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean continueBuffering(long playbackPositionUs) {
|
||||
public boolean continueBuffering(long positionUs) {
|
||||
// MediaExtractor takes care of buffering and blocks until it has samples, so we can always
|
||||
// return true here. Although note that the blocking behavior is itself as bug, as per the
|
||||
// TODO further up this file. This method will need to return something else as part of fixing
|
||||
@ -112,7 +112,7 @@ public final class FrameworkSampleSource implements SampleSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readData(int track, long playbackPositionUs, MediaFormatHolder formatHolder,
|
||||
public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
|
||||
Assertions.checkState(prepared);
|
||||
Assertions.checkState(trackStates[track] != TRACK_STATE_DISABLED);
|
||||
@ -144,7 +144,7 @@ public final class FrameworkSampleSource implements SampleSource {
|
||||
if ((sampleHolder.flags & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0) {
|
||||
sampleHolder.cryptoInfo.setFromExtractorV16(extractor);
|
||||
}
|
||||
seekTimeUs = -1;
|
||||
seekPositionUs = -1;
|
||||
extractor.advance();
|
||||
return SAMPLE_READ;
|
||||
} else {
|
||||
@ -168,13 +168,13 @@ public final class FrameworkSampleSource implements SampleSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekToUs(long timeUs) {
|
||||
public void seekToUs(long positionUs) {
|
||||
Assertions.checkState(prepared);
|
||||
if (seekTimeUs != timeUs) {
|
||||
if (seekPositionUs != positionUs) {
|
||||
// Avoid duplicate calls to the underlying extractor's seek method in the case that there
|
||||
// have been no interleaving calls to advance.
|
||||
seekTimeUs = timeUs;
|
||||
extractor.seekTo(timeUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
|
||||
seekPositionUs = positionUs;
|
||||
extractor.seekTo(positionUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
|
||||
for (int i = 0; i < trackStates.length; ++i) {
|
||||
if (trackStates[i] != TRACK_STATE_DISABLED) {
|
||||
pendingDiscontinuities[i] = true;
|
||||
|
@ -29,10 +29,10 @@ import android.os.SystemClock;
|
||||
/**
|
||||
* The media time when the clock was last set or stopped.
|
||||
*/
|
||||
private long timeUs;
|
||||
private long positionUs;
|
||||
|
||||
/**
|
||||
* The difference between {@link SystemClock#elapsedRealtime()} and {@link #timeUs}
|
||||
* The difference between {@link SystemClock#elapsedRealtime()} and {@link #positionUs}
|
||||
* when the clock was last set or started.
|
||||
*/
|
||||
private long deltaUs;
|
||||
@ -43,7 +43,7 @@ import android.os.SystemClock;
|
||||
public void start() {
|
||||
if (!started) {
|
||||
started = true;
|
||||
deltaUs = elapsedRealtimeMinus(timeUs);
|
||||
deltaUs = elapsedRealtimeMinus(positionUs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,28 +52,28 @@ import android.os.SystemClock;
|
||||
*/
|
||||
public void stop() {
|
||||
if (started) {
|
||||
timeUs = elapsedRealtimeMinus(deltaUs);
|
||||
positionUs = elapsedRealtimeMinus(deltaUs);
|
||||
started = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeUs The time to set in microseconds.
|
||||
* @param timeUs The position to set in microseconds.
|
||||
*/
|
||||
public void setTimeUs(long timeUs) {
|
||||
this.timeUs = timeUs;
|
||||
public void setPositionUs(long timeUs) {
|
||||
this.positionUs = timeUs;
|
||||
deltaUs = elapsedRealtimeMinus(timeUs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The current time in microseconds.
|
||||
* @return The current position in microseconds.
|
||||
*/
|
||||
public long getTimeUs() {
|
||||
return started ? elapsedRealtimeMinus(deltaUs) : timeUs;
|
||||
public long getPositionUs() {
|
||||
return started ? elapsedRealtimeMinus(deltaUs) : positionUs;
|
||||
}
|
||||
|
||||
private long elapsedRealtimeMinus(long microSeconds) {
|
||||
return SystemClock.elapsedRealtime() * 1000 - microSeconds;
|
||||
private long elapsedRealtimeMinus(long toSubtractUs) {
|
||||
return SystemClock.elapsedRealtime() * 1000 - toSubtractUs;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -269,14 +269,14 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long timeUs, boolean joining) {
|
||||
super.onEnabled(timeUs, joining);
|
||||
protected void onEnabled(long positionUs, boolean joining) {
|
||||
super.onEnabled(positionUs, joining);
|
||||
lastReportedCurrentPositionUs = Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSomeWork(long timeUs) throws ExoPlaybackException {
|
||||
super.doSomeWork(timeUs);
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||
super.doSomeWork(positionUs, elapsedRealtimeUs);
|
||||
maybeSampleSyncParams();
|
||||
}
|
||||
|
||||
@ -585,16 +585,16 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long timeUs) throws ExoPlaybackException {
|
||||
super.seekTo(timeUs);
|
||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
super.seekTo(positionUs);
|
||||
// TODO: Try and re-use the same AudioTrack instance once [redacted] is fixed.
|
||||
releaseAudioTrack();
|
||||
lastReportedCurrentPositionUs = Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processOutputBuffer(long timeUs, MediaCodec codec, ByteBuffer buffer,
|
||||
MediaCodec.BufferInfo bufferInfo, int bufferIndex, boolean shouldSkip)
|
||||
protected boolean processOutputBuffer(long positionUs, long elapsedRealtimeUs, MediaCodec codec,
|
||||
ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo, int bufferIndex, boolean shouldSkip)
|
||||
throws ExoPlaybackException {
|
||||
if (shouldSkip) {
|
||||
codec.releaseOutputBuffer(bufferIndex, false);
|
||||
|
@ -217,13 +217,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long timeUs, boolean joining) {
|
||||
source.enable(trackIndex, timeUs);
|
||||
protected void onEnabled(long positionUs, boolean joining) {
|
||||
source.enable(trackIndex, positionUs);
|
||||
sourceState = SOURCE_STATE_NOT_READY;
|
||||
inputStreamEnded = false;
|
||||
outputStreamEnded = false;
|
||||
waitingForKeys = false;
|
||||
currentPositionUs = timeUs;
|
||||
currentPositionUs = positionUs;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -367,9 +367,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long timeUs) throws ExoPlaybackException {
|
||||
currentPositionUs = timeUs;
|
||||
source.seekToUs(timeUs);
|
||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
currentPositionUs = positionUs;
|
||||
source.seekToUs(positionUs);
|
||||
sourceState = SOURCE_STATE_NOT_READY;
|
||||
inputStreamEnded = false;
|
||||
outputStreamEnded = false;
|
||||
@ -387,22 +387,22 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSomeWork(long timeUs) throws ExoPlaybackException {
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||
try {
|
||||
sourceState = source.continueBuffering(timeUs)
|
||||
sourceState = source.continueBuffering(positionUs)
|
||||
? (sourceState == SOURCE_STATE_NOT_READY ? SOURCE_STATE_READY : sourceState)
|
||||
: SOURCE_STATE_NOT_READY;
|
||||
checkForDiscontinuity();
|
||||
if (format == null) {
|
||||
readFormat();
|
||||
} else if (codec == null && !shouldInitCodec() && getState() == TrackRenderer.STATE_STARTED) {
|
||||
discardSamples(timeUs);
|
||||
discardSamples(positionUs);
|
||||
} else {
|
||||
if (codec == null && shouldInitCodec()) {
|
||||
maybeInitCodec();
|
||||
}
|
||||
if (codec != null) {
|
||||
while (drainOutputBuffer(timeUs)) {}
|
||||
while (drainOutputBuffer(positionUs, elapsedRealtimeUs)) {}
|
||||
if (feedInputBuffer(true)) {
|
||||
while (feedInputBuffer(false)) {}
|
||||
}
|
||||
@ -421,10 +421,10 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private void discardSamples(long timeUs) throws IOException, ExoPlaybackException {
|
||||
private void discardSamples(long positionUs) throws IOException, ExoPlaybackException {
|
||||
sampleHolder.data = null;
|
||||
int result = SampleSource.SAMPLE_READ;
|
||||
while (result == SampleSource.SAMPLE_READ && currentPositionUs <= timeUs) {
|
||||
while (result == SampleSource.SAMPLE_READ && currentPositionUs <= positionUs) {
|
||||
result = source.readData(trackIndex, currentPositionUs, formatHolder, sampleHolder, false);
|
||||
if (result == SampleSource.SAMPLE_READ) {
|
||||
if (!sampleHolder.decodeOnly) {
|
||||
@ -469,7 +469,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||
|
||||
/**
|
||||
* @param firstFeed True if this is the first call to this method from the current invocation of
|
||||
* {@link #doSomeWork(long)}. False otherwise.
|
||||
* {@link #doSomeWork(long, long)}. False otherwise.
|
||||
* @return True if it may be possible to feed more input data. False otherwise.
|
||||
* @throws IOException If an error occurs reading data from the upstream source.
|
||||
* @throws ExoPlaybackException If an error occurs feeding the input buffer.
|
||||
@ -694,7 +694,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||
* @return True if it may be possible to drain more output data. False otherwise.
|
||||
* @throws ExoPlaybackException If an error occurs draining the output buffer.
|
||||
*/
|
||||
private boolean drainOutputBuffer(long timeUs) throws ExoPlaybackException {
|
||||
private boolean drainOutputBuffer(long positionUs, long elapsedRealtimeUs)
|
||||
throws ExoPlaybackException {
|
||||
if (outputStreamEnded) {
|
||||
return false;
|
||||
}
|
||||
@ -722,8 +723,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||
|
||||
boolean decodeOnly = decodeOnlyPresentationTimestamps.contains(
|
||||
outputBufferInfo.presentationTimeUs);
|
||||
if (processOutputBuffer(timeUs, codec, outputBuffers[outputIndex], outputBufferInfo,
|
||||
outputIndex, decodeOnly)) {
|
||||
if (processOutputBuffer(positionUs, elapsedRealtimeUs, codec, outputBuffers[outputIndex],
|
||||
outputBufferInfo, outputIndex, decodeOnly)) {
|
||||
if (decodeOnly) {
|
||||
decodeOnlyPresentationTimestamps.remove(outputBufferInfo.presentationTimeUs);
|
||||
} else {
|
||||
@ -743,9 +744,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||
* longer required. False otherwise.
|
||||
* @throws ExoPlaybackException If an error occurs processing the output buffer.
|
||||
*/
|
||||
protected abstract boolean processOutputBuffer(long timeUs, MediaCodec codec, ByteBuffer buffer,
|
||||
MediaCodec.BufferInfo bufferInfo, int bufferIndex, boolean shouldSkip)
|
||||
throws ExoPlaybackException;
|
||||
protected abstract boolean processOutputBuffer(long positionUs, long elapsedRealtimeUs,
|
||||
MediaCodec codec, ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo, int bufferIndex,
|
||||
boolean shouldSkip) throws ExoPlaybackException;
|
||||
|
||||
/**
|
||||
* Returns the name of the secure variant of a given decoder.
|
||||
|
@ -225,8 +225,8 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long startTimeUs, boolean joining) {
|
||||
super.onEnabled(startTimeUs, joining);
|
||||
protected void onEnabled(long positionUs, boolean joining) {
|
||||
super.onEnabled(positionUs, joining);
|
||||
renderedFirstFrame = false;
|
||||
if (joining && allowedJoiningTimeUs > 0) {
|
||||
joiningDeadlineUs = SystemClock.elapsedRealtime() * 1000L + allowedJoiningTimeUs;
|
||||
@ -234,8 +234,8 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long timeUs) throws ExoPlaybackException {
|
||||
super.seekTo(timeUs);
|
||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
||||
super.seekTo(positionUs);
|
||||
renderedFirstFrame = false;
|
||||
joiningDeadlineUs = -1;
|
||||
}
|
||||
@ -354,14 +354,15 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processOutputBuffer(long timeUs, MediaCodec codec, ByteBuffer buffer,
|
||||
MediaCodec.BufferInfo bufferInfo, int bufferIndex, boolean shouldSkip) {
|
||||
protected boolean processOutputBuffer(long positionUs, long elapsedRealtimeUs, MediaCodec codec,
|
||||
ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo, int bufferIndex, boolean shouldSkip) {
|
||||
if (shouldSkip) {
|
||||
skipOutputBuffer(codec, bufferIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
long earlyUs = bufferInfo.presentationTimeUs - timeUs;
|
||||
long elapsedSinceStartOfLoop = SystemClock.elapsedRealtime() * 1000 - elapsedRealtimeUs;
|
||||
long earlyUs = bufferInfo.presentationTimeUs - positionUs - elapsedSinceStartOfLoop;
|
||||
if (earlyUs < -30000) {
|
||||
// We're more than 30ms late rendering the frame.
|
||||
dropOutputBuffer(codec, bufferIndex);
|
||||
|
@ -85,9 +85,9 @@ public interface SampleSource {
|
||||
* This method should not be called until after the source has been successfully prepared.
|
||||
*
|
||||
* @param track The track to enable.
|
||||
* @param timeUs The player's current playback position.
|
||||
* @param positionUs The player's current playback position.
|
||||
*/
|
||||
public void enable(int track, long timeUs);
|
||||
public void enable(int track, long positionUs);
|
||||
|
||||
/**
|
||||
* Disable the specified track.
|
||||
@ -101,12 +101,12 @@ public interface SampleSource {
|
||||
/**
|
||||
* Indicates to the source that it should still be buffering data.
|
||||
*
|
||||
* @param playbackPositionUs The current playback position.
|
||||
* @param positionUs The current playback position.
|
||||
* @return True if the source has available samples, or if the end of the stream has been reached.
|
||||
* False if more data needs to be buffered for samples to become available.
|
||||
* @throws IOException If an error occurred reading from the source.
|
||||
*/
|
||||
public boolean continueBuffering(long playbackPositionUs) throws IOException;
|
||||
public boolean continueBuffering(long positionUs) throws IOException;
|
||||
|
||||
/**
|
||||
* Attempts to read either a sample, a new format or or a discontinuity from the source.
|
||||
@ -118,7 +118,7 @@ public interface SampleSource {
|
||||
* than the one for which data was requested.
|
||||
*
|
||||
* @param track The track from which to read.
|
||||
* @param playbackPositionUs The current playback position.
|
||||
* @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}
|
||||
@ -129,7 +129,7 @@ public interface SampleSource {
|
||||
* {@link #DISCONTINUITY_READ}, {@link #NOTHING_READ} or {@link #END_OF_STREAM}.
|
||||
* @throws IOException If an error occurred reading from the source.
|
||||
*/
|
||||
public int readData(int track, long playbackPositionUs, MediaFormatHolder formatHolder,
|
||||
public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) throws IOException;
|
||||
|
||||
/**
|
||||
@ -137,16 +137,16 @@ public interface SampleSource {
|
||||
* <p>
|
||||
* This method should not be called until after the source has been successfully prepared.
|
||||
*
|
||||
* @param timeUs The seek position in microseconds.
|
||||
* @param positionUs The seek position in microseconds.
|
||||
*/
|
||||
public void seekToUs(long timeUs);
|
||||
public void seekToUs(long positionUs);
|
||||
|
||||
/**
|
||||
* Returns an estimate of the position up to which data is buffered.
|
||||
* <p>
|
||||
* This method should not be called until after the source has been successfully prepared.
|
||||
*
|
||||
* @return An estimate of the absolute position in micro-seconds up to which data is buffered,
|
||||
* @return An estimate of the absolute position in microseconds up to which data is buffered,
|
||||
* or {@link TrackRenderer#END_OF_TRACK_US} if data is buffered to the end of the stream, or
|
||||
* {@link TrackRenderer#UNKNOWN_TIME_US} if no estimate is available.
|
||||
*/
|
||||
|
@ -18,6 +18,8 @@ package com.google.android.exoplayer;
|
||||
import com.google.android.exoplayer.ExoPlayer.ExoPlayerComponent;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
/**
|
||||
* Renders a single component of media.
|
||||
*
|
||||
@ -59,7 +61,7 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
|
||||
*/
|
||||
protected static final int STATE_ENABLED = 2;
|
||||
/**
|
||||
* The renderer is started. Calls to {@link #doSomeWork(long)} should cause the media to be
|
||||
* The renderer is started. Calls to {@link #doSomeWork(long, long)} should cause the media to be
|
||||
* rendered.
|
||||
*/
|
||||
protected static final int STATE_STARTED = 3;
|
||||
@ -83,9 +85,9 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
|
||||
/**
|
||||
* A time source renderer is a renderer that, when started, advances its own playback position.
|
||||
* This means that {@link #getCurrentPositionUs()} will return increasing positions independently
|
||||
* to increasing values being passed to {@link #doSomeWork(long)}. A player may have at most one
|
||||
* time source renderer. If provided, the player will use such a renderer as its source of time
|
||||
* during playback.
|
||||
* to increasing values being passed to {@link #doSomeWork(long, long)}. A player may have at most
|
||||
* one time source renderer. If provided, the player will use such a renderer as its source of
|
||||
* time during playback.
|
||||
* <p>
|
||||
* This method may be called when the renderer is in any state.
|
||||
*
|
||||
@ -136,15 +138,15 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
|
||||
/**
|
||||
* Enable the renderer.
|
||||
*
|
||||
* @param timeUs The player's current position.
|
||||
* @param positionUs The player's current position.
|
||||
* @param joining Whether this renderer is being enabled to join an ongoing playback. If true
|
||||
* then {@link #start} must be called immediately after this method returns (unless a
|
||||
* {@link ExoPlaybackException} is thrown).
|
||||
*/
|
||||
/* package */ final void enable(long timeUs, boolean joining) throws ExoPlaybackException {
|
||||
/* package */ final void enable(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
Assertions.checkState(state == TrackRenderer.STATE_PREPARED);
|
||||
state = TrackRenderer.STATE_ENABLED;
|
||||
onEnabled(timeUs, joining);
|
||||
onEnabled(positionUs, joining);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,18 +154,18 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
|
||||
* <p>
|
||||
* The default implementation is a no-op.
|
||||
*
|
||||
* @param timeUs The player's current position.
|
||||
* @param positionUs The player's current position.
|
||||
* @param joining Whether this renderer is being enabled to join an ongoing playback. If true
|
||||
* then {@link #onStarted} is guaranteed to be called immediately after this method returns
|
||||
* (unless a {@link ExoPlaybackException} is thrown).
|
||||
* @throws ExoPlaybackException If an error occurs.
|
||||
*/
|
||||
protected void onEnabled(long timeUs, boolean joining) throws ExoPlaybackException {
|
||||
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the renderer, meaning that calls to {@link #doSomeWork(long)} will cause the
|
||||
* Starts the renderer, meaning that calls to {@link #doSomeWork(long, long)} will cause the
|
||||
* track to be rendered.
|
||||
*/
|
||||
/* package */ final void start() throws ExoPlaybackException {
|
||||
@ -289,10 +291,14 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
|
||||
* This method may be called when the renderer is in the following states:
|
||||
* {@link #STATE_ENABLED}, {@link #STATE_STARTED}
|
||||
*
|
||||
* @param timeUs The current playback time.
|
||||
* @param positionUs The current media time in microseconds, measured at the start of the
|
||||
* current iteration of the rendering loop.
|
||||
* @param elapsedRealtimeUs {@link SystemClock#elapsedRealtime()} in microseconds, measured at
|
||||
* the start of the current iteration of the rendering loop.
|
||||
* @throws ExoPlaybackException If an error occurs.
|
||||
*/
|
||||
protected abstract void doSomeWork(long timeUs) throws ExoPlaybackException;
|
||||
protected abstract void doSomeWork(long positionUs, long elapsedRealtimeUs)
|
||||
throws ExoPlaybackException;
|
||||
|
||||
/**
|
||||
* Returns the duration of the media being rendered.
|
||||
@ -300,7 +306,7 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
|
||||
* This method may be called when the renderer is in the following states:
|
||||
* {@link #STATE_PREPARED}, {@link #STATE_ENABLED}, {@link #STATE_STARTED}
|
||||
*
|
||||
* @return The duration of the track in micro-seconds, or {@link #MATCH_LONGEST_US} if
|
||||
* @return The duration of the track in microseconds, or {@link #MATCH_LONGEST_US} if
|
||||
* the track's duration should match that of the longest track whose duration is known, or
|
||||
* or {@link #UNKNOWN_TIME_US} if the duration is not known.
|
||||
*/
|
||||
@ -312,17 +318,17 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
|
||||
* This method may be called when the renderer is in the following states:
|
||||
* {@link #STATE_ENABLED}, {@link #STATE_STARTED}
|
||||
*
|
||||
* @return The current playback position in micro-seconds.
|
||||
* @return The current playback position in microseconds.
|
||||
*/
|
||||
protected abstract long getCurrentPositionUs();
|
||||
|
||||
/**
|
||||
* Returns an estimate of the absolute position in micro-seconds up to which data is buffered.
|
||||
* Returns an estimate of the absolute position in microseconds up to which data is buffered.
|
||||
* <p>
|
||||
* This method may be called when the renderer is in the following states:
|
||||
* {@link #STATE_ENABLED}, {@link #STATE_STARTED}
|
||||
*
|
||||
* @return An estimate of the absolute position in micro-seconds up to which data is buffered,
|
||||
* @return An estimate of the absolute position in microseconds up to which data is buffered,
|
||||
* or {@link #END_OF_TRACK_US} if the track is fully buffered, or {@link #UNKNOWN_TIME_US} if
|
||||
* no estimate is available.
|
||||
*/
|
||||
@ -334,10 +340,10 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
|
||||
* This method may be called when the renderer is in the following states:
|
||||
* {@link #STATE_ENABLED}
|
||||
*
|
||||
* @param timeUs The desired time in micro-seconds.
|
||||
* @param positionUs The desired playback position in microseconds.
|
||||
* @throws ExoPlaybackException If an error occurs.
|
||||
*/
|
||||
protected abstract void seekTo(long timeUs) throws ExoPlaybackException;
|
||||
protected abstract void seekTo(long positionUs) throws ExoPlaybackException;
|
||||
|
||||
@Override
|
||||
public void handleMessage(int what, Object object) throws ExoPlaybackException {
|
||||
|
@ -154,7 +154,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
private int state;
|
||||
private long downstreamPositionUs;
|
||||
private long lastSeekPositionUs;
|
||||
private long pendingResetTime;
|
||||
private long pendingResetPositionUs;
|
||||
private long lastPerformedBufferOperation;
|
||||
private boolean pendingDiscontinuity;
|
||||
|
||||
@ -219,7 +219,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable(int track, long timeUs) {
|
||||
public void enable(int track, long positionUs) {
|
||||
Assertions.checkState(state == STATE_PREPARED);
|
||||
Assertions.checkState(track == 0);
|
||||
state = STATE_ENABLED;
|
||||
@ -227,9 +227,9 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
loadControl.register(this, bufferSizeContribution);
|
||||
downstreamFormat = null;
|
||||
downstreamMediaFormat = null;
|
||||
downstreamPositionUs = timeUs;
|
||||
lastSeekPositionUs = timeUs;
|
||||
restartFrom(timeUs);
|
||||
downstreamPositionUs = positionUs;
|
||||
lastSeekPositionUs = positionUs;
|
||||
restartFrom(positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -253,10 +253,10 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean continueBuffering(long playbackPositionUs) throws IOException {
|
||||
public boolean continueBuffering(long positionUs) throws IOException {
|
||||
Assertions.checkState(state == STATE_ENABLED);
|
||||
downstreamPositionUs = playbackPositionUs;
|
||||
chunkSource.continueBuffering(playbackPositionUs);
|
||||
downstreamPositionUs = positionUs;
|
||||
chunkSource.continueBuffering(positionUs);
|
||||
updateLoadControl();
|
||||
if (isPendingReset() || mediaChunks.isEmpty()) {
|
||||
return false;
|
||||
@ -271,7 +271,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readData(int track, long playbackPositionUs, MediaFormatHolder formatHolder,
|
||||
public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) throws IOException {
|
||||
Assertions.checkState(state == STATE_ENABLED);
|
||||
Assertions.checkState(track == 0);
|
||||
@ -285,7 +285,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
return NOTHING_READ;
|
||||
}
|
||||
|
||||
downstreamPositionUs = playbackPositionUs;
|
||||
downstreamPositionUs = positionUs;
|
||||
if (isPendingReset()) {
|
||||
if (currentLoadableException != null) {
|
||||
throw currentLoadableException;
|
||||
@ -304,7 +304,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
discardDownstreamMediaChunk();
|
||||
mediaChunk = mediaChunks.getFirst();
|
||||
mediaChunk.seekToStart();
|
||||
return readData(track, playbackPositionUs, formatHolder, sampleHolder, false);
|
||||
return readData(track, positionUs, formatHolder, sampleHolder, false);
|
||||
} else if (mediaChunk.isLastChunk()) {
|
||||
return END_OF_STREAM;
|
||||
}
|
||||
@ -350,32 +350,32 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekToUs(long timeUs) {
|
||||
public void seekToUs(long positionUs) {
|
||||
Assertions.checkState(state == STATE_ENABLED);
|
||||
downstreamPositionUs = timeUs;
|
||||
lastSeekPositionUs = timeUs;
|
||||
if (pendingResetTime == timeUs) {
|
||||
downstreamPositionUs = positionUs;
|
||||
lastSeekPositionUs = positionUs;
|
||||
if (pendingResetPositionUs == positionUs) {
|
||||
return;
|
||||
}
|
||||
|
||||
MediaChunk mediaChunk = getMediaChunk(timeUs);
|
||||
MediaChunk mediaChunk = getMediaChunk(positionUs);
|
||||
if (mediaChunk == null) {
|
||||
restartFrom(timeUs);
|
||||
restartFrom(positionUs);
|
||||
pendingDiscontinuity = true;
|
||||
} else {
|
||||
pendingDiscontinuity |= mediaChunk.seekTo(timeUs, mediaChunk == mediaChunks.getFirst());
|
||||
pendingDiscontinuity |= mediaChunk.seekTo(positionUs, mediaChunk == mediaChunks.getFirst());
|
||||
discardDownstreamMediaChunks(mediaChunk);
|
||||
updateLoadControl();
|
||||
}
|
||||
}
|
||||
|
||||
private MediaChunk getMediaChunk(long timeUs) {
|
||||
private MediaChunk getMediaChunk(long positionUs) {
|
||||
Iterator<MediaChunk> mediaChunkIterator = mediaChunks.iterator();
|
||||
while (mediaChunkIterator.hasNext()) {
|
||||
MediaChunk mediaChunk = mediaChunkIterator.next();
|
||||
if (timeUs < mediaChunk.startTimeUs) {
|
||||
if (positionUs < mediaChunk.startTimeUs) {
|
||||
return null;
|
||||
} else if (mediaChunk.isLastChunk() || timeUs < mediaChunk.endTimeUs) {
|
||||
} else if (mediaChunk.isLastChunk() || positionUs < mediaChunk.endTimeUs) {
|
||||
return mediaChunk;
|
||||
}
|
||||
}
|
||||
@ -386,7 +386,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
public long getBufferedPositionUs() {
|
||||
Assertions.checkState(state == STATE_ENABLED);
|
||||
if (isPendingReset()) {
|
||||
return pendingResetTime;
|
||||
return pendingResetPositionUs;
|
||||
}
|
||||
MediaChunk mediaChunk = mediaChunks.getLast();
|
||||
Chunk currentLoadable = currentLoadableHolder.chunk;
|
||||
@ -448,7 +448,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
}
|
||||
clearCurrentLoadable();
|
||||
if (state == STATE_ENABLED) {
|
||||
restartFrom(pendingResetTime);
|
||||
restartFrom(pendingResetPositionUs);
|
||||
} else {
|
||||
clearMediaChunks();
|
||||
loadControl.trimAllocator();
|
||||
@ -476,8 +476,8 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
// no-op
|
||||
}
|
||||
|
||||
private void restartFrom(long timeUs) {
|
||||
pendingResetTime = timeUs;
|
||||
private void restartFrom(long positionUs) {
|
||||
pendingResetPositionUs = positionUs;
|
||||
if (loader.isLoading()) {
|
||||
loader.cancelLoading();
|
||||
} else {
|
||||
@ -501,7 +501,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
private void updateLoadControl() {
|
||||
long loadPositionUs;
|
||||
if (isPendingReset()) {
|
||||
loadPositionUs = pendingResetTime;
|
||||
loadPositionUs = pendingResetPositionUs;
|
||||
} else {
|
||||
MediaChunk lastMediaChunk = mediaChunks.getLast();
|
||||
loadPositionUs = lastMediaChunk.nextChunkIndex == -1 ? -1 : lastMediaChunk.endTimeUs;
|
||||
@ -529,8 +529,8 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
if (currentLoadableHolder.chunk == null || now - lastPerformedBufferOperation > 1000) {
|
||||
lastPerformedBufferOperation = now;
|
||||
currentLoadableHolder.queueSize = readOnlyMediaChunks.size();
|
||||
chunkSource.getChunkOperation(readOnlyMediaChunks, pendingResetTime, downstreamPositionUs,
|
||||
currentLoadableHolder);
|
||||
chunkSource.getChunkOperation(readOnlyMediaChunks, pendingResetPositionUs,
|
||||
downstreamPositionUs, currentLoadableHolder);
|
||||
discardUpstreamMediaChunks(currentLoadableHolder.queueSize);
|
||||
}
|
||||
if (nextLoader) {
|
||||
@ -552,8 +552,8 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
Chunk backedOffChunk = currentLoadableHolder.chunk;
|
||||
if (!isMediaChunk(backedOffChunk)) {
|
||||
currentLoadableHolder.queueSize = readOnlyMediaChunks.size();
|
||||
chunkSource.getChunkOperation(readOnlyMediaChunks, pendingResetTime, downstreamPositionUs,
|
||||
currentLoadableHolder);
|
||||
chunkSource.getChunkOperation(readOnlyMediaChunks, pendingResetPositionUs,
|
||||
downstreamPositionUs, currentLoadableHolder);
|
||||
discardUpstreamMediaChunks(currentLoadableHolder.queueSize);
|
||||
if (currentLoadableHolder.chunk == backedOffChunk) {
|
||||
// Chunk was unchanged. Resume loading.
|
||||
@ -577,7 +577,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
MediaChunk removedChunk = mediaChunks.removeLast();
|
||||
Assertions.checkState(backedOffChunk == removedChunk);
|
||||
currentLoadableHolder.queueSize = readOnlyMediaChunks.size();
|
||||
chunkSource.getChunkOperation(readOnlyMediaChunks, pendingResetTime, downstreamPositionUs,
|
||||
chunkSource.getChunkOperation(readOnlyMediaChunks, pendingResetPositionUs, downstreamPositionUs,
|
||||
currentLoadableHolder);
|
||||
mediaChunks.add(removedChunk);
|
||||
|
||||
@ -603,8 +603,8 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
if (isMediaChunk(currentLoadable)) {
|
||||
MediaChunk mediaChunk = (MediaChunk) currentLoadable;
|
||||
if (isPendingReset()) {
|
||||
mediaChunk.seekTo(pendingResetTime, false);
|
||||
pendingResetTime = NO_RESET_PENDING;
|
||||
mediaChunk.seekTo(pendingResetPositionUs, false);
|
||||
pendingResetPositionUs = NO_RESET_PENDING;
|
||||
}
|
||||
mediaChunks.add(mediaChunk);
|
||||
notifyLoadStarted(mediaChunk.format.id, mediaChunk.trigger, false,
|
||||
@ -674,7 +674,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
}
|
||||
|
||||
private boolean isPendingReset() {
|
||||
return pendingResetTime != NO_RESET_PENDING;
|
||||
return pendingResetPositionUs != NO_RESET_PENDING;
|
||||
}
|
||||
|
||||
private long getRetryDelayMillis(long errorCount) {
|
||||
@ -757,13 +757,13 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
}
|
||||
|
||||
private void notifyDownstreamFormatChanged(final String formatId, final int trigger,
|
||||
final long mediaTimeUs) {
|
||||
final long positionUs) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onDownstreamFormatChanged(eventSourceId, formatId, trigger,
|
||||
usToMs(mediaTimeUs));
|
||||
usToMs(positionUs));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -115,43 +115,43 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long timeUs, boolean joining) {
|
||||
source.enable(trackIndex, timeUs);
|
||||
protected void onEnabled(long positionUs, boolean joining) {
|
||||
source.enable(trackIndex, positionUs);
|
||||
parserThread = new HandlerThread("textParser");
|
||||
parserThread.start();
|
||||
parserHelper = new SubtitleParserHelper(parserThread.getLooper(), subtitleParser);
|
||||
seekToInternal(timeUs);
|
||||
seekToInternal(positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long timeUs) {
|
||||
source.seekToUs(timeUs);
|
||||
seekToInternal(timeUs);
|
||||
protected void seekTo(long positionUs) {
|
||||
source.seekToUs(positionUs);
|
||||
seekToInternal(positionUs);
|
||||
}
|
||||
|
||||
private void seekToInternal(long timeUs) {
|
||||
private void seekToInternal(long positionUs) {
|
||||
inputStreamEnded = false;
|
||||
currentPositionUs = timeUs;
|
||||
source.seekToUs(timeUs);
|
||||
if (subtitle != null && (timeUs < subtitle.getStartTime()
|
||||
|| subtitle.getLastEventTime() <= timeUs)) {
|
||||
currentPositionUs = positionUs;
|
||||
source.seekToUs(positionUs);
|
||||
if (subtitle != null && (positionUs < subtitle.getStartTime()
|
||||
|| subtitle.getLastEventTime() <= positionUs)) {
|
||||
subtitle = null;
|
||||
}
|
||||
parserHelper.flush();
|
||||
clearTextRenderer();
|
||||
syncNextEventIndex(timeUs);
|
||||
syncNextEventIndex(positionUs);
|
||||
textRendererNeedsUpdate = subtitle != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSomeWork(long timeUs) throws ExoPlaybackException {
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||
try {
|
||||
source.continueBuffering(timeUs);
|
||||
source.continueBuffering(positionUs);
|
||||
} catch (IOException e) {
|
||||
throw new ExoPlaybackException(e);
|
||||
}
|
||||
|
||||
currentPositionUs = timeUs;
|
||||
currentPositionUs = positionUs;
|
||||
|
||||
if (parserHelper.isParsing()) {
|
||||
return;
|
||||
@ -169,13 +169,13 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
|
||||
if (subtitle == null && dequeuedSubtitle != null) {
|
||||
// We've dequeued a new subtitle. Sync the event index and update the subtitle.
|
||||
subtitle = dequeuedSubtitle;
|
||||
syncNextEventIndex(timeUs);
|
||||
syncNextEventIndex(positionUs);
|
||||
textRendererNeedsUpdate = true;
|
||||
} else if (subtitle != null) {
|
||||
// We're iterating through the events in a subtitle. Set textRendererNeedsUpdate if we
|
||||
// advance to the next event.
|
||||
long nextEventTimeUs = getNextEventTime();
|
||||
while (nextEventTimeUs <= timeUs) {
|
||||
while (nextEventTimeUs <= positionUs) {
|
||||
nextSubtitleEventIndex++;
|
||||
nextEventTimeUs = getNextEventTime();
|
||||
textRendererNeedsUpdate = true;
|
||||
@ -191,7 +191,7 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
|
||||
if (subtitle == null) {
|
||||
try {
|
||||
SampleHolder sampleHolder = parserHelper.getSampleHolder();
|
||||
int result = source.readData(trackIndex, timeUs, formatHolder, sampleHolder, false);
|
||||
int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false);
|
||||
if (result == SampleSource.SAMPLE_READ) {
|
||||
parserHelper.startParseOperation();
|
||||
} else if (result == SampleSource.END_OF_STREAM) {
|
||||
@ -208,7 +208,7 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
|
||||
if (subtitle == null) {
|
||||
clearTextRenderer();
|
||||
} else {
|
||||
updateTextRenderer(timeUs);
|
||||
updateTextRenderer(positionUs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -256,8 +256,8 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void syncNextEventIndex(long timeUs) {
|
||||
nextSubtitleEventIndex = subtitle == null ? -1 : subtitle.getNextEventTimeIndex(timeUs);
|
||||
private void syncNextEventIndex(long positionUs) {
|
||||
nextSubtitleEventIndex = subtitle == null ? -1 : subtitle.getNextEventTimeIndex(positionUs);
|
||||
}
|
||||
|
||||
private long getNextEventTime() {
|
||||
@ -266,8 +266,8 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
|
||||
: (subtitle.getEventTime(nextSubtitleEventIndex));
|
||||
}
|
||||
|
||||
private void updateTextRenderer(long timeUs) {
|
||||
String text = subtitle.getText(timeUs);
|
||||
private void updateTextRenderer(long positionUs) {
|
||||
String text = subtitle.getText(positionUs);
|
||||
log("updateTextRenderer; text=: " + text);
|
||||
if (textRendererHandler != null) {
|
||||
textRendererHandler.obtainMessage(MSG_UPDATE_OVERLAY, text).sendToTarget();
|
||||
|
Loading…
x
Reference in New Issue
Block a user