diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java index fa1fa263f2..68bc2e9a2b 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java @@ -106,6 +106,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer { public LibvpxVideoRenderer(boolean scaleToFit, long allowedJoiningTimeMs, Handler eventHandler, VideoRendererEventListener eventListener, int maxDroppedFrameCountToNotify) { + super(C.TRACK_TYPE_VIDEO); this.scaleToFit = scaleToFit; this.allowedJoiningTimeMs = allowedJoiningTimeMs; this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify; @@ -138,11 +139,6 @@ public final class LibvpxVideoRenderer extends BaseRenderer { return isLibvpxAvailable() ? VpxDecoder.getLibvpxConfig() : null; } - @Override - public int getTrackType() { - return C.TRACK_TYPE_VIDEO; - } - @Override public int supportsFormat(Format format) { return isLibvpxAvailable() && MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType) @@ -359,7 +355,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer { } @Override - protected void onReset(long positionUs, boolean joining) { + protected void onPositionReset(long positionUs, boolean joining) { inputStreamEnded = false; outputStreamEnded = false; renderedFirstFrame = false; diff --git a/library/src/main/java/com/google/android/exoplayer2/BaseRenderer.java b/library/src/main/java/com/google/android/exoplayer2/BaseRenderer.java index 6294fa8dd2..fe1e0837c4 100644 --- a/library/src/main/java/com/google/android/exoplayer2/BaseRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/BaseRenderer.java @@ -27,6 +27,8 @@ import java.io.IOException; */ public abstract class BaseRenderer implements Renderer, RendererCapabilities { + private final int trackType; + private int index; private int state; private SampleStream stream; @@ -34,10 +36,20 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { private boolean readEndOfStream; private boolean streamIsFinal; - public BaseRenderer() { + /** + * @param trackType The track type that the renderer handles. One of the {@link C} + * {@code TRACK_TYPE_*} constants. + */ + public BaseRenderer(int trackType) { + this.trackType = trackType; readEndOfStream = true; } + @Override + public final int getTrackType() { + return trackType; + } + @Override public final RendererCapabilities getCapabilities() { return this; @@ -48,11 +60,6 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { this.index = index; } - @Override - public final int getIndex() { - return index; - } - @Override public MediaClock getMediaClock() { return null; @@ -70,19 +77,14 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { state = STATE_ENABLED; onEnabled(joining); replaceStream(formats, stream, offsetUs); - onReset(positionUs, joining); + onPositionReset(positionUs, joining); } - /** - * Called when the renderer is enabled. - *
- * The default implementation is a no-op. - * - * @param joining Whether this renderer is being enabled to join an ongoing playback. - * @throws ExoPlaybackException If an error occurs. - */ - protected void onEnabled(boolean joining) throws ExoPlaybackException { - // Do nothing. + @Override + public final void start() throws ExoPlaybackException { + Assertions.checkState(state == STATE_ENABLED); + state = STATE_STARTED; + onStarted(); } @Override @@ -95,38 +97,6 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { onStreamChanged(formats); } - /** - * Called when the renderer's stream has changed. - *
- * The default implementation is a no-op. - * - * @param formats The enabled formats. - * @throws ExoPlaybackException Thrown if an error occurs. - */ - protected void onStreamChanged(Format[] formats) throws ExoPlaybackException { - // Do nothing. - } - - @Override - public final void reset(long positionUs) throws ExoPlaybackException { - streamIsFinal = false; - onReset(positionUs, false); - } - - /** - * Invoked when a reset is encountered, and also when the renderer is enabled. - *
- * This method may be called when the renderer is in the following states: - * {@link #STATE_ENABLED}, {@link #STATE_STARTED}. - * - * @param positionUs The playback position in microseconds. - * @param joining Whether this renderer is being enabled to join an ongoing playback. - * @throws ExoPlaybackException If an error occurs handling the reset. - */ - protected void onReset(long positionUs, boolean joining) throws ExoPlaybackException { - // Do nothing. - } - @Override public final boolean hasReadStreamToEnd() { return readEndOfStream; @@ -138,21 +108,14 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { } @Override - public final void start() throws ExoPlaybackException { - Assertions.checkState(state == STATE_ENABLED); - state = STATE_STARTED; - onStarted(); + public final void maybeThrowStreamError() throws IOException { + stream.maybeThrowError(); } - /** - * Called when the renderer is started. - *
- * The default implementation is a no-op. - * - * @throws ExoPlaybackException If an error occurs. - */ - protected void onStarted() throws ExoPlaybackException { - // Do nothing. + @Override + public final void resetPosition(long positionUs) throws ExoPlaybackException { + streamIsFinal = false; + onPositionReset(positionUs, false); } @Override @@ -162,17 +125,6 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { onStopped(); } - /** - * Called when the renderer is stopped. - *
- * The default implementation is a no-op. - * - * @throws ExoPlaybackException If an error occurs. - */ - protected void onStopped() throws ExoPlaybackException { - // Do nothing. - } - @Override public final void disable() { Assertions.checkState(state == STATE_ENABLED); @@ -182,20 +134,6 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { streamIsFinal = false; } - /** - * Called when the renderer is disabled. - *
- * The default implementation is a no-op. - */ - protected void onDisabled() { - // Do nothing. - } - - @Override - public final void maybeThrowStreamError() throws IOException { - stream.maybeThrowError(); - } - // RendererCapabilities implementation. @Override @@ -210,8 +148,95 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { // Do nothing. } + // Methods to be overridden by subclasses. + + /** + * Called when the renderer is enabled. + *
+ * The default implementation is a no-op. + * + * @param joining Whether this renderer is being enabled to join an ongoing playback. + * @throws ExoPlaybackException If an error occurs. + */ + protected void onEnabled(boolean joining) throws ExoPlaybackException { + // Do nothing. + } + + /** + * Called when the renderer's stream has changed. This occurs when the renderer is enabled after + * {@link #onEnabled(boolean)} has been called, and also when the stream has been replaced whilst + * the renderer is enabled or started. + *
+ * The default implementation is a no-op. + * + * @param formats The enabled formats. + * @throws ExoPlaybackException If an error occurs. + */ + protected void onStreamChanged(Format[] formats) throws ExoPlaybackException { + // Do nothing. + } + + /** + * Invoked when the position is reset. This occurs when the renderer is enabled after + * {@link #onStreamChanged(Format[])} has been called, and also when a position discontinuity + * is encountered. + *
+ * After a position reset, the renderer's {@link SampleStream} is guaranteed to provide samples + * starting from a key frame. + *
+ * The default implementation is a no-op. + * + * @param positionUs The new playback position in microseconds. + * @param joining Whether this renderer is being enabled to join an ongoing playback. + * @throws ExoPlaybackException If an error occurs. + */ + protected void onPositionReset(long positionUs, boolean joining) + throws ExoPlaybackException { + // Do nothing. + } + + /** + * Called when the renderer is started. + *
+ * The default implementation is a no-op. + * + * @throws ExoPlaybackException If an error occurs. + */ + protected void onStarted() throws ExoPlaybackException { + // Do nothing. + } + + /** + * Called when the renderer is stopped. + *
+ * The default implementation is a no-op. + * + * @throws ExoPlaybackException If an error occurs. + */ + protected void onStopped() throws ExoPlaybackException { + // Do nothing. + } + + /** + * Called when the renderer is disabled. + *
+ * The default implementation is a no-op. + */ + protected void onDisabled() { + // Do nothing. + } + // Methods to be called by subclasses. + /** + * Returns the index of the renderer within the player. + * + * @return The index of the renderer within the player. + */ + protected final int getIndex() { + return index; + } + /** * Reads from the enabled upstream source. * diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 9f69421a0f..aaeb561ec7 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -500,7 +500,7 @@ import java.util.ArrayList; internalPositionUs = sourceOffsetUs + periodPositionUs; standaloneMediaClock.setPositionUs(internalPositionUs); for (Renderer renderer : enabledRenderers) { - renderer.reset(internalPositionUs); + renderer.resetPosition(internalPositionUs); } } diff --git a/library/src/main/java/com/google/android/exoplayer2/Renderer.java b/library/src/main/java/com/google/android/exoplayer2/Renderer.java index 0d743e876f..fc05c7b23f 100644 --- a/library/src/main/java/com/google/android/exoplayer2/Renderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/Renderer.java @@ -71,13 +71,6 @@ public interface Renderer extends ExoPlayerComponent { */ void setIndex(int index); - /** - * Returns the index of the renderer within the player. - * - * @return The index of the renderer within the player. - */ - int getIndex(); - /** * 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 @@ -97,6 +90,9 @@ public interface Renderer extends ExoPlayerComponent { /** * Enable the renderer to consume from the specified {@link SampleStream}. + *
+ * This method may be called when the renderer is in the following states: + * {@link #STATE_DISABLED}. * * @param formats The enabled formats. * @param stream The {@link SampleStream} from which the renderer should consume. @@ -110,7 +106,21 @@ public interface Renderer extends ExoPlayerComponent { long offsetUs) throws ExoPlaybackException; /** - * Sets the {@link SampleStream} from which samples will be consumed. + * Starts the renderer, meaning that calls to {@link #render(long, long)} will cause media to be + * rendered. + *
+ * This method may be called when the renderer is in the following states: + * {@link #STATE_ENABLED}. + * + * @throws ExoPlaybackException If an error occurs. + */ + void start() throws ExoPlaybackException; + + /** + * Replaces the {@link SampleStream} from which samples will be consumed. + *
+ * This method may be called when the renderer is in the following states: + * {@link #STATE_ENABLED}, {@link #STATE_STARTED}. * * @param formats The enabled formats. * @param stream The {@link SampleStream} from which the renderer should consume. @@ -121,60 +131,58 @@ public interface Renderer extends ExoPlayerComponent { void replaceStream(Format[] formats, SampleStream stream, long offsetUs) throws ExoPlaybackException; - /** - * Called when a reset is encountered. - * - * @param positionUs The playback position in microseconds. - * @throws ExoPlaybackException If an error occurs handling the reset. - */ - void reset(long positionUs) throws ExoPlaybackException; - /** * Returns whether the renderer has read the current {@link SampleStream} to the end. + *
+ * This method may be called when the renderer is in the following states: + * {@link #STATE_ENABLED}, {@link #STATE_STARTED}. */ boolean hasReadStreamToEnd(); /** * Signals to the renderer that the current {@link SampleStream} will be the final one supplied * before it is next disabled or reset. + *
+ * This method may be called when the renderer is in the following states: + * {@link #STATE_ENABLED}, {@link #STATE_STARTED}. */ void setCurrentStreamIsFinal(); - /** - * Starts the renderer, meaning that calls to {@link #render(long, long)} will cause media to be - * rendered. - * - * @throws ExoPlaybackException If an error occurs. - */ - void start() throws ExoPlaybackException; - - /** - * Stops the renderer. - * - * @throws ExoPlaybackException If an error occurs. - */ - void stop() throws ExoPlaybackException; - - /** - * Disable the renderer. - */ - void disable(); - /** * Throws an error that's preventing the renderer from reading from its {@link SampleStream}. Does * nothing if no such error exists. *
* This method may be called when the renderer is in the following states: - * {@link #STATE_ENABLED}. + * {@link #STATE_ENABLED}, {@link #STATE_STARTED}. * * @throws IOException An error that's preventing the renderer from making progress or buffering * more data. */ void maybeThrowStreamError() throws IOException; + /** + * Called when a position discontinuity is encountered. + *
+ * After a position discontinuity, the renderer's {@link SampleStream} is guaranteed to provide + * samples starting from a key frame. + *
+ * This method may be called when the renderer is in the following states: + * {@link #STATE_ENABLED}, {@link #STATE_STARTED}. + * + * @param positionUs The new playback position in microseconds. + * @throws ExoPlaybackException If an error occurs handling the reset. + */ + void resetPosition(long positionUs) throws ExoPlaybackException; + /** * Incrementally renders the {@link SampleStream}. *
+ * If the renderer is in the {@link #STATE_ENABLED} state then each call to this method will do + * work toward being ready to render the {@link SampleStream} when the renderer is started. It may + * also render the very start of the media, for example the first frame of a video stream. If the + * renderer is in the {@link #STATE_STARTED} state then calls to this method will render the + * {@link SampleStream} in sync with the specified media positions. + *
* This method should return quickly, and should not block if the renderer is unable to make * useful progress. *
@@ -218,4 +226,22 @@ public interface Renderer extends ExoPlayerComponent { */ boolean isEnded(); + /** + * Stops the renderer. + *
+ * This method may be called when the renderer is in the following states: + * {@link #STATE_STARTED}. + * + * @throws ExoPlaybackException If an error occurs. + */ + void stop() throws ExoPlaybackException; + + /** + * Disable the renderer. + *
+ * This method may be called when the renderer is in the following states:
+ * {@link #STATE_ENABLED}.
+ */
+ void disable();
+
}
diff --git a/library/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java b/library/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
index fc772c9b63..a8f69d4741 100644
--- a/library/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
+++ b/library/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
@@ -132,17 +132,12 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
Handler eventHandler, AudioRendererEventListener eventListener,
AudioCapabilities audioCapabilities, int streamType) {
- super(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
+ super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
audioTrack = new AudioTrack(audioCapabilities, streamType);
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
}
- @Override
- public int getTrackType() {
- return C.TRACK_TYPE_AUDIO;
- }
-
@Override
protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format)
throws DecoderQueryException {
@@ -264,8 +259,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
}
@Override
- protected void onReset(long positionUs, boolean joining) throws ExoPlaybackException {
- super.onReset(positionUs, joining);
+ protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
+ super.onPositionReset(positionUs, joining);
audioTrack.reset();
currentPositionUs = positionUs;
allowPositionDiscontinuity = true;
diff --git a/library/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java b/library/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java
index 20fe038f0f..70eda89bca 100644
--- a/library/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java
+++ b/library/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java
@@ -85,6 +85,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
public SimpleDecoderAudioRenderer(Handler eventHandler,
AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities,
int streamType) {
+ super(C.TRACK_TYPE_AUDIO);
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
audioTrack = new AudioTrack(audioCapabilities, streamType);
@@ -137,11 +138,6 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
decoderCounters.ensureUpdated();
}
- @Override
- public int getTrackType() {
- return C.TRACK_TYPE_AUDIO;
- }
-
protected abstract SimpleDecoder
- * Note that the first call to this method following a call to {@link #onReset(long, boolean)}
- * will always receive a new {@link ByteBuffer} to be processed.
+ * Note that the first call to this method following a call to
+ * {@link #onPositionReset(long, boolean)} will always receive a new {@link ByteBuffer} to be
+ * processed.
*
* @param positionUs The current media time in microseconds, measured at the start of the
* current iteration of the rendering loop.
diff --git a/library/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java b/library/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java
index d52a9e1a48..a9d15b3315 100644
--- a/library/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java
+++ b/library/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java
@@ -77,6 +77,7 @@ public final class MetadataRenderer