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;