From dcc1ac56df877b0b90e28f4cb1bf03eb11c6c981 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 15 Jul 2016 04:16:23 -0700 Subject: [PATCH] Make Renderer an interface In V1 we received complaints that Renderer was too locked down. In particular, it wasn't possible to implement a Renderer that wrapped another Renderer, since most of the methods were package private and final (e.g. enable). This change defines Renderer as a proper interface, removing this limitation. A method-reordering-only change will follow this one, to get things into a nice consistent/sane order in each of the Renderer classes. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=127527995 --- .../android/exoplayer2/demo/EventLogger.java | 19 +- .../exoplayer2/demo/TrackSelectionHelper.java | 6 +- .../ext/vp9/LibvpxVideoRenderer.java | 13 +- .../android/exoplayer2/BaseRenderer.java | 236 ++++++++++++++++++ .../exoplayer2/ExoPlayerImplInternal.java | 6 +- .../google/android/exoplayer2/Renderer.java | 214 ++-------------- .../audio/MediaCodecAudioRenderer.java | 11 +- .../audio/SimpleDecoderAudioRenderer.java | 16 +- .../mediacodec/MediaCodecRenderer.java | 14 +- .../exoplayer2/metadata/MetadataRenderer.java | 13 +- .../android/exoplayer2/text/TextRenderer.java | 16 +- .../video/MediaCodecVideoRenderer.java | 2 +- .../playbacktests/gts/DashTest.java | 9 +- 13 files changed, 322 insertions(+), 253 deletions(-) create mode 100644 library/src/main/java/com/google/android/exoplayer2/BaseRenderer.java diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java b/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java index 5c741cb2bf..df6b9f8af7 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java @@ -18,7 +18,7 @@ package com.google.android.exoplayer2.demo; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.Renderer; +import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.drm.StreamingDrmSessionManager; @@ -130,7 +130,8 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb TrackGroup trackGroup = trackGroups.get(groupIndex); for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { String status = getTrackStatusString(false); - String formatSupport = getFormatSupportString(Renderer.FORMAT_UNSUPPORTED_TYPE); + String formatSupport = getFormatSupportString( + RendererCapabilities.FORMAT_UNSUPPORTED_TYPE); Log.d(TAG, " " + status + " Track:" + trackIndex + ", " + getFormatString(trackGroup.getFormat(trackIndex)) + ", supported=" + formatSupport); @@ -296,13 +297,13 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb private static String getFormatSupportString(int formatSupport) { switch (formatSupport) { - case Renderer.FORMAT_HANDLED: + case RendererCapabilities.FORMAT_HANDLED: return "YES"; - case Renderer.FORMAT_EXCEEDS_CAPABILITIES: + case RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES: return "NO_EXCEEDS_CAPABILITIES"; - case Renderer.FORMAT_UNSUPPORTED_SUBTYPE: + case RendererCapabilities.FORMAT_UNSUPPORTED_SUBTYPE: return "NO_UNSUPPORTED_TYPE"; - case Renderer.FORMAT_UNSUPPORTED_TYPE: + case RendererCapabilities.FORMAT_UNSUPPORTED_TYPE: return "NO"; default: return "?"; @@ -314,11 +315,11 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb return "N/A"; } switch (adaptiveSupport) { - case Renderer.ADAPTIVE_SEAMLESS: + case RendererCapabilities.ADAPTIVE_SEAMLESS: return "YES"; - case Renderer.ADAPTIVE_NOT_SEAMLESS: + case RendererCapabilities.ADAPTIVE_NOT_SEAMLESS: return "YES_NOT_SEAMLESS"; - case Renderer.ADAPTIVE_NOT_SUPPORTED: + case RendererCapabilities.ADAPTIVE_NOT_SUPPORTED: return "NO"; default: return "?"; diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java b/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java index 433d45023f..cab1ba588d 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java @@ -16,7 +16,7 @@ package com.google.android.exoplayer2.demo; import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.Renderer; +import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; @@ -81,7 +81,7 @@ import java.util.Locale; trackGroupsAdaptive = new boolean[trackGroups.length]; for (int i = 0; i < trackGroups.length; i++) { trackGroupsAdaptive[i] = trackInfo.getAdaptiveSupport(rendererIndex, i, false) - != Renderer.ADAPTIVE_NOT_SUPPORTED; + != RendererCapabilities.ADAPTIVE_NOT_SUPPORTED; } isDisabled = selector.getRendererDisabled(rendererIndex); override = selector.hasSelectionOverride(rendererIndex, trackGroups) @@ -133,7 +133,7 @@ import java.util.Locale; trackViewLayoutId, root, false); trackView.setText(buildTrackName(group.getFormat(trackIndex))); if (trackInfo.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex) - == Renderer.FORMAT_HANDLED) { + == RendererCapabilities.FORMAT_HANDLED) { haveSupportedTracks = true; trackView.setTag(Pair.create(groupIndex, trackIndex)); trackView.setOnClickListener(this); 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 5a2bfb14f4..fa1fa263f2 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 @@ -15,12 +15,12 @@ */ package com.google.android.exoplayer2.ext.vp9; +import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; -import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.util.MimeTypes; @@ -37,7 +37,7 @@ import android.view.Surface; /** * Decodes and renders video using the native VP9 decoder. */ -public final class LibvpxVideoRenderer extends Renderer { +public final class LibvpxVideoRenderer extends BaseRenderer { /** * The type of a message that can be passed to an instance of this class via @@ -150,7 +150,7 @@ public final class LibvpxVideoRenderer extends Renderer { } @Override - protected void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { + public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { if (outputStreamEnded) { return; } @@ -241,8 +241,7 @@ public final class LibvpxVideoRenderer extends Renderer { return false; } - if (getState() == Renderer.STATE_STARTED - && outputBuffer.timestampUs <= positionUs + 30000) { + if (getState() == STATE_STARTED && outputBuffer.timestampUs <= positionUs + 30000) { renderBuffer(); } return false; @@ -330,12 +329,12 @@ public final class LibvpxVideoRenderer extends Renderer { } @Override - protected boolean isEnded() { + public boolean isEnded() { return outputStreamEnded; } @Override - protected boolean isReady() { + public boolean isReady() { if (format != null && (isSourceReady() || outputBuffer != null) && renderedFirstFrame) { // Ready. If we were joining then we've now joined, so clear the joining deadline. joiningDeadlineMs = -1; diff --git a/library/src/main/java/com/google/android/exoplayer2/BaseRenderer.java b/library/src/main/java/com/google/android/exoplayer2/BaseRenderer.java new file mode 100644 index 0000000000..2335ec9901 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer2/BaseRenderer.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2016 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.exoplayer2; + +import com.google.android.exoplayer2.decoder.DecoderInputBuffer; +import com.google.android.exoplayer2.source.SampleStream; +import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.util.MediaClock; + +import java.io.IOException; + +/** + * An abstract base class suitable for most {@link Renderer} implementations. + */ +public abstract class BaseRenderer implements Renderer { + + private int index; + private int state; + private SampleStream stream; + private long streamOffsetUs; + private boolean readEndOfStream; + private boolean streamIsFinal; + + public BaseRenderer() { + readEndOfStream = true; + } + + @Override + public final void setIndex(int index) { + this.index = index; + } + + @Override + public final int getIndex() { + return index; + } + + @Override + public MediaClock getMediaClock() { + return null; + } + + @Override + public final int getState() { + return state; + } + + @Override + public final void enable(Format[] formats, SampleStream stream, long positionUs, + boolean joining, long offsetUs) throws ExoPlaybackException { + Assertions.checkState(state == STATE_DISABLED); + state = STATE_ENABLED; + onEnabled(joining); + replaceStream(formats, stream, offsetUs); + onReset(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 replaceStream(Format[] formats, SampleStream stream, long offsetUs) + throws ExoPlaybackException { + Assertions.checkState(!streamIsFinal); + this.stream = stream; + readEndOfStream = false; + streamOffsetUs = offsetUs; + 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; + } + + @Override + public final void setCurrentStreamIsFinal() { + streamIsFinal = true; + } + + @Override + public final void start() throws ExoPlaybackException { + Assertions.checkState(state == STATE_ENABLED); + state = STATE_STARTED; + onStarted(); + } + + /** + * 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 stop() throws ExoPlaybackException { + Assertions.checkState(state == STATE_STARTED); + state = STATE_ENABLED; + 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); + state = STATE_DISABLED; + onDisabled(); + stream = null; + 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 + public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { + return ADAPTIVE_NOT_SUPPORTED; + } + + // ExoPlayerComponent implementation. + + @Override + public void handleMessage(int what, Object object) throws ExoPlaybackException { + // Do nothing. + } + + // Methods to be called by subclasses. + + /** + * Reads from the enabled upstream source. + * + * @see SampleStream#readData(FormatHolder, DecoderInputBuffer) + */ + protected final int readSource(FormatHolder formatHolder, DecoderInputBuffer buffer) { + int result = stream.readData(formatHolder, buffer); + if (result == C.RESULT_BUFFER_READ) { + if (buffer.isEndOfStream()) { + readEndOfStream = true; + return streamIsFinal ? C.RESULT_BUFFER_READ : C.RESULT_NOTHING_READ; + } + buffer.timeUs += streamOffsetUs; + } + return result; + } + + /** + * Returns whether the upstream source is ready. + * + * @return True if the source is ready. False otherwise. + */ + protected final boolean isSourceReady() { + return readEndOfStream ? streamIsFinal : stream.isReady(); + } + +} 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 88d9e38c89..bf66b6275a 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -717,12 +717,12 @@ import java.util.ArrayList; for (int j = 0; j < formats.length; j++) { formats[j] = groups.get(newSelection.group).getFormat(newSelection.getTrack(j)); } - renderer.replaceSampleStream(formats, readingPeriod.sampleStreams[i], + renderer.replaceStream(formats, readingPeriod.sampleStreams[i], readingPeriod.offsetUs); } else { // The renderer will be disabled when transitioning to playing the next period. Mark // the SampleStream as final to play out any remaining data. - renderer.setCurrentSampleStreamIsFinal(); + renderer.setCurrentStreamIsFinal(); } } } @@ -731,7 +731,7 @@ import java.util.ArrayList; readingPeriod = null; // This is the last period, so signal the renderers to read the end of the stream. for (Renderer renderer : enabledRenderers) { - renderer.setCurrentSampleStreamIsFinal(); + renderer.setCurrentStreamIsFinal(); } } } 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 14cb29640e..b590875e2c 100644 --- a/library/src/main/java/com/google/android/exoplayer2/Renderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/Renderer.java @@ -16,9 +16,7 @@ package com.google.android.exoplayer2; import com.google.android.exoplayer2.ExoPlayer.ExoPlayerComponent; -import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.source.SampleStream; -import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MediaClock; import java.io.IOException; @@ -34,50 +32,35 @@ import java.io.IOException; * alt="Renderer state transitions" * border="0"/>

*/ -public abstract class Renderer implements ExoPlayerComponent, RendererCapabilities { +public interface Renderer extends ExoPlayerComponent, RendererCapabilities { /** * The renderer is disabled. */ - protected static final int STATE_DISABLED = 0; + int STATE_DISABLED = 0; /** * The renderer is enabled but not started. A renderer in this state will typically hold any * resources that it requires for rendering (e.g. media decoders). */ - protected static final int STATE_ENABLED = 1; + int STATE_ENABLED = 1; /** * The renderer is started. Calls to {@link #render(long, long)} will cause media to be rendered. */ - protected static final int STATE_STARTED = 2; - - private int index; - private int state; - private SampleStream stream; - private long streamOffsetUs; - private boolean readEndOfStream; - private boolean streamIsFinal; - - public Renderer() { - readEndOfStream = true; - } + int STATE_STARTED = 2; /** * Sets the index of this renderer within the player. * * @param index The renderer index. */ - /* package */ final void setIndex(int index) { - this.index = index; - } + void setIndex(int index); /** * Returns the index of the renderer within the player. * * @return The index of the renderer within the player. */ - protected final int getIndex() { - return index; - } + int getIndex(); /** * If the renderer advances its own playback position then this method returns a corresponding @@ -87,18 +70,14 @@ public abstract class Renderer implements ExoPlayerComponent, RendererCapabiliti * * @return The {@link MediaClock} tracking the playback position of the renderer, or null. */ - protected MediaClock getMediaClock() { - return null; - } + MediaClock getMediaClock(); /** * Returns the current state of the renderer. * * @return The current state (one of the {@code STATE_*} constants). */ - protected final int getState() { - return state; - } + int getState(); /** * Enable the renderer to consume from the specified {@link SampleStream}. @@ -111,26 +90,8 @@ public abstract class Renderer implements ExoPlayerComponent, RendererCapabiliti * before they are rendered. * @throws ExoPlaybackException If an error occurs. */ - /* package */ final void enable(Format[] formats, SampleStream stream, long positionUs, - boolean joining, long offsetUs) throws ExoPlaybackException { - Assertions.checkState(state == STATE_DISABLED); - state = STATE_ENABLED; - onEnabled(joining); - replaceSampleStream(formats, stream, offsetUs); - onReset(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. - } + void enable(Format[] formats, SampleStream stream, long positionUs, boolean joining, + long offsetUs) throws ExoPlaybackException; /** * Sets the {@link SampleStream} from which samples will be consumed. @@ -141,26 +102,8 @@ public abstract class Renderer implements ExoPlayerComponent, RendererCapabiliti * they are rendered. * @throws ExoPlaybackException If an error occurs. */ - /* package */ final void replaceSampleStream(Format[] formats, SampleStream stream, long offsetUs) - throws ExoPlaybackException { - Assertions.checkState(!streamIsFinal); - this.stream = stream; - readEndOfStream = false; - streamOffsetUs = offsetUs; - 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. - } + void replaceStream(Format[] formats, SampleStream stream, long offsetUs) + throws ExoPlaybackException; /** * Called when a reset is encountered. @@ -168,39 +111,18 @@ public abstract class Renderer implements ExoPlayerComponent, RendererCapabiliti * @param positionUs The playback position in microseconds. * @throws ExoPlaybackException If an error occurs handling the reset. */ - /* package */ 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. - } + void reset(long positionUs) throws ExoPlaybackException; /** * Returns whether the renderer has read the current {@link SampleStream} to the end. */ - /* package */ final boolean hasReadStreamToEnd() { - return readEndOfStream; - } + boolean hasReadStreamToEnd(); /** * Signals to the renderer that the current {@link SampleStream} will be the final one supplied * before it is next disabled or reset. */ - /* package */ final void setCurrentSampleStreamIsFinal() { - streamIsFinal = true; - } + void setCurrentStreamIsFinal(); /** * Starts the renderer, meaning that calls to {@link #render(long, long)} will cause media to be @@ -208,66 +130,19 @@ public abstract class Renderer implements ExoPlayerComponent, RendererCapabiliti * * @throws ExoPlaybackException If an error occurs. */ - /* package */ final void start() throws ExoPlaybackException { - Assertions.checkState(state == STATE_ENABLED); - state = STATE_STARTED; - onStarted(); - } - - /** - * 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. - } + void start() throws ExoPlaybackException; /** * Stops the renderer. * * @throws ExoPlaybackException If an error occurs. */ - /* package */ final void stop() throws ExoPlaybackException { - Assertions.checkState(state == STATE_STARTED); - state = STATE_ENABLED; - 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. - } + void stop() throws ExoPlaybackException; /** * Disable the renderer. */ - /* package */ final void disable() { - Assertions.checkState(state == STATE_ENABLED); - state = STATE_DISABLED; - onDisabled(); - stream = null; - streamIsFinal = false; - } - - /** - * Called when the renderer is disabled. - *

- * The default implementation is a no-op. - */ - protected void onDisabled() { - // Do nothing. - } - - // Methods to be called by subclasses. + void disable(); /** * Throws an error that's preventing the renderer from reading from its {@link SampleStream}. Does @@ -279,37 +154,7 @@ public abstract class Renderer implements ExoPlayerComponent, RendererCapabiliti * @throws IOException An error that's preventing the renderer from making progress or buffering * more data. */ - protected final void maybeThrowStreamError() throws IOException { - stream.maybeThrowError(); - } - - /** - * Reads from the enabled upstream source. - * - * @see SampleStream#readData(FormatHolder, DecoderInputBuffer) - */ - protected final int readSource(FormatHolder formatHolder, DecoderInputBuffer buffer) { - int result = stream.readData(formatHolder, buffer); - if (result == C.RESULT_BUFFER_READ) { - if (buffer.isEndOfStream()) { - readEndOfStream = true; - return streamIsFinal ? C.RESULT_BUFFER_READ : C.RESULT_NOTHING_READ; - } - buffer.timeUs += streamOffsetUs; - } - return result; - } - - /** - * Returns whether the upstream source is ready. - * - * @return True if the source is ready. False otherwise. - */ - protected final boolean isSourceReady() { - return readEndOfStream ? streamIsFinal : stream.isReady(); - } - - // Abstract methods. + void maybeThrowStreamError() throws IOException; /** * Incrementally renders the {@link SampleStream}. @@ -326,8 +171,7 @@ public abstract class Renderer implements ExoPlayerComponent, RendererCapabiliti * measured at the start of the current iteration of the rendering loop. * @throws ExoPlaybackException If an error occurs. */ - protected abstract void render(long positionUs, long elapsedRealtimeUs) - throws ExoPlaybackException; + void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException; /** * Whether the renderer is able to immediately render media from the current position. @@ -344,7 +188,7 @@ public abstract class Renderer implements ExoPlayerComponent, RendererCapabiliti * * @return True if the renderer is ready to render media. False otherwise. */ - protected abstract boolean isReady(); + boolean isReady(); /** * Whether the renderer is ready for the {@link ExoPlayer} instance to transition to @@ -356,20 +200,6 @@ public abstract class Renderer implements ExoPlayerComponent, RendererCapabiliti * * @return Whether the renderer is ready for the player to transition to the ended state. */ - protected abstract boolean isEnded(); - - // RendererCapabilities implementation - - @Override - public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { - return ADAPTIVE_NOT_SUPPORTED; - } - - // ExoPlayerComponent implementation. - - @Override - public void handleMessage(int what, Object object) throws ExoPlaybackException { - // Do nothing. - } + boolean isEnded(); } 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 35b3086fcd..fc772c9b63 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 @@ -18,7 +18,6 @@ package com.google.android.exoplayer2.audio; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.audio.AudioRendererEventListener.EventDispatcher; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; @@ -210,7 +209,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } @Override - protected MediaClock getMediaClock() { + public MediaClock getMediaClock() { return this; } @@ -300,12 +299,12 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } @Override - protected boolean isEnded() { + public boolean isEnded() { return super.isEnded() && !audioTrack.hasPendingData(); } @Override - protected boolean isReady() { + public boolean isReady() { return audioTrack.hasPendingData() || super.isReady(); } @@ -351,14 +350,14 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } catch (AudioTrack.InitializationException e) { throw ExoPlaybackException.createForRenderer(e, getIndex()); } - if (getState() == Renderer.STATE_STARTED) { + if (getState() == STATE_STARTED) { audioTrack.play(); } } else { // Check for AudioTrack underrun. boolean audioTrackHadData = audioTrackHasData; audioTrackHasData = audioTrack.hasPendingData(); - if (audioTrackHadData && !audioTrackHasData && getState() == Renderer.STATE_STARTED) { + if (audioTrackHadData && !audioTrackHasData && getState() == STATE_STARTED) { long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs; long bufferSizeUs = audioTrack.getBufferSizeUs(); long bufferSizeMs = bufferSizeUs == C.UNSET_TIME_US ? -1 : bufferSizeUs / 1000; 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 0f871cd1c3..20fe038f0f 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 @@ -15,11 +15,11 @@ */ package com.google.android.exoplayer2.audio; +import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; -import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.audio.AudioRendererEventListener.EventDispatcher; import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; @@ -37,7 +37,7 @@ import android.os.SystemClock; /** * Decodes and renders audio using a {@link SimpleDecoder}. */ -public abstract class SimpleDecoderAudioRenderer extends Renderer implements MediaClock { +public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements MediaClock { private final EventDispatcher eventDispatcher; private final FormatHolder formatHolder; @@ -92,12 +92,12 @@ public abstract class SimpleDecoderAudioRenderer extends Renderer implements Med } @Override - protected MediaClock getMediaClock() { + public MediaClock getMediaClock() { return this; } @Override - protected void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { + public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { if (outputStreamEnded) { return; } @@ -192,14 +192,14 @@ public abstract class SimpleDecoderAudioRenderer extends Renderer implements Med onAudioSessionId(audioSessionId); } audioTrackHasData = false; - if (getState() == Renderer.STATE_STARTED) { + if (getState() == STATE_STARTED) { audioTrack.play(); } } else { // Check for AudioTrack underrun. boolean audioTrackHadData = audioTrackHasData; audioTrackHasData = audioTrack.hasPendingData(); - if (audioTrackHadData && !audioTrackHasData && getState() == Renderer.STATE_STARTED) { + if (audioTrackHadData && !audioTrackHasData && getState() == STATE_STARTED) { long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs; long bufferSizeUs = audioTrack.getBufferSizeUs(); long bufferSizeMs = bufferSizeUs == C.UNSET_TIME_US ? -1 : bufferSizeUs / 1000; @@ -270,12 +270,12 @@ public abstract class SimpleDecoderAudioRenderer extends Renderer implements Med } @Override - protected boolean isEnded() { + public boolean isEnded() { return outputStreamEnded && !audioTrack.hasPendingData(); } @Override - protected boolean isReady() { + public boolean isReady() { return audioTrack.hasPendingData() || (inputFormat != null && (isSourceReady() || outputBuffer != null)); } diff --git a/library/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index 44a6cfa2c0..4043214e97 100644 --- a/library/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -15,11 +15,11 @@ */ package com.google.android.exoplayer2.mediacodec; +import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; -import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.drm.DrmSession; @@ -46,10 +46,10 @@ import java.util.ArrayList; import java.util.List; /** - * An abstract {@link Renderer} that uses {@link MediaCodec} to decode samples for rendering. + * An abstract renderer that uses {@link MediaCodec} to decode samples for rendering. */ @TargetApi(16) -public abstract class MediaCodecRenderer extends Renderer { +public abstract class MediaCodecRenderer extends BaseRenderer { /** * Thrown when a failure occurs instantiating a decoder. @@ -361,7 +361,7 @@ public abstract class MediaCodecRenderer extends Renderer { throwDecoderInitError(new DecoderInitializationException(format, e, drmSessionRequiresSecureDecoder, codecName)); } - codecHotswapDeadlineMs = getState() == Renderer.STATE_STARTED + codecHotswapDeadlineMs = getState() == STATE_STARTED ? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS) : -1; inputIndex = -1; outputIndex = -1; @@ -452,7 +452,7 @@ public abstract class MediaCodecRenderer extends Renderer { } @Override - protected void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { + public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { if (format == null) { readFormat(); } @@ -780,12 +780,12 @@ public abstract class MediaCodecRenderer extends Renderer { } @Override - protected boolean isEnded() { + public boolean isEnded() { return outputStreamEnded; } @Override - protected boolean isReady() { + public boolean isReady() { return format != null && !waitingForKeys && (isSourceReady() || outputIndex >= 0 || (SystemClock.elapsedRealtime() < codecHotswapDeadlineMs)); } 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 a35e9931a5..d52a9e1a48 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 @@ -20,6 +20,7 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.Renderer; +import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.util.Assertions; @@ -35,7 +36,7 @@ import java.nio.ByteBuffer; * * @param The type of the metadata. */ -public final class MetadataRenderer extends Renderer implements Callback { +public final class MetadataRenderer extends BaseRenderer implements Callback { /** * An output for the renderer. @@ -90,8 +91,8 @@ public final class MetadataRenderer extends Renderer implements Callback { @Override public int supportsFormat(Format format) { - return metadataDecoder.canDecode(format.sampleMimeType) ? Renderer.FORMAT_HANDLED - : Renderer.FORMAT_UNSUPPORTED_TYPE; + return metadataDecoder.canDecode(format.sampleMimeType) ? FORMAT_HANDLED + : FORMAT_UNSUPPORTED_TYPE; } @Override @@ -101,7 +102,7 @@ public final class MetadataRenderer extends Renderer implements Callback { } @Override - protected void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { + public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { if (!inputStreamEnded && pendingMetadata == null) { buffer.clear(); int result = readSource(formatHolder, buffer); @@ -134,12 +135,12 @@ public final class MetadataRenderer extends Renderer implements Callback { } @Override - protected boolean isEnded() { + public boolean isEnded() { return inputStreamEnded; } @Override - protected boolean isReady() { + public boolean isReady() { return true; } diff --git a/library/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java b/library/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java index 04a7190adc..02bd34e875 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java @@ -15,11 +15,11 @@ */ package com.google.android.exoplayer2.text; +import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; -import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MimeTypes; @@ -33,14 +33,14 @@ import java.util.Collections; import java.util.List; /** - * A {@link Renderer} for subtitles. + * A renderer for subtitles. *

* Text is parsed from sample data using {@link SubtitleDecoder} instances obtained from a * {@link SubtitleDecoderFactory}. The actual rendering of each line of text is delegated to a * {@link Output}. */ @TargetApi(16) -public final class TextRenderer extends Renderer implements Callback { +public final class TextRenderer extends BaseRenderer implements Callback { /** * An output for the renderer. @@ -106,7 +106,7 @@ public final class TextRenderer extends Renderer implements Callback { @Override public int supportsFormat(Format format) { - return decoderFactory.supportsFormat(format) ? Renderer.FORMAT_HANDLED + return decoderFactory.supportsFormat(format) ? FORMAT_HANDLED : (MimeTypes.isText(format.sampleMimeType) ? FORMAT_UNSUPPORTED_SUBTYPE : FORMAT_UNSUPPORTED_TYPE); } @@ -137,7 +137,7 @@ public final class TextRenderer extends Renderer implements Callback { } @Override - protected void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { + public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { if (outputStreamEnded) { return; } @@ -151,7 +151,7 @@ public final class TextRenderer extends Renderer implements Callback { } } - if (getState() != Renderer.STATE_STARTED) { + if (getState() != STATE_STARTED) { return; } @@ -237,12 +237,12 @@ public final class TextRenderer extends Renderer implements Callback { } @Override - protected boolean isEnded() { + public boolean isEnded() { return outputStreamEnded; } @Override - protected boolean isReady() { + public boolean isReady() { // Don't block playback whilst subtitles are loading. // Note: To change this behavior, it will be necessary to consider [Internal: b/12949941]. return true; diff --git a/library/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 70cbf8ef86..d5209db98e 100644 --- a/library/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -244,7 +244,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { } @Override - protected boolean isReady() { + public boolean isReady() { if (renderedFirstFrame && super.isReady()) { // Ready. If we were joining then we've now joined, so clear the joining deadline. joiningDeadlineMs = -1; diff --git a/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/gts/DashTest.java b/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/gts/DashTest.java index c48ad9ac9f..14d06aec73 100644 --- a/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/gts/DashTest.java +++ b/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/gts/DashTest.java @@ -18,7 +18,6 @@ package com.google.android.exoplayer2.playbacktests.gts; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; -import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; @@ -535,8 +534,7 @@ public final class DashTest extends ActivityInstrumentationTestCase2