From 6cb20525cbb15d14c78f68506cd1bdc7ac7c1c75 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 26 Jan 2016 05:43:04 -0800 Subject: [PATCH] ExoPlayer V2 Refactor - Step 3 Move binding of tracks<->renderers to ExoPlayerImplInternal. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=113046018 --- .../android/exoplayer/DummyTrackRenderer.java | 18 +--- .../exoplayer/ExoPlayerImplInternal.java | 68 +++++++++----- .../exoplayer/MediaCodecTrackRenderer.java | 8 +- .../android/exoplayer/MediaCodecUtil.java | 1 + .../MediaCodecVideoTrackRenderer.java | 5 +- .../exoplayer/SampleSourceTrackRenderer.java | 60 +----------- .../android/exoplayer/TrackRenderer.java | 92 ++++--------------- .../exoplayer/text/TextTrackRenderer.java | 20 ++-- .../text/eia608/Eia608TrackRenderer.java | 6 -- 9 files changed, 85 insertions(+), 193 deletions(-) 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 a7a06f9a96..311764f0ac 100644 --- a/library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java @@ -17,26 +17,12 @@ package com.google.android.exoplayer; /** * A {@link TrackRenderer} that does nothing. - *

- * This renderer returns 0 from {@link #getTrackCount()} in order to request that it should be - * ignored. {@link IllegalStateException} is thrown from all other methods documented to indicate - * that they should not be invoked unless the renderer is prepared. */ public final class DummyTrackRenderer extends TrackRenderer { @Override - protected void doPrepare(SampleSource sampleSource) throws ExoPlaybackException { - // Do nothing. - } - - @Override - protected int getTrackCount() { - return 0; - } - - @Override - protected MediaFormat getFormat(int track) { - throw new IllegalStateException(); + protected boolean handlesTrack(MediaFormat mediaFormat) throws ExoPlaybackException { + return false; } @Override 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 4bb73065bb..2ce5f92010 100644 --- a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer; import com.google.android.exoplayer.ExoPlayer.ExoPlayerComponent; +import com.google.android.exoplayer.SampleSource.TrackStream; import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.PriorityHandlerThread; import com.google.android.exoplayer.util.TraceUtil; @@ -74,7 +75,7 @@ import java.util.concurrent.atomic.AtomicInteger; private final long minBufferUs; private final long minRebufferUs; private final List enabledRenderers; - private final MediaFormat[][] trackFormats; + private final int[][] trackIndices; private final int[] selectedTrackIndices; private final Handler handler; private final HandlerThread internalPlaybackThread; @@ -123,8 +124,8 @@ import java.util.concurrent.atomic.AtomicInteger; standaloneMediaClock = new StandaloneMediaClock(); pendingSeekCount = new AtomicInteger(); - enabledRenderers = new ArrayList<>(selectedTrackIndices.length); - trackFormats = new MediaFormat[selectedTrackIndices.length][]; + enabledRenderers = new ArrayList<>(renderers.length); + trackIndices = new int[renderers.length][]; // Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can // not normally change to this priority" is incorrect. internalPlaybackThread = new PriorityHandlerThread("ExoPlayerImplInternal:Handler", @@ -299,24 +300,41 @@ import java.util.concurrent.atomic.AtomicInteger; boolean allRenderersEnded = true; boolean allRenderersReadyOrEnded = true; + + // Establish the mapping from renderer to track index (trackIndices), and build a list of + // formats corresponding to each renderer (trackFormats). + int trackCount = source.getTrackCount(); + boolean[] trackMappedFlags = new boolean[trackCount]; + MediaFormat[][] trackFormats = new MediaFormat[renderers.length][]; for (int rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) { TrackRenderer renderer = renderers[rendererIndex]; - renderer.prepare(source); - int rendererTrackCount = renderer.getTrackCount(); - MediaFormat[] rendererTrackFormats = new MediaFormat[rendererTrackCount]; - for (int trackIndex = 0; trackIndex < rendererTrackCount; trackIndex++) { - rendererTrackFormats[trackIndex] = renderer.getFormat(trackIndex); - } - trackFormats[rendererIndex] = rendererTrackFormats; - if (rendererTrackCount > 0) { - int trackIndex = selectedTrackIndices[rendererIndex]; - if (0 <= trackIndex && trackIndex < rendererTrackFormats.length) { - renderer.enable(trackIndex, positionUs, false); - enabledRenderers.add(renderer); - allRenderersEnded = allRenderersEnded && renderer.isEnded(); - allRenderersReadyOrEnded = allRenderersReadyOrEnded && isReadyOrEnded(renderer); + int rendererTrackCount = 0; + int[] rendererTrackIndices = new int[trackCount]; + MediaFormat[] rendererTrackFormats = new MediaFormat[trackCount]; + for (int trackIndex = 0; trackIndex < trackCount; trackIndex++) { + MediaFormat trackFormat = source.getFormat(trackIndex); + if (!trackMappedFlags[trackIndex] && renderer.handlesTrack(trackFormat)) { + trackMappedFlags[trackIndex] = true; + rendererTrackIndices[rendererTrackCount] = trackIndex; + rendererTrackFormats[rendererTrackCount++] = trackFormat; } } + trackIndices[rendererIndex] = Arrays.copyOf(rendererTrackIndices, rendererTrackCount); + trackFormats[rendererIndex] = Arrays.copyOf(rendererTrackFormats, rendererTrackCount); + } + + // Enable renderers where appropriate. + for (int rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) { + TrackRenderer renderer = renderers[rendererIndex]; + int trackIndex = selectedTrackIndices[rendererIndex]; + if (0 <= trackIndex && trackIndex < trackIndices[rendererIndex].length) { + int sourceTrackIndex = trackIndices[rendererIndex][trackIndex]; + TrackStream trackStream = source.enable(sourceTrackIndex, positionUs); + renderer.enable(trackStream, positionUs, false); + enabledRenderers.add(renderer); + allRenderersEnded = allRenderersEnded && renderer.isEnded(); + allRenderersReadyOrEnded = allRenderersReadyOrEnded && isReadyOrEnded(renderer); + } } if (allRenderersEnded && (durationUs == C.UNKNOWN_TIME_US || durationUs <= positionUs)) { @@ -507,20 +525,18 @@ import java.util.concurrent.atomic.AtomicInteger; return; } for (int i = 0; i < renderers.length; i++) { - unprepare(renderers[i]); + resetRendererInternal(renderers[i]); } enabledRenderers.clear(); + Arrays.fill(trackIndices, null); source = null; } - private void unprepare(TrackRenderer renderer) { + private void resetRendererInternal(TrackRenderer renderer) { try { ensureStopped(renderer); if (renderer.getState() == TrackRenderer.STATE_ENABLED) { renderer.disable(); - renderer.unprepare(); - } else if (renderer.getState() == TrackRenderer.STATE_PREPARED) { - renderer.unprepare(); } } catch (ExoPlaybackException e) { // There's nothing we can do. @@ -562,13 +578,13 @@ import java.util.concurrent.atomic.AtomicInteger; TrackRenderer renderer = renderers[rendererIndex]; int rendererState = renderer.getState(); - if (rendererState == TrackRenderer.STATE_UNPREPARED || renderer.getTrackCount() == 0) { + if (trackIndices[rendererIndex].length == 0) { return; } boolean isEnabled = rendererState == TrackRenderer.STATE_ENABLED || rendererState == TrackRenderer.STATE_STARTED; - boolean shouldEnable = 0 <= trackIndex && trackIndex < trackFormats[rendererIndex].length; + boolean shouldEnable = 0 <= trackIndex && trackIndex < trackIndices[rendererIndex].length; if (isEnabled) { // The renderer is currently enabled. We need to disable it, so that we can either re-enable @@ -590,7 +606,9 @@ import java.util.concurrent.atomic.AtomicInteger; boolean playing = playWhenReady && state == ExoPlayer.STATE_READY; // Consider as joining if the renderer was previously disabled, but not when switching tracks. boolean joining = !isEnabled && playing; - renderer.enable(trackIndex, positionUs, joining); + int sourceTrackIndex = trackIndices[rendererIndex][trackIndex]; + TrackStream trackStream = source.enable(sourceTrackIndex, positionUs); + renderer.enable(trackStream, positionUs, joining); enabledRenderers.add(renderer); if (playing) { renderer.start(); 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 077fd4f4b7..34279f1493 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java @@ -262,8 +262,12 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer } @Override - protected final boolean handlesTrack(MediaFormat mediaFormat) throws DecoderQueryException { - return handlesTrack(mediaCodecSelector, mediaFormat); + protected final boolean handlesTrack(MediaFormat mediaFormat) throws ExoPlaybackException { + try { + return handlesTrack(mediaCodecSelector, mediaFormat); + } catch (DecoderQueryException e) { + throw new ExoPlaybackException(e); + } } /** diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecUtil.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecUtil.java index 9ff406de56..c3fb06c618 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecUtil.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecUtil.java @@ -43,6 +43,7 @@ public final class MediaCodecUtil { * Such failures are not expected in normal operation and are normally temporary (e.g. if the * mediaserver process has crashed and is yet to restart). */ + // TODO[REFACTOR]: Shouldn't implement IOException. public static class DecoderQueryException extends IOException { private DecoderQueryException(Throwable cause) { diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java index 9f12c6702a..dc976f57fa 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer; import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; +import com.google.android.exoplayer.SampleSource.TrackStream; import com.google.android.exoplayer.drm.DrmSessionManager; import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.TraceUtil; @@ -220,9 +221,9 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { } @Override - protected void onEnabled(int track, long positionUs, boolean joining) + protected void onEnabled(TrackStream trackStream, long positionUs, boolean joining) throws ExoPlaybackException { - super.onEnabled(track, positionUs, joining); + super.onEnabled(trackStream, positionUs, joining); if (joining && allowedJoiningTimeUs > 0) { joiningDeadlineUs = SystemClock.elapsedRealtime() * 1000L + allowedJoiningTimeUs; } diff --git a/library/src/main/java/com/google/android/exoplayer/SampleSourceTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/SampleSourceTrackRenderer.java index cd97fa3329..a18277d8b4 100644 --- a/library/src/main/java/com/google/android/exoplayer/SampleSourceTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/SampleSourceTrackRenderer.java @@ -15,11 +15,10 @@ */ package com.google.android.exoplayer; -import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer.SampleSource.TrackStream; +import com.google.android.exoplayer.util.Assertions; import java.io.IOException; -import java.util.Arrays; /** * Base class for {@link TrackRenderer} implementations that render samples obtained from a @@ -27,36 +26,12 @@ import java.util.Arrays; */ public abstract class SampleSourceTrackRenderer extends TrackRenderer { - private SampleSource source; private TrackStream trackStream; - private int[] handledTrackIndices; @Override - protected final void doPrepare(SampleSource source) throws ExoPlaybackException { - int sourceTrackCount = source.getTrackCount(); - int[] handledTrackIndices = new int[sourceTrackCount]; - int handledTrackCount = 0; - for (int trackIndex = 0; trackIndex < sourceTrackCount; trackIndex++) { - MediaFormat format = source.getFormat(trackIndex); - boolean handlesTrack; - try { - handlesTrack = handlesTrack(format); - } catch (DecoderQueryException e) { - throw new ExoPlaybackException(e); - } - if (handlesTrack) { - handledTrackIndices[handledTrackCount] = trackIndex; - handledTrackCount++; - } - } - this.source = source; - this.handledTrackIndices = Arrays.copyOf(handledTrackIndices, handledTrackCount); - } - - @Override - protected void onEnabled(int track, long positionUs, boolean joining) + protected void onEnabled(TrackStream trackStream, long positionUs, boolean joining) throws ExoPlaybackException { - trackStream = source.enable(handledTrackIndices[track], positionUs); + this.trackStream = Assertions.checkNotNull(trackStream); onReset(positionUs); } @@ -74,9 +49,7 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer { @Override protected void maybeThrowError() throws IOException { - if (source != null) { - trackStream.maybeThrowError(); - } + trackStream.maybeThrowError(); } @Override @@ -85,22 +58,6 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer { trackStream = null; } - @Override - protected void onUnprepared() { - source = null; - handledTrackIndices = null; - } - - @Override - protected final int getTrackCount() { - return handledTrackIndices.length; - } - - @Override - protected final MediaFormat getFormat(int track) { - return source.getFormat(handledTrackIndices[track]); - } - // Methods to be called by subclasses. /** @@ -120,15 +77,6 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer { // Abstract methods. - /** - * Returns whether this renderer is capable of handling the provided track. - * - * @param mediaFormat The format of the track. - * @return True if the renderer can handle the track, false otherwise. - * @throws DecoderQueryException Thrown if there was an error querying decoders. - */ - protected abstract boolean handlesTrack(MediaFormat mediaFormat) throws DecoderQueryException; - /** * Invoked when a reset is encountered. Also invoked when the renderer is enabled. * 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 7bd05199f1..72ebb26ee2 100644 --- a/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer; import com.google.android.exoplayer.ExoPlayer.ExoPlayerComponent; +import com.google.android.exoplayer.SampleSource.TrackStream; import com.google.android.exoplayer.util.Assertions; import java.io.IOException; @@ -34,29 +35,21 @@ import java.io.IOException; public abstract class TrackRenderer implements ExoPlayerComponent { /** - * The renderer has not yet been prepared. + * The renderer is idle. */ - protected static final int STATE_UNPREPARED = 0; - /** - * The renderer has completed necessary preparation. Preparation may include, for example, - * reading the header of a media file to determine the track format and duration. - *

- * The renderer should not hold scarce or expensive system resources (e.g. media decoders) and - * should not be actively buffering media data when in this state. - */ - protected static final int STATE_PREPARED = 1; + protected static final int STATE_IDLE = 0; /** * The renderer is enabled. It should either be ready to be started, or be actively working * towards this state (e.g. a renderer in this state will typically hold any resources that it * requires, such as media decoders, and will have buffered or be buffering any media data that * is required to start playback). */ - protected static final int STATE_ENABLED = 2; + protected static final int STATE_ENABLED = 1; /** * The renderer is started. Calls to {@link #doSomeWork(long, long)} should cause the media to be * rendered. */ - protected static final int STATE_STARTED = 3; + protected static final int STATE_STARTED = 2; private int state; @@ -82,56 +75,27 @@ public abstract class TrackRenderer implements ExoPlayerComponent { } /** - * Prepares the renderer to read from the provided {@link SampleSource}. - *

- * The {@link SampleSource} must itself be prepared before it is passed to this method. + * Returns whether this renderer is capable of handling the provided track. * - * @param sampleSource The {@link SampleSource} from which to read. + * @param mediaFormat The format of the track. + * @return True if the renderer can handle the track, false otherwise. * @throws ExoPlaybackException If an error occurs. */ - /* package */ final void prepare(SampleSource sampleSource) throws ExoPlaybackException { - Assertions.checkState(state == STATE_UNPREPARED); - Assertions.checkState(sampleSource.isPrepared()); - doPrepare(sampleSource); - state = STATE_PREPARED; - } + protected abstract boolean handlesTrack(MediaFormat mediaFormat) throws ExoPlaybackException; /** - * Called when the renderer is prepared. + * Enable the renderer to consume from the specified {@link TrackStream}. * - * @param sampleSource The {@link SampleSource} from which to read. - * @throws ExoPlaybackException If an error occurs. - */ - protected abstract void doPrepare(SampleSource sampleSource) throws ExoPlaybackException; - - /** - * Returns the number of tracks exposed by the renderer. - * - * @return The number of tracks. - */ - protected abstract int getTrackCount(); - - /** - * Returns the format of the specified track. - * - * @param track The track index. - * @return The format of the specified track. - */ - protected abstract MediaFormat getFormat(int track); - - /** - * Enable the renderer for a specified track. - * - * @param track The track for which the renderer is being enabled. + * @param trackStream The track stream from which the renderer should consume. * @param positionUs The player's current position. * @param joining Whether this renderer is being enabled to join an ongoing playback. * @throws ExoPlaybackException If an error occurs. */ - /* package */ final void enable(int track, long positionUs, boolean joining) + /* package */ final void enable(TrackStream trackStream, long positionUs, boolean joining) throws ExoPlaybackException { - Assertions.checkState(state == STATE_PREPARED); + Assertions.checkState(state == STATE_IDLE); state = STATE_ENABLED; - onEnabled(track, positionUs, joining); + onEnabled(trackStream, positionUs, joining); } /** @@ -139,12 +103,12 @@ public abstract class TrackRenderer implements ExoPlayerComponent { *

* The default implementation is a no-op. * - * @param track The track for which the renderer is being enabled. + * @param trackStream The track stream from which the renderer should consume. * @param positionUs The player's current position. * @param joining Whether this renderer is being enabled to join an ongoing playback. * @throws ExoPlaybackException If an error occurs. */ - protected void onEnabled(int track, long positionUs, boolean joining) + protected void onEnabled(TrackStream trackStream, long positionUs, boolean joining) throws ExoPlaybackException { // Do nothing. } @@ -201,7 +165,7 @@ public abstract class TrackRenderer implements ExoPlayerComponent { */ /* package */ final void disable() throws ExoPlaybackException { Assertions.checkState(state == STATE_ENABLED); - state = STATE_PREPARED; + state = STATE_IDLE; onDisabled(); } @@ -216,28 +180,6 @@ public abstract class TrackRenderer implements ExoPlayerComponent { // Do nothing. } - /** - * Unprepares the renderer. - * - * @throws ExoPlaybackException If an error occurs. - */ - /* package */ final void unprepare() throws ExoPlaybackException { - Assertions.checkState(state == STATE_PREPARED); - state = STATE_UNPREPARED; - onUnprepared(); - } - - /** - * Called when the renderer is unprepared. - *

- * The default implementation is a no-op. - * - * @throws ExoPlaybackException If an error occurs. - */ - protected void onUnprepared() throws ExoPlaybackException { - // Do nothing. - } - /** * Whether the renderer is ready for the {@link ExoPlayer} instance to transition to * {@link ExoPlayer#STATE_ENDED}. The player will make this transition as soon as {@code true} is 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 04a4de0bcb..48296d2c88 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 @@ -159,16 +159,6 @@ public final class TextTrackRenderer extends SampleSourceTrackRenderer implement return getParserIndex(mediaFormat) != -1; } - @Override - protected void onEnabled(int track, long positionUs, boolean joining) - throws ExoPlaybackException { - super.onEnabled(track, positionUs, joining); - parserIndex = getParserIndex(getFormat(track)); - parserThread = new HandlerThread("textParser"); - parserThread.start(); - parserHelper = new SubtitleParserHelper(parserThread.getLooper(), subtitleParsers[parserIndex]); - } - @Override protected void onReset(long positionUs) { inputStreamEnded = false; @@ -227,7 +217,15 @@ public final class TextTrackRenderer extends SampleSourceTrackRenderer implement sampleHolder.clearData(); int result = readSource(formatHolder, sampleHolder); if (result == TrackStream.FORMAT_READ) { - parserHelper.setFormat(formatHolder.format); + if (parserHelper == null) { + // This is the first format we've seen since the renderer was enabled. + parserIndex = getParserIndex(formatHolder.format); + parserThread = new HandlerThread("textParser"); + parserThread.start(); + parserHelper = new SubtitleParserHelper(parserThread.getLooper(), + subtitleParsers[parserIndex]); + parserHelper.setFormat(formatHolder.format); + } } else if (result == TrackStream.SAMPLE_READ) { parserHelper.startParseOperation(); } else if (result == TrackStream.END_OF_STREAM) { 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 9ec391dc4a..ae871d62c7 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 @@ -90,12 +90,6 @@ public final class Eia608TrackRenderer extends SampleSourceTrackRenderer impleme return eia608Parser.canParse(mediaFormat.mimeType); } - @Override - protected void onEnabled(int track, long positionUs, boolean joining) - throws ExoPlaybackException { - super.onEnabled(track, positionUs, joining); - } - @Override protected void onReset(long positionUs) { inputStreamEnded = false;