From 8e58a3f5f58db1f92ebf52de76c31c8139f3d036 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Thu, 18 Jun 2015 14:24:53 +0100 Subject: [PATCH] Steps toward full multi-track support. 1. Remove requirement for TrackRenderer implementations to report current position, unless they are time sources. 2. Expose whether renderers have media to play. The immediate benefit of this is to solve the referenced GitHub issue, and also to only display the appropriate Audio/Video/Text buttons in the demo app for the media being played. This is also a natural step toward multi-track support. Github issue: #541 --- .../exoplayer/demo/PlayerActivity.java | 13 +-- .../exoplayer/demo/player/DemoPlayer.java | 25 +++--- .../ext/opus/LibopusAudioTrackRenderer.java | 36 ++++---- .../ext/vp9/LibvpxVideoTrackRenderer.java | 44 ++++------ .../android/exoplayer/DummyTrackRenderer.java | 5 -- .../google/android/exoplayer/ExoPlayer.java | 18 +++- .../android/exoplayer/ExoPlayerImpl.java | 31 +++++-- .../exoplayer/ExoPlayerImplInternal.java | 82 +++++++++++-------- .../google/android/exoplayer/MediaClock.java | 59 +------------ .../MediaCodecAudioTrackRenderer.java | 32 ++++---- .../exoplayer/MediaCodecTrackRenderer.java | 46 +++++------ .../exoplayer/StandaloneMediaClock.java | 76 +++++++++++++++++ .../android/exoplayer/TrackRenderer.java | 27 ++---- .../metadata/MetadataTrackRenderer.java | 16 +--- .../exoplayer/text/TextTrackRenderer.java | 14 +--- .../text/eia608/Eia608TrackRenderer.java | 27 ++---- 16 files changed, 285 insertions(+), 266 deletions(-) create mode 100644 library/src/main/java/com/google/android/exoplayer/StandaloneMediaClock.java diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java index 0a0d19a2ce..a27a80b783 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java @@ -268,6 +268,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, return new ExtractorRendererBuilder(this, userAgent, contentUri, new FragmentedMp4Extractor()); case TYPE_WEBM: + case TYPE_MKV: return new ExtractorRendererBuilder(this, userAgent, contentUri, new WebmExtractor()); default: throw new IllegalStateException("Unsupported type: " + contentType); @@ -377,7 +378,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, } private boolean haveTracks(int type) { - return player != null && player.getTracks(type) != null; + return player != null && player.getTrackCount(type) > 0; } public void showVideoPopup(View v) { @@ -440,8 +441,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, if (player == null) { return; } - String[] tracks = player.getTracks(trackType); - if (tracks == null) { + int trackCount = player.getTrackCount(trackType); + if (trackCount == 0) { return; } popup.setOnMenuItemClickListener(new OnMenuItemClickListener() { @@ -455,11 +456,11 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, Menu menu = popup.getMenu(); // ID_OFFSET ensures we avoid clashing with Menu.NONE (which equals 0) menu.add(MENU_GROUP_TRACKS, DemoPlayer.DISABLED_TRACK + ID_OFFSET, Menu.NONE, R.string.off); - if (tracks.length == 1 && TextUtils.isEmpty(tracks[0])) { + if (trackCount == 1 && TextUtils.isEmpty(player.getTrackName(trackType, 0))) { menu.add(MENU_GROUP_TRACKS, DemoPlayer.PRIMARY_TRACK + ID_OFFSET, Menu.NONE, R.string.on); } else { - for (int i = 0; i < tracks.length; i++) { - menu.add(MENU_GROUP_TRACKS, i + ID_OFFSET, Menu.NONE, tracks[i]); + for (int i = 0; i < trackCount; i++) { + menu.add(MENU_GROUP_TRACKS, i + ID_OFFSET, Menu.NONE, player.getTrackName(trackType, i)); } } menu.setGroupCheckable(MENU_GROUP_TRACKS, true, true); diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java index c7e639699b..6a095fec53 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java @@ -263,8 +263,12 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi pushSurface(true); } - public String[] getTracks(int type) { - return trackNames == null ? null : trackNames[type]; + public int getTrackCount(int type) { + return !player.getRendererHasMedia(type) ? 0 : trackNames[type].length; + } + + public String getTrackName(int type, int index) { + return trackNames[type][index]; } public int getSelectedTrackIndex(int type) { @@ -323,15 +327,16 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi if (multiTrackSources == null) { multiTrackSources = new MultiTrackChunkSource[RENDERER_COUNT]; } - for (int i = 0; i < RENDERER_COUNT; i++) { - if (renderers[i] == null) { + for (int rendererIndex = 0; rendererIndex < RENDERER_COUNT; rendererIndex++) { + if (renderers[rendererIndex] == null) { // Convert a null renderer to a dummy renderer. - renderers[i] = new DummyTrackRenderer(); - } else if (trackNames[i] == null) { - // We have a renderer so we must have at least one track, but the names are unknown. - // Initialize the correct number of null track names. - int trackCount = multiTrackSources[i] == null ? 1 : multiTrackSources[i].getTrackCount(); - trackNames[i] = new String[trackCount]; + renderers[rendererIndex] = new DummyTrackRenderer(); + } + if (trackNames[rendererIndex] == null) { + // Convert a null trackNames to an array of suitable length. + int trackCount = multiTrackSources[rendererIndex] != null + ? multiTrackSources[rendererIndex].getTrackCount() : 1; + trackNames[rendererIndex] = new String[trackCount]; } } // Complete preparation. diff --git a/extensions/opus/src/main/java/com/google/android/exoplayer/ext/opus/LibopusAudioTrackRenderer.java b/extensions/opus/src/main/java/com/google/android/exoplayer/ext/opus/LibopusAudioTrackRenderer.java index d88db9377c..58e2019e28 100644 --- a/extensions/opus/src/main/java/com/google/android/exoplayer/ext/opus/LibopusAudioTrackRenderer.java +++ b/extensions/opus/src/main/java/com/google/android/exoplayer/ext/opus/LibopusAudioTrackRenderer.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer.ext.opus; import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlayer; +import com.google.android.exoplayer.MediaClock; import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormatHolder; import com.google.android.exoplayer.SampleSource; @@ -38,7 +39,7 @@ import java.util.List; * * @author vigneshv@google.com (Vignesh Venkatasubramanian) */ -public class LibopusAudioTrackRenderer extends TrackRenderer { +public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClock { /** * Interface definition for a callback to be notified of {@link LibopusAudioTrackRenderer} events. @@ -87,6 +88,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer { private int trackIndex; private long currentPositionUs; + private boolean allowPositionDiscontinuity; private boolean inputStreamEnded; private boolean outputStreamEnded; private boolean sourceIsReady; @@ -119,8 +121,8 @@ public class LibopusAudioTrackRenderer extends TrackRenderer { } @Override - protected boolean isTimeSource() { - return true; + protected MediaClock getMediaClock() { + return this; } @Override @@ -237,7 +239,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer { // If we are out of sync, allow currentPositionUs to jump backwards. if ((handleBufferResult & AudioTrack.RESULT_POSITION_DISCONTINUITY) != 0) { - currentPositionUs = Long.MIN_VALUE; + allowPositionDiscontinuity = true; } // Release the buffer if it was consumed. @@ -323,26 +325,31 @@ public class LibopusAudioTrackRenderer extends TrackRenderer { } @Override - protected long getCurrentPositionUs() { - long audioTrackCurrentPositionUs = audioTrack.getCurrentPositionUs(isEnded()); - if (audioTrackCurrentPositionUs != AudioTrack.CURRENT_POSITION_NOT_SET) { - // Make sure we don't ever report time moving backwards. - currentPositionUs = Math.max(currentPositionUs, audioTrackCurrentPositionUs); + public long getPositionUs() { + long newCurrentPositionUs = audioTrack.getCurrentPositionUs(isEnded()); + if (newCurrentPositionUs != AudioTrack.CURRENT_POSITION_NOT_SET) { + currentPositionUs = allowPositionDiscontinuity ? newCurrentPositionUs + : Math.max(currentPositionUs, newCurrentPositionUs); + allowPositionDiscontinuity = false; } return currentPositionUs; } @Override protected long getBufferedPositionUs() { - long sourceBufferedPosition = source.getBufferedPositionUs(); - return sourceBufferedPosition == UNKNOWN_TIME_US || sourceBufferedPosition == END_OF_TRACK_US - ? sourceBufferedPosition : Math.max(sourceBufferedPosition, getCurrentPositionUs()); + return source.getBufferedPositionUs(); } @Override protected void seekTo(long positionUs) throws ExoPlaybackException { + source.seekToUs(positionUs); + seekToInternal(positionUs); + } + + private void seekToInternal(long positionUs) { audioTrack.reset(); currentPositionUs = positionUs; + allowPositionDiscontinuity = true; source.seekToUs(positionUs); inputStreamEnded = false; outputStreamEnded = false; @@ -352,10 +359,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer { @Override protected void onEnabled(long positionUs, boolean joining) { source.enable(trackIndex, positionUs); - sourceIsReady = false; - inputStreamEnded = false; - outputStreamEnded = false; - currentPositionUs = Long.MIN_VALUE; + seekToInternal(positionUs); } @Override diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java index 628c5e759f..5ceff05548 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java @@ -111,7 +111,6 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { private boolean outputRgb; private int trackIndex; - private long currentPositionUs; private boolean inputStreamEnded; private boolean outputStreamEnded; private boolean sourceIsReady; @@ -181,9 +180,9 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { } try { sourceIsReady = source.continueBuffering(positionUs); - checkForDiscontinuity(); + checkForDiscontinuity(positionUs); if (format == null) { - readFormat(); + readFormat(positionUs); } else { // TODO: Add support for dynamic switching between one type of surface to another. // Create the decoder. @@ -194,7 +193,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { processOutputBuffer(positionUs, elapsedRealtimeUs); // Queue input buffers. - while (feedInputBuffer()) {} + while (feedInputBuffer(positionUs)) {} } } catch (VpxDecoderException e) { notifyDecoderError(e); @@ -226,7 +225,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { long elapsedSinceStartOfLoop = SystemClock.elapsedRealtime() * 1000 - elapsedRealtimeUs; long timeToRenderUs = outputBuffer.timestampUs - positionUs - elapsedSinceStartOfLoop; - if (timeToRenderUs < -30000 || outputBuffer.timestampUs < currentPositionUs) { + if (timeToRenderUs < -30000 || outputBuffer.timestampUs < positionUs) { // Drop frame if we are too late. droppedFrameCount++; if (droppedFrameCount == maxDroppedFrameCountToNotify) { @@ -276,7 +275,6 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { } private void releaseOutputBuffer() throws VpxDecoderException { - currentPositionUs = outputBuffer.timestampUs; decoder.releaseOutputBuffer(outputBuffer); outputBuffer = null; } @@ -296,7 +294,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { surface.unlockCanvasAndPost(canvas); } - private boolean feedInputBuffer() throws IOException, VpxDecoderException { + private boolean feedInputBuffer(long positionUs) throws IOException, VpxDecoderException { if (inputStreamEnded) { return false; } @@ -308,8 +306,8 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { } } - int result = source.readData(trackIndex, currentPositionUs, formatHolder, - inputBuffer.sampleHolder, false); + int result = source.readData(trackIndex, positionUs, formatHolder, inputBuffer.sampleHolder, + false); if (result == SampleSource.NOTHING_READ) { return false; } @@ -336,11 +334,11 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { return true; } - private void checkForDiscontinuity() throws IOException { + private void checkForDiscontinuity(long positionUs) throws IOException { if (decoder == null) { return; } - int result = source.readData(trackIndex, currentPositionUs, formatHolder, null, true); + int result = source.readData(trackIndex, positionUs, formatHolder, null, true); if (result == SampleSource.DISCONTINUITY_READ) { flushDecoder(); } @@ -367,36 +365,28 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { return source.getTrackInfo(trackIndex).durationUs; } - @Override - protected long getCurrentPositionUs() { - return currentPositionUs; - } - @Override protected long getBufferedPositionUs() { - long sourceBufferedPosition = source.getBufferedPositionUs(); - return sourceBufferedPosition == UNKNOWN_TIME_US || sourceBufferedPosition == END_OF_TRACK_US - ? sourceBufferedPosition : Math.max(sourceBufferedPosition, getCurrentPositionUs()); + return source.getBufferedPositionUs(); } @Override protected void seekTo(long positionUs) throws ExoPlaybackException { - currentPositionUs = positionUs; source.seekToUs(positionUs); - inputStreamEnded = false; - outputStreamEnded = false; - renderedFirstFrame = false; - sourceIsReady = false; + seekToInternal(); } @Override protected void onEnabled(long positionUs, boolean joining) { source.enable(trackIndex, positionUs); + seekToInternal(); + } + + private void seekToInternal() { sourceIsReady = false; inputStreamEnded = false; outputStreamEnded = false; renderedFirstFrame = false; - currentPositionUs = positionUs; } @Override @@ -427,8 +417,8 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { source.disable(trackIndex); } - private void readFormat() throws IOException { - int result = source.readData(trackIndex, currentPositionUs, formatHolder, null, false); + private void readFormat(long positionUs) throws IOException { + int result = source.readData(trackIndex, positionUs, formatHolder, null, false); if (result == SampleSource.FORMAT_READ) { format = formatHolder.format; } diff --git a/library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java index 39aec48781..f926073740 100644 --- a/library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java @@ -59,9 +59,4 @@ public class DummyTrackRenderer extends TrackRenderer { throw new IllegalStateException(); } - @Override - protected long getCurrentPositionUs() { - throw new IllegalStateException(); - } - } diff --git a/library/src/main/java/com/google/android/exoplayer/ExoPlayer.java b/library/src/main/java/com/google/android/exoplayer/ExoPlayer.java index a0b544062a..e54645e095 100644 --- a/library/src/main/java/com/google/android/exoplayer/ExoPlayer.java +++ b/library/src/main/java/com/google/android/exoplayer/ExoPlayer.java @@ -261,21 +261,31 @@ public interface ExoPlayer { */ public void prepare(TrackRenderer... renderers); + /** + * Returns whether the renderer at the given index has media to play. + *

+ * Always returns false whilst the player is in the {@link #STATE_PREPARING} state. + * + * @param rendererIndex The index of the renderer. + * @return True if the renderer has media to play, false otherwise. + */ + public boolean getRendererHasMedia(int rendererIndex); + /** * Sets whether the renderer at the given index is enabled. * - * @param index The index of the renderer. + * @param rendererIndex The index of the renderer. * @param enabled Whether the renderer at the given index should be enabled. */ - public void setRendererEnabled(int index, boolean enabled); + public void setRendererEnabled(int rendererIndex, boolean enabled); /** * Whether the renderer at the given index is enabled. * - * @param index The index of the renderer. + * @param rendererIndex The index of the renderer. * @return Whether the renderer is enabled. */ - public boolean getRendererEnabled(int index); + public boolean getRendererEnabled(int rendererIndex); /** * Sets whether playback should proceed when {@link #getPlaybackState()} == {@link #STATE_READY}. diff --git a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImpl.java b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImpl.java index a378cd5652..6729bfddd8 100644 --- a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImpl.java +++ b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImpl.java @@ -21,6 +21,7 @@ import android.os.Looper; import android.os.Message; import android.util.Log; +import java.util.Arrays; import java.util.concurrent.CopyOnWriteArraySet; /** @@ -33,6 +34,7 @@ import java.util.concurrent.CopyOnWriteArraySet; private final Handler eventHandler; private final ExoPlayerImplInternal internalPlayer; private final CopyOnWriteArraySet listeners; + private final boolean[] rendererHasMediaFlags; private final boolean[] rendererEnabledFlags; private boolean playWhenReady; @@ -56,6 +58,7 @@ import java.util.concurrent.CopyOnWriteArraySet; this.playWhenReady = false; this.playbackState = STATE_IDLE; this.listeners = new CopyOnWriteArraySet<>(); + this.rendererHasMediaFlags = new boolean[rendererCount]; this.rendererEnabledFlags = new boolean[rendererCount]; for (int i = 0; i < rendererEnabledFlags.length; i++) { rendererEnabledFlags[i] = true; @@ -92,20 +95,26 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public void prepare(TrackRenderer... renderers) { + Arrays.fill(rendererHasMediaFlags, false); internalPlayer.prepare(renderers); } @Override - public void setRendererEnabled(int index, boolean enabled) { - if (rendererEnabledFlags[index] != enabled) { - rendererEnabledFlags[index] = enabled; - internalPlayer.setRendererEnabled(index, enabled); + public boolean getRendererHasMedia(int rendererIndex) { + return rendererHasMediaFlags[rendererIndex]; + } + + @Override + public void setRendererEnabled(int rendererIndex, boolean enabled) { + if (rendererEnabledFlags[rendererIndex] != enabled) { + rendererEnabledFlags[rendererIndex] = enabled; + internalPlayer.setRendererEnabled(rendererIndex, enabled); } } @Override - public boolean getRendererEnabled(int index) { - return rendererEnabledFlags[index]; + public boolean getRendererEnabled(int rendererIndex) { + return rendererEnabledFlags[rendererIndex]; } @Override @@ -182,6 +191,16 @@ import java.util.concurrent.CopyOnWriteArraySet; // Not private so it can be called from an inner class without going through a thunk method. /* package */ void handleEvent(Message msg) { switch (msg.what) { + case ExoPlayerImplInternal.MSG_PREPARED: { + boolean[] rendererHasMediaFlags = (boolean[]) msg.obj; + System.arraycopy(rendererHasMediaFlags, 0, this.rendererHasMediaFlags, 0, + rendererHasMediaFlags.length); + playbackState = msg.arg1; + for (Listener listener : listeners) { + listener.onPlayerStateChanged(playWhenReady, playbackState); + } + break; + } case ExoPlayerImplInternal.MSG_STATE_CHANGED: { playbackState = msg.arg1; for (Listener listener : listeners) { diff --git a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java index d828b8275c..5f065094f5 100644 --- a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java @@ -40,9 +40,10 @@ import java.util.List; private static final String TAG = "ExoPlayerImplInternal"; // External messages - public static final int MSG_STATE_CHANGED = 1; - public static final int MSG_SET_PLAY_WHEN_READY_ACK = 2; - public static final int MSG_ERROR = 3; + public static final int MSG_PREPARED = 1; + public static final int MSG_STATE_CHANGED = 2; + public static final int MSG_SET_PLAY_WHEN_READY_ACK = 3; + public static final int MSG_ERROR = 4; // Internal messages private static final int MSG_PREPARE = 1; @@ -62,14 +63,15 @@ import java.util.List; private final Handler handler; private final HandlerThread internalPlaybackThread; private final Handler eventHandler; - private final MediaClock mediaClock; + private final StandaloneMediaClock standaloneMediaClock; private final boolean[] rendererEnabledFlags; private final long minBufferUs; private final long minRebufferUs; private final List enabledRenderers; private TrackRenderer[] renderers; - private TrackRenderer timeSourceTrackRenderer; + private TrackRenderer rendererMediaClockSource; + private MediaClock rendererMediaClock; private boolean released; private boolean playWhenReady; @@ -98,7 +100,7 @@ import java.util.List; this.durationUs = TrackRenderer.UNKNOWN_TIME_US; this.bufferedPositionUs = TrackRenderer.UNKNOWN_TIME_US; - mediaClock = new MediaClock(); + standaloneMediaClock = new StandaloneMediaClock(); enabledRenderers = new ArrayList<>(rendererEnabledFlags.length); // Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can // not normally change to this priority" is incorrect. @@ -250,9 +252,11 @@ import java.util.List; resetInternal(); this.renderers = renderers; for (int i = 0; i < renderers.length; i++) { - if (renderers[i].isTimeSource()) { - Assertions.checkState(timeSourceTrackRenderer == null); - timeSourceTrackRenderer = renderers[i]; + MediaClock mediaClock = renderers[i].getMediaClock(); + if (mediaClock != null) { + Assertions.checkState(rendererMediaClock == null); + rendererMediaClock = mediaClock; + rendererMediaClockSource = renderers[i]; } } setState(ExoPlayer.STATE_PREPARING); @@ -280,9 +284,11 @@ import java.util.List; long durationUs = 0; boolean allRenderersEnded = true; boolean allRenderersReadyOrEnded = true; - for (int i = 0; i < renderers.length; i++) { - TrackRenderer renderer = renderers[i]; - if (renderer.getState() == TrackRenderer.STATE_PREPARED) { + boolean[] rendererHasMediaFlags = new boolean[renderers.length]; + for (int rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) { + TrackRenderer renderer = renderers[rendererIndex]; + rendererHasMediaFlags[rendererIndex] = renderer.getState() == TrackRenderer.STATE_PREPARED; + if (rendererHasMediaFlags[rendererIndex]) { if (durationUs == TrackRenderer.UNKNOWN_TIME_US) { // We've already encountered a track for which the duration is unknown, so the media // duration is unknown regardless of the duration of this track. @@ -296,7 +302,7 @@ import java.util.List; durationUs = Math.max(durationUs, trackDurationUs); } } - if (rendererEnabledFlags[i]) { + if (rendererEnabledFlags[rendererIndex]) { renderer.enable(positionUs, false); enabledRenderers.add(renderer); allRenderersEnded = allRenderersEnded && renderer.isEnded(); @@ -309,14 +315,19 @@ import java.util.List; if (allRenderersEnded && (durationUs == TrackRenderer.UNKNOWN_TIME_US || durationUs <= positionUs)) { // We don't expect this case, but handle it anyway. - setState(ExoPlayer.STATE_ENDED); + state = ExoPlayer.STATE_ENDED; } else { - setState(allRenderersReadyOrEnded ? ExoPlayer.STATE_READY : ExoPlayer.STATE_BUFFERING); - if (playWhenReady && state == ExoPlayer.STATE_READY) { - startRenderers(); - } + state = allRenderersReadyOrEnded ? ExoPlayer.STATE_READY : ExoPlayer.STATE_BUFFERING; } + // Fire an event indicating that the player has been prepared, passing the initial state and + // renderer media flags. + eventHandler.obtainMessage(MSG_PREPARED, state, 0, rendererHasMediaFlags).sendToTarget(); + + // Start the renderers if required, and schedule the first piece of work. + if (playWhenReady && state == ExoPlayer.STATE_READY) { + startRenderers(); + } handler.sendEmptyMessage(MSG_DO_SOME_WORK); } @@ -364,26 +375,26 @@ import java.util.List; private void startRenderers() throws ExoPlaybackException { rebuffering = false; - mediaClock.start(); + standaloneMediaClock.start(); for (int i = 0; i < enabledRenderers.size(); i++) { enabledRenderers.get(i).start(); } } private void stopRenderers() throws ExoPlaybackException { - mediaClock.stop(); + standaloneMediaClock.stop(); for (int i = 0; i < enabledRenderers.size(); i++) { ensureStopped(enabledRenderers.get(i)); } } private void updatePositionUs() { - if (timeSourceTrackRenderer != null && enabledRenderers.contains(timeSourceTrackRenderer) - && !timeSourceTrackRenderer.isEnded()) { - positionUs = timeSourceTrackRenderer.getCurrentPositionUs(); - mediaClock.setPositionUs(positionUs); + if (rendererMediaClock != null && enabledRenderers.contains(rendererMediaClockSource) + && !rendererMediaClockSource.isEnded()) { + positionUs = rendererMediaClock.getPositionUs(); + standaloneMediaClock.setPositionUs(positionUs); } else { - positionUs = mediaClock.getPositionUs(); + positionUs = standaloneMediaClock.getPositionUs(); } elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000; } @@ -464,8 +475,8 @@ import java.util.List; private void seekToInternal(long positionMs) throws ExoPlaybackException { rebuffering = false; positionUs = positionMs * 1000L; - mediaClock.stop(); - mediaClock.setPositionUs(positionUs); + standaloneMediaClock.stop(); + standaloneMediaClock.setPositionUs(positionUs); if (state == ExoPlayer.STATE_IDLE || state == ExoPlayer.STATE_PREPARING) { return; } @@ -496,7 +507,7 @@ import java.util.List; handler.removeMessages(MSG_DO_SOME_WORK); handler.removeMessages(MSG_INCREMENTAL_PREPARE); rebuffering = false; - mediaClock.stop(); + standaloneMediaClock.stop(); if (renderers == null) { return; } @@ -506,7 +517,8 @@ import java.util.List; release(renderer); } renderers = null; - timeSourceTrackRenderer = null; + rendererMediaClock = null; + rendererMediaClockSource = null; enabledRenderers.clear(); } @@ -555,18 +567,18 @@ import java.util.List; } } - private void setRendererEnabledInternal(int index, boolean enabled) + private void setRendererEnabledInternal(int rendererIndex, boolean enabled) throws ExoPlaybackException { - if (rendererEnabledFlags[index] == enabled) { + if (rendererEnabledFlags[rendererIndex] == enabled) { return; } - rendererEnabledFlags[index] = enabled; + rendererEnabledFlags[rendererIndex] = enabled; if (state == ExoPlayer.STATE_IDLE || state == ExoPlayer.STATE_PREPARING) { return; } - TrackRenderer renderer = renderers[index]; + TrackRenderer renderer = renderers[rendererIndex]; int rendererState = renderer.getState(); if (rendererState != TrackRenderer.STATE_PREPARED && rendererState != TrackRenderer.STATE_ENABLED && @@ -583,10 +595,10 @@ import java.util.List; } handler.sendEmptyMessage(MSG_DO_SOME_WORK); } else { - if (renderer == timeSourceTrackRenderer) { + if (renderer == rendererMediaClockSource) { // 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.setPositionUs(renderer.getCurrentPositionUs()); + standaloneMediaClock.setPositionUs(rendererMediaClock.getPositionUs()); } ensureStopped(renderer); enabledRenderers.remove(renderer); diff --git a/library/src/main/java/com/google/android/exoplayer/MediaClock.java b/library/src/main/java/com/google/android/exoplayer/MediaClock.java index c2696e3b74..6cfe50e156 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaClock.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaClock.java @@ -15,65 +15,14 @@ */ package com.google.android.exoplayer; -import android.os.SystemClock; - /** - * A simple clock for tracking the progression of media time. The clock can be started, stopped and - * its time can be set and retrieved. When started, this clock is based on - * {@link SystemClock#elapsedRealtime()}. + * Tracks the progression of media time. */ -/* package */ class MediaClock { - - private boolean started; +public interface MediaClock { /** - * The media time when the clock was last set or stopped. + * @return The current media position in microseconds. */ - private long positionUs; - - /** - * The difference between {@link SystemClock#elapsedRealtime()} and {@link #positionUs} - * when the clock was last set or started. - */ - private long deltaUs; - - /** - * Starts the clock. Does nothing if the clock is already started. - */ - public void start() { - if (!started) { - started = true; - deltaUs = elapsedRealtimeMinus(positionUs); - } - } - - /** - * Stops the clock. Does nothing if the clock is already stopped. - */ - public void stop() { - if (started) { - positionUs = elapsedRealtimeMinus(deltaUs); - started = false; - } - } - - /** - * @param timeUs The position to set in microseconds. - */ - public void setPositionUs(long timeUs) { - this.positionUs = timeUs; - deltaUs = elapsedRealtimeMinus(timeUs); - } - - /** - * @return The current position in microseconds. - */ - public long getPositionUs() { - return started ? elapsedRealtimeMinus(deltaUs) : positionUs; - } - - private long elapsedRealtimeMinus(long toSubtractUs) { - return SystemClock.elapsedRealtime() * 1000 - toSubtractUs; - } + long getPositionUs(); } diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java index 48b54db597..a28240477d 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java @@ -31,7 +31,7 @@ import java.nio.ByteBuffer; * Decodes and renders audio using {@link MediaCodec} and {@link android.media.AudioTrack}. */ @TargetApi(16) -public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer { +public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implements MediaClock { /** * Interface definition for a callback to be notified of {@link MediaCodecAudioTrackRenderer} @@ -72,6 +72,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer { private int audioSessionId; private long currentPositionUs; + private boolean allowPositionDiscontinuity; /** * @param source The upstream source from which the renderer obtains samples. @@ -151,8 +152,8 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer { } @Override - protected boolean isTimeSource() { - return true; + protected MediaClock getMediaClock() { + return this; } @Override @@ -163,7 +164,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer { @Override protected void onEnabled(long positionUs, boolean joining) { super.onEnabled(positionUs, joining); - currentPositionUs = Long.MIN_VALUE; + seekToInternal(positionUs); } @Override @@ -219,14 +220,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer { } @Override - protected long getCurrentPositionUs() { - long audioTrackCurrentPositionUs = audioTrack.getCurrentPositionUs(isEnded()); - if (audioTrackCurrentPositionUs == AudioTrack.CURRENT_POSITION_NOT_SET) { - // Use the super class position before audio playback starts. - currentPositionUs = Math.max(currentPositionUs, super.getCurrentPositionUs()); - } else { - // Make sure we don't ever report time moving backwards. - currentPositionUs = Math.max(currentPositionUs, audioTrackCurrentPositionUs); + public long getPositionUs() { + long newCurrentPositionUs = audioTrack.getCurrentPositionUs(isEnded()); + if (newCurrentPositionUs != AudioTrack.CURRENT_POSITION_NOT_SET) { + currentPositionUs = allowPositionDiscontinuity ? newCurrentPositionUs + : Math.max(currentPositionUs, newCurrentPositionUs); + allowPositionDiscontinuity = false; } return currentPositionUs; } @@ -244,9 +243,14 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer { @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. audioTrack.reset(); - currentPositionUs = Long.MIN_VALUE; + currentPositionUs = positionUs; + allowPositionDiscontinuity = true; } @Override @@ -290,7 +294,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer { // If we are out of sync, allow currentPositionUs to jump backwards. if ((handleBufferResult & AudioTrack.RESULT_POSITION_DISCONTINUITY) != 0) { - currentPositionUs = Long.MIN_VALUE; + allowPositionDiscontinuity = true; } // Release the buffer if it was consumed. diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java index 45d148355c..457320ed0d 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java @@ -212,7 +212,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { private boolean outputStreamEnded; private boolean waitingForKeys; private boolean waitingForFirstSyncFrame; - private long currentPositionUs; /** * @param source The upstream source from which the renderer obtains samples. @@ -283,11 +282,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { @Override protected void onEnabled(long positionUs, boolean joining) { source.enable(trackIndex, positionUs); - sourceState = SOURCE_STATE_NOT_READY; - inputStreamEnded = false; - outputStreamEnded = false; - waitingForKeys = false; - currentPositionUs = positionUs; + seekToInternal(); } /** @@ -457,11 +452,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { source.release(); } - @Override - protected long getCurrentPositionUs() { - return currentPositionUs; - } - @Override protected long getDurationUs() { return source.getTrackInfo(trackIndex).durationUs; @@ -469,15 +459,16 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { @Override protected long getBufferedPositionUs() { - long sourceBufferedPosition = source.getBufferedPositionUs(); - return sourceBufferedPosition == UNKNOWN_TIME_US || sourceBufferedPosition == END_OF_TRACK_US - ? sourceBufferedPosition : Math.max(sourceBufferedPosition, getCurrentPositionUs()); + return source.getBufferedPositionUs(); } @Override protected void seekTo(long positionUs) throws ExoPlaybackException { - currentPositionUs = positionUs; source.seekToUs(positionUs); + seekToInternal(); + } + + private void seekToInternal() { sourceState = SOURCE_STATE_NOT_READY; inputStreamEnded = false; outputStreamEnded = false; @@ -499,9 +490,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { sourceState = source.continueBuffering(positionUs) ? (sourceState == SOURCE_STATE_NOT_READY ? SOURCE_STATE_READY : sourceState) : SOURCE_STATE_NOT_READY; - checkForDiscontinuity(); + checkForDiscontinuity(positionUs); if (format == null) { - readFormat(); + readFormat(positionUs); } if (codec == null && shouldInitCodec()) { maybeInitCodec(); @@ -509,8 +500,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { if (codec != null) { TraceUtil.beginSection("drainAndFeed"); while (drainOutputBuffer(positionUs, elapsedRealtimeUs)) {} - if (feedInputBuffer(true)) { - while (feedInputBuffer(false)) {} + if (feedInputBuffer(positionUs, true)) { + while (feedInputBuffer(positionUs, false)) {} } TraceUtil.endSection(); } @@ -520,18 +511,18 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { } } - private void readFormat() throws IOException, ExoPlaybackException { - int result = source.readData(trackIndex, currentPositionUs, formatHolder, sampleHolder, false); + private void readFormat(long positionUs) throws IOException, ExoPlaybackException { + int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); if (result == SampleSource.FORMAT_READ) { onInputFormatChanged(formatHolder); } } - private void checkForDiscontinuity() throws IOException, ExoPlaybackException { + private void checkForDiscontinuity(long positionUs) throws IOException, ExoPlaybackException { if (codec == null) { return; } - int result = source.readData(trackIndex, currentPositionUs, formatHolder, sampleHolder, true); + int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, true); if (result == SampleSource.DISCONTINUITY_READ) { flushCodec(); } @@ -561,13 +552,16 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { } /** + * @param positionUs The current media time in microseconds, measured at the start of the + * current iteration of the rendering loop. * @param firstFeed True if this is the first call to this method from the current invocation of * {@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. */ - private boolean feedInputBuffer(boolean firstFeed) throws IOException, ExoPlaybackException { + private boolean feedInputBuffer(long positionUs, boolean firstFeed) + throws IOException, ExoPlaybackException { if (inputStreamEnded || codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM) { // The input stream has ended, or we need to re-initialize the codec but are still waiting @@ -607,7 +601,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { } codecReconfigurationState = RECONFIGURATION_STATE_QUEUE_PENDING; } - result = source.readData(trackIndex, currentPositionUs, formatHolder, sampleHolder, false); + result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); if (firstFeed && sourceState == SOURCE_STATE_READY && result == SampleSource.NOTHING_READ) { sourceState = SOURCE_STATE_READY_READ_MAY_FAIL; } @@ -857,8 +851,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { outputBufferInfo, outputIndex, decodeOnlyIndex != -1)) { if (decodeOnlyIndex != -1) { decodeOnlyPresentationTimestamps.remove(decodeOnlyIndex); - } else { - currentPositionUs = outputBufferInfo.presentationTimeUs; } outputIndex = -1; return true; diff --git a/library/src/main/java/com/google/android/exoplayer/StandaloneMediaClock.java b/library/src/main/java/com/google/android/exoplayer/StandaloneMediaClock.java new file mode 100644 index 0000000000..f4b85b0d35 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/StandaloneMediaClock.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer; + +import android.os.SystemClock; + +/** + * A standalone {@link MediaClock}. The clock can be started, stopped and its time can be set and + * retrieved. When started, this clock is based on {@link SystemClock#elapsedRealtime()}. + */ +/* package */ class StandaloneMediaClock implements MediaClock { + + private boolean started; + + /** + * The media time when the clock was last set or stopped. + */ + private long positionUs; + + /** + * The difference between {@link SystemClock#elapsedRealtime()} and {@link #positionUs} + * when the clock was last set or started. + */ + private long deltaUs; + + /** + * Starts the clock. Does nothing if the clock is already started. + */ + public void start() { + if (!started) { + started = true; + deltaUs = elapsedRealtimeMinus(positionUs); + } + } + + /** + * Stops the clock. Does nothing if the clock is already stopped. + */ + public void stop() { + if (started) { + positionUs = elapsedRealtimeMinus(deltaUs); + started = false; + } + } + + /** + * @param timeUs The position to set in microseconds. + */ + public void setPositionUs(long timeUs) { + this.positionUs = timeUs; + deltaUs = elapsedRealtimeMinus(timeUs); + } + + @Override + public long getPositionUs() { + return started ? elapsedRealtimeMinus(deltaUs) : positionUs; + } + + private long elapsedRealtimeMinus(long toSubtractUs) { + return SystemClock.elapsedRealtime() * 1000 - toSubtractUs; + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java index 3b8b4f372b..4d2f994a01 100644 --- a/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java @@ -81,18 +81,15 @@ public abstract class TrackRenderer implements ExoPlayerComponent { private int state; /** - * 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, 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. - *

- * This method may be called when the renderer is in any state. + * If the renderer advances its own playback position then this method returns a corresponding + * {@link MediaClock}. If provided, the player will use the returned {@link MediaClock} as its + * source of time during playback. A player may have at most one renderer that returns a + * {@link MediaClock} from this method. * - * @return True if the renderer should be considered a time source. False otherwise. + * @return The {@link MediaClock} tracking the playback position of the renderer, or null. */ - protected boolean isTimeSource() { - return false; + protected MediaClock getMediaClock() { + return null; } /** @@ -312,16 +309,6 @@ public abstract class TrackRenderer implements ExoPlayerComponent { */ protected abstract long getDurationUs(); - /** - * Returns the current playback position. - *

- * 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 microseconds. - */ - protected abstract long getCurrentPositionUs(); - /** * Returns an estimate of the absolute position in microseconds up to which data is buffered. *

diff --git a/library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java index c0774f15ad..3aa11e7962 100644 --- a/library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java @@ -63,7 +63,6 @@ public class MetadataTrackRenderer extends TrackRenderer implements Callback private final SampleHolder sampleHolder; private int trackIndex; - private long currentPositionUs; private boolean inputStreamEnded; private long pendingMetadataTimestamp; @@ -112,17 +111,16 @@ public class MetadataTrackRenderer extends TrackRenderer implements Callback @Override protected void onEnabled(long positionUs, boolean joining) { source.enable(trackIndex, positionUs); - seekToInternal(positionUs); + seekToInternal(); } @Override protected void seekTo(long positionUs) throws ExoPlaybackException { source.seekToUs(positionUs); - seekToInternal(positionUs); + seekToInternal(); } - private void seekToInternal(long positionUs) { - currentPositionUs = positionUs; + private void seekToInternal() { pendingMetadata = null; inputStreamEnded = false; } @@ -130,7 +128,6 @@ public class MetadataTrackRenderer extends TrackRenderer implements Callback @Override protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { - currentPositionUs = positionUs; try { source.continueBuffering(positionUs); } catch (IOException e) { @@ -152,7 +149,7 @@ public class MetadataTrackRenderer extends TrackRenderer implements Callback } } - if (pendingMetadata != null && pendingMetadataTimestamp <= currentPositionUs) { + if (pendingMetadata != null && pendingMetadataTimestamp <= positionUs) { invokeRenderer(pendingMetadata); pendingMetadata = null; } @@ -169,11 +166,6 @@ public class MetadataTrackRenderer extends TrackRenderer implements Callback return source.getTrackInfo(trackIndex).durationUs; } - @Override - protected long getCurrentPositionUs() { - return currentPositionUs; - } - @Override protected long getBufferedPositionUs() { return TrackRenderer.END_OF_TRACK_US; diff --git a/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java index 7387d53bdc..0eae41bc13 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java @@ -52,7 +52,6 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { private int parserIndex; private int trackIndex; - private long currentPositionUs; private boolean inputStreamEnded; private Subtitle subtitle; @@ -110,18 +109,17 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { parserThread = new HandlerThread("textParser"); parserThread.start(); parserHelper = new SubtitleParserHelper(parserThread.getLooper(), subtitleParsers[parserIndex]); - seekToInternal(positionUs); + seekToInternal(); } @Override protected void seekTo(long positionUs) { source.seekToUs(positionUs); - seekToInternal(positionUs); + seekToInternal(); } - private void seekToInternal(long positionUs) { + private void seekToInternal() { inputStreamEnded = false; - currentPositionUs = positionUs; subtitle = null; nextSubtitle = null; parserHelper.flush(); @@ -130,7 +128,6 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { @Override protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { - currentPositionUs = positionUs; try { source.continueBuffering(positionUs); } catch (IOException e) { @@ -205,11 +202,6 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { source.release(); } - @Override - protected long getCurrentPositionUs() { - return currentPositionUs; - } - @Override protected long getDurationUs() { return source.getTrackInfo(trackIndex).durationUs; diff --git a/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java index 947ec3a84f..343d3d0c1e 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java @@ -63,7 +63,6 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { private final TreeSet pendingCaptionLists; private int trackIndex; - private long currentPositionUs; private boolean inputStreamEnded; private int captionMode; @@ -114,17 +113,16 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { @Override protected void onEnabled(long positionUs, boolean joining) { source.enable(trackIndex, positionUs); - seekToInternal(positionUs); + seekToInternal(); } @Override protected void seekTo(long positionUs) throws ExoPlaybackException { source.seekToUs(positionUs); - seekToInternal(positionUs); + seekToInternal(); } - private void seekToInternal(long positionUs) { - currentPositionUs = positionUs; + private void seekToInternal() { inputStreamEnded = false; pendingCaptionLists.clear(); clearPendingSample(); @@ -134,9 +132,7 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { } @Override - protected void doSomeWork(long positionUs, long elapsedRealtimeUs) - throws ExoPlaybackException { - currentPositionUs = positionUs; + protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { try { source.continueBuffering(positionUs); } catch (IOException e) { @@ -144,7 +140,7 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { } if (isSamplePending()) { - maybeParsePendingSample(); + maybeParsePendingSample(positionUs); } int result = inputStreamEnded ? SampleSource.END_OF_STREAM : SampleSource.SAMPLE_READ; @@ -152,7 +148,7 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { try { result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); if (result == SampleSource.SAMPLE_READ) { - maybeParsePendingSample(); + maybeParsePendingSample(positionUs); } else if (result == SampleSource.END_OF_STREAM) { inputStreamEnded = true; } @@ -162,7 +158,7 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { } while (!pendingCaptionLists.isEmpty()) { - if (pendingCaptionLists.first().timeUs > currentPositionUs) { + if (pendingCaptionLists.first().timeUs > positionUs) { // We're too early to render any of the pending caption lists. return; } @@ -186,11 +182,6 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { return source.getTrackInfo(trackIndex).durationUs; } - @Override - protected long getCurrentPositionUs() { - return currentPositionUs; - } - @Override protected long getBufferedPositionUs() { return TrackRenderer.END_OF_TRACK_US; @@ -238,8 +229,8 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { } } - private void maybeParsePendingSample() { - if (sampleHolder.timeUs > currentPositionUs + MAX_SAMPLE_READAHEAD_US) { + private void maybeParsePendingSample(long positionUs) { + if (sampleHolder.timeUs > positionUs + MAX_SAMPLE_READAHEAD_US) { // We're too early to parse the sample. return; }