ExoPlayer V2 Refactor - Step 3

Move binding of tracks<->renderers to ExoPlayerImplInternal.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=113046018
This commit is contained in:
olly 2016-01-26 05:43:04 -08:00 committed by Oliver Woodman
parent cdae9ac5d2
commit 6cb20525cb
9 changed files with 85 additions and 193 deletions

View File

@ -17,26 +17,12 @@ package com.google.android.exoplayer;
/**
* A {@link TrackRenderer} that does nothing.
* <p>
* 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

View File

@ -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<TrackRenderer> 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();

View File

@ -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);
}
}
/**

View File

@ -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) {

View File

@ -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;
}

View File

@ -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.
*

View File

@ -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.
* <p>
* 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}.
* <p>
* 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 {
* <p>
* 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.
* <p>
* 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

View File

@ -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) {

View File

@ -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;