mirror of
https://github.com/samsonjs/media.git
synced 2026-04-05 11:15:46 +00:00
Internal plumbing for multi-track support.
- Generalize rendererEnabledFlags to be selected track indices through ExoPlayerImpl/ExoPlayerImplInternal. - Selecting an out-of-bound track index (e.g. -1) is equivalent to disabling a renderer prior to the generalization. - A prepared TrackRenderer that exposes 0 tracks is equivalent to a TrackRenderer in the STATE_IGNORE state prior to the generalization. Issue #514.
This commit is contained in:
parent
13f4a3e3bd
commit
6085d185fa
13 changed files with 222 additions and 126 deletions
|
|
@ -131,8 +131,9 @@ public final class LibopusAudioTrackRenderer extends SampleSourceTrackRenderer
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
super.onEnabled(positionUs, joining);
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
seekToInternal(positionUs);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -349,8 +349,9 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
super.onEnabled(positionUs, joining);
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,15 +18,25 @@ package com.google.android.exoplayer;
|
|||
/**
|
||||
* A {@link TrackRenderer} that does nothing.
|
||||
* <p>
|
||||
* This renderer returns {@link TrackRenderer#STATE_IGNORE} from {@link #doPrepare(long)} in order
|
||||
* to request that it should be ignored. {@link IllegalStateException} is thrown from all methods
|
||||
* that are documented to indicate that they should not be invoked unless the renderer is prepared.
|
||||
* 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 int doPrepare(long positionUs) {
|
||||
return STATE_IGNORE;
|
||||
protected boolean doPrepare(long positionUs) throws ExoPlaybackException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getTrackCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TrackInfo getTrackInfo(int track) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
private final Handler eventHandler;
|
||||
private final ExoPlayerImplInternal internalPlayer;
|
||||
private final CopyOnWriteArraySet<Listener> listeners;
|
||||
private final boolean[] rendererHasMediaFlags;
|
||||
private final boolean[] rendererEnabledFlags;
|
||||
private final TrackInfo[][] trackInfos;
|
||||
private final int[] selectedTrackIndices;
|
||||
|
||||
private boolean playWhenReady;
|
||||
private int playbackState;
|
||||
|
|
@ -58,18 +58,15 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
this.playWhenReady = false;
|
||||
this.playbackState = STATE_IDLE;
|
||||
this.listeners = new CopyOnWriteArraySet<>();
|
||||
this.rendererHasMediaFlags = new boolean[rendererCount];
|
||||
this.rendererEnabledFlags = new boolean[rendererCount];
|
||||
for (int i = 0; i < rendererEnabledFlags.length; i++) {
|
||||
rendererEnabledFlags[i] = true;
|
||||
}
|
||||
this.trackInfos = new TrackInfo[rendererCount][];
|
||||
this.selectedTrackIndices = new int[rendererCount];
|
||||
eventHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
ExoPlayerImpl.this.handleEvent(msg);
|
||||
}
|
||||
};
|
||||
internalPlayer = new ExoPlayerImplInternal(eventHandler, playWhenReady, rendererEnabledFlags,
|
||||
internalPlayer = new ExoPlayerImplInternal(eventHandler, playWhenReady, selectedTrackIndices,
|
||||
minBufferMs, minRebufferMs);
|
||||
}
|
||||
|
||||
|
|
@ -95,26 +92,49 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
|
||||
@Override
|
||||
public void prepare(TrackRenderer... renderers) {
|
||||
Arrays.fill(rendererHasMediaFlags, false);
|
||||
Arrays.fill(trackInfos, null);
|
||||
internalPlayer.prepare(renderers);
|
||||
}
|
||||
|
||||
@Override
|
||||
// TODO: Deprecate in ExoPlayer.
|
||||
public boolean getRendererHasMedia(int rendererIndex) {
|
||||
return rendererHasMediaFlags[rendererIndex];
|
||||
return getRendererTrackCount(rendererIndex) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
// TODO: Deprecate in ExoPlayer.
|
||||
public void setRendererEnabled(int rendererIndex, boolean enabled) {
|
||||
if (rendererEnabledFlags[rendererIndex] != enabled) {
|
||||
rendererEnabledFlags[rendererIndex] = enabled;
|
||||
internalPlayer.setRendererEnabled(rendererIndex, enabled);
|
||||
setRendererSelectedTrack(rendererIndex, enabled ? 0 : -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
// TODO: Deprecate in ExoPlayer.
|
||||
public boolean getRendererEnabled(int rendererIndex) {
|
||||
return getRendererSelectedTrack(rendererIndex) == 0;
|
||||
}
|
||||
|
||||
// TODO: Expose in ExoPlayer.
|
||||
public int getRendererTrackCount(int rendererIndex) {
|
||||
return trackInfos[rendererIndex] != null ? trackInfos[rendererIndex].length : 0;
|
||||
}
|
||||
|
||||
// TODO: Expose in ExoPlayer.
|
||||
public TrackInfo getRendererTrackInfo(int rendererIndex, int trackIndex) {
|
||||
return trackInfos[rendererIndex][trackIndex];
|
||||
}
|
||||
|
||||
// TODO: Expose in ExoPlayer.
|
||||
public void setRendererSelectedTrack(int rendererIndex, int trackIndex) {
|
||||
if (selectedTrackIndices[rendererIndex] != trackIndex) {
|
||||
selectedTrackIndices[rendererIndex] = trackIndex;
|
||||
internalPlayer.setRendererSelectedTrack(rendererIndex, trackIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRendererEnabled(int rendererIndex) {
|
||||
return rendererEnabledFlags[rendererIndex];
|
||||
// TODO: Expose in ExoPlayer.
|
||||
public int getRendererSelectedTrack(int rendererIndex) {
|
||||
return selectedTrackIndices[rendererIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -192,9 +212,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
/* package */ void handleEvent(Message msg) {
|
||||
switch (msg.what) {
|
||||
case ExoPlayerImplInternal.MSG_PREPARED: {
|
||||
boolean[] rendererHasMediaFlags = (boolean[]) msg.obj;
|
||||
System.arraycopy(rendererHasMediaFlags, 0, this.rendererHasMediaFlags, 0,
|
||||
rendererHasMediaFlags.length);
|
||||
TrackInfo[][] trackInfos = (TrackInfo[][]) msg.obj;
|
||||
System.arraycopy(trackInfos, 0, this.trackInfos, 0, trackInfos.length);
|
||||
playbackState = msg.arg1;
|
||||
for (Listener listener : listeners) {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import android.util.Log;
|
|||
import android.util.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -54,7 +55,7 @@ import java.util.List;
|
|||
private static final int MSG_RELEASE = 5;
|
||||
private static final int MSG_SEEK_TO = 6;
|
||||
private static final int MSG_DO_SOME_WORK = 7;
|
||||
private static final int MSG_SET_RENDERER_ENABLED = 8;
|
||||
private static final int MSG_SET_RENDERER_SELECTED_TRACK = 8;
|
||||
private static final int MSG_CUSTOM = 9;
|
||||
|
||||
private static final int PREPARE_INTERVAL_MS = 10;
|
||||
|
|
@ -65,11 +66,12 @@ import java.util.List;
|
|||
private final HandlerThread internalPlaybackThread;
|
||||
private final Handler eventHandler;
|
||||
private final StandaloneMediaClock standaloneMediaClock;
|
||||
private final boolean[] rendererEnabledFlags;
|
||||
private final List<TrackRenderer> enabledRenderers;
|
||||
private final TrackInfo[][] trackInfos;
|
||||
private final int[] selectedTrackIndices;
|
||||
private final long minBufferUs;
|
||||
private final long minRebufferUs;
|
||||
|
||||
private final List<TrackRenderer> enabledRenderers;
|
||||
private TrackRenderer[] renderers;
|
||||
private TrackRenderer rendererMediaClockSource;
|
||||
private MediaClock rendererMediaClock;
|
||||
|
|
@ -87,22 +89,19 @@ import java.util.List;
|
|||
private volatile long bufferedPositionUs;
|
||||
|
||||
public ExoPlayerImplInternal(Handler eventHandler, boolean playWhenReady,
|
||||
boolean[] rendererEnabledFlags, int minBufferMs, int minRebufferMs) {
|
||||
int[] selectedTrackIndices, int minBufferMs, int minRebufferMs) {
|
||||
this.eventHandler = eventHandler;
|
||||
this.playWhenReady = playWhenReady;
|
||||
this.rendererEnabledFlags = new boolean[rendererEnabledFlags.length];
|
||||
this.minBufferUs = minBufferMs * 1000L;
|
||||
this.minRebufferUs = minRebufferMs * 1000L;
|
||||
for (int i = 0; i < rendererEnabledFlags.length; i++) {
|
||||
this.rendererEnabledFlags[i] = rendererEnabledFlags[i];
|
||||
}
|
||||
|
||||
this.selectedTrackIndices = Arrays.copyOf(selectedTrackIndices, selectedTrackIndices.length);
|
||||
this.state = ExoPlayer.STATE_IDLE;
|
||||
this.durationUs = TrackRenderer.UNKNOWN_TIME_US;
|
||||
this.bufferedPositionUs = TrackRenderer.UNKNOWN_TIME_US;
|
||||
|
||||
standaloneMediaClock = new StandaloneMediaClock();
|
||||
enabledRenderers = new ArrayList<>(rendererEnabledFlags.length);
|
||||
enabledRenderers = new ArrayList<>(selectedTrackIndices.length);
|
||||
trackInfos = new TrackInfo[selectedTrackIndices.length][];
|
||||
// Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can
|
||||
// not normally change to this priority" is incorrect.
|
||||
internalPlaybackThread = new PriorityHandlerThread(getClass().getSimpleName() + ":Handler",
|
||||
|
|
@ -146,8 +145,9 @@ import java.util.List;
|
|||
handler.sendEmptyMessage(MSG_STOP);
|
||||
}
|
||||
|
||||
public void setRendererEnabled(int index, boolean enabled) {
|
||||
handler.obtainMessage(MSG_SET_RENDERER_ENABLED, index, enabled ? 1 : 0).sendToTarget();
|
||||
public void setRendererSelectedTrack(int rendererIndex, int trackIndex) {
|
||||
handler.obtainMessage(MSG_SET_RENDERER_SELECTED_TRACK, rendererIndex, trackIndex)
|
||||
.sendToTarget();
|
||||
}
|
||||
|
||||
public void sendMessage(ExoPlayerComponent target, int messageType, Object message) {
|
||||
|
|
@ -223,8 +223,8 @@ import java.util.List;
|
|||
sendMessageInternal(msg.arg1, msg.obj);
|
||||
return true;
|
||||
}
|
||||
case MSG_SET_RENDERER_ENABLED: {
|
||||
setRendererEnabledInternal(msg.arg1, msg.arg2 != 0);
|
||||
case MSG_SET_RENDERER_SELECTED_TRACK: {
|
||||
setRendererSelectedTrackInternal(msg.arg1, msg.arg2);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
|
|
@ -253,6 +253,7 @@ import java.util.List;
|
|||
private void prepareInternal(TrackRenderer[] renderers) throws ExoPlaybackException {
|
||||
resetInternal();
|
||||
this.renderers = renderers;
|
||||
Arrays.fill(trackInfos, null);
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
MediaClock mediaClock = renderers[i].getMediaClock();
|
||||
if (mediaClock != null) {
|
||||
|
|
@ -288,11 +289,15 @@ import java.util.List;
|
|||
long durationUs = 0;
|
||||
boolean allRenderersEnded = true;
|
||||
boolean allRenderersReadyOrEnded = true;
|
||||
boolean[] rendererHasMediaFlags = new boolean[renderers.length];
|
||||
for (int rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) {
|
||||
TrackRenderer renderer = renderers[rendererIndex];
|
||||
rendererHasMediaFlags[rendererIndex] = renderer.getState() == TrackRenderer.STATE_PREPARED;
|
||||
if (rendererHasMediaFlags[rendererIndex]) {
|
||||
int rendererTrackCount = renderer.getTrackCount();
|
||||
TrackInfo[] rendererTrackInfos = new TrackInfo[rendererTrackCount];
|
||||
for (int trackIndex = 0; trackIndex < rendererTrackCount; trackIndex++) {
|
||||
rendererTrackInfos[trackIndex] = renderer.getTrackInfo(trackIndex);
|
||||
}
|
||||
trackInfos[rendererIndex] = rendererTrackInfos;
|
||||
if (rendererTrackCount > 0) {
|
||||
if (durationUs == TrackRenderer.UNKNOWN_TIME_US) {
|
||||
// We've already encountered a track for which the duration is unknown, so the media
|
||||
// duration is unknown regardless of the duration of this track.
|
||||
|
|
@ -306,8 +311,9 @@ import java.util.List;
|
|||
durationUs = Math.max(durationUs, trackDurationUs);
|
||||
}
|
||||
}
|
||||
if (rendererEnabledFlags[rendererIndex]) {
|
||||
renderer.enable(positionUs, false);
|
||||
int trackIndex = selectedTrackIndices[rendererIndex];
|
||||
if (0 <= trackIndex && trackIndex < rendererTrackInfos.length) {
|
||||
renderer.enable(trackIndex, positionUs, false);
|
||||
enabledRenderers.add(renderer);
|
||||
allRenderersEnded = allRenderersEnded && renderer.isEnded();
|
||||
allRenderersReadyOrEnded = allRenderersReadyOrEnded && rendererReadyOrEnded(renderer);
|
||||
|
|
@ -325,8 +331,8 @@ import java.util.List;
|
|||
}
|
||||
|
||||
// Fire an event indicating that the player has been prepared, passing the initial state and
|
||||
// renderer media flags.
|
||||
eventHandler.obtainMessage(MSG_PREPARED, state, 0, rendererHasMediaFlags).sendToTarget();
|
||||
// renderer track information.
|
||||
eventHandler.obtainMessage(MSG_PREPARED, state, 0, trackInfos).sendToTarget();
|
||||
|
||||
// Start the renderers if required, and schedule the first piece of work.
|
||||
if (playWhenReady && state == ExoPlayer.STATE_READY) {
|
||||
|
|
@ -583,43 +589,54 @@ import java.util.List;
|
|||
}
|
||||
}
|
||||
|
||||
private void setRendererEnabledInternal(int rendererIndex, boolean enabled)
|
||||
private void setRendererSelectedTrackInternal(int rendererIndex, int trackIndex)
|
||||
throws ExoPlaybackException {
|
||||
if (rendererEnabledFlags[rendererIndex] == enabled) {
|
||||
if (selectedTrackIndices[rendererIndex] == trackIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
rendererEnabledFlags[rendererIndex] = enabled;
|
||||
selectedTrackIndices[rendererIndex] = trackIndex;
|
||||
if (state == ExoPlayer.STATE_IDLE || state == ExoPlayer.STATE_PREPARING) {
|
||||
return;
|
||||
}
|
||||
|
||||
TrackRenderer renderer = renderers[rendererIndex];
|
||||
int rendererState = renderer.getState();
|
||||
if (rendererState != TrackRenderer.STATE_PREPARED &&
|
||||
rendererState != TrackRenderer.STATE_ENABLED &&
|
||||
rendererState != TrackRenderer.STATE_STARTED) {
|
||||
if (rendererState == TrackRenderer.STATE_UNPREPARED
|
||||
|| rendererState == TrackRenderer.STATE_RELEASED
|
||||
|| renderer.getTrackCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
boolean playing = playWhenReady && state == ExoPlayer.STATE_READY;
|
||||
renderer.enable(positionUs, playing);
|
||||
enabledRenderers.add(renderer);
|
||||
if (playing) {
|
||||
renderer.start();
|
||||
}
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
} else {
|
||||
if (renderer == rendererMediaClockSource) {
|
||||
boolean isEnabled = rendererState == TrackRenderer.STATE_ENABLED
|
||||
|| rendererState == TrackRenderer.STATE_STARTED;
|
||||
boolean shouldEnable = 0 <= trackIndex && trackIndex < trackInfos[rendererIndex].length;
|
||||
|
||||
if (isEnabled) {
|
||||
// The renderer is currently enabled. We need to disable it, so that we can either re-enable
|
||||
// it with the newly selected track (if shouldEnable is true) or because we want to leave it
|
||||
// disabled (if shouldEnable is false).
|
||||
if (!shouldEnable && renderer == rendererMediaClockSource) {
|
||||
// We've been using rendererMediaClockSource to advance the current position, but it's being
|
||||
// disabled. Sync standaloneMediaClock so that it can take over timing responsibilities.
|
||||
// disabled and won't be re-enabled. Sync standaloneMediaClock so that it can take over
|
||||
// timing responsibilities.
|
||||
standaloneMediaClock.setPositionUs(rendererMediaClock.getPositionUs());
|
||||
}
|
||||
ensureStopped(renderer);
|
||||
enabledRenderers.remove(renderer);
|
||||
renderer.disable();
|
||||
}
|
||||
|
||||
if (shouldEnable) {
|
||||
// Re-enable the renderer with the newly selected track.
|
||||
boolean playing = playWhenReady && state == ExoPlayer.STATE_READY;
|
||||
renderer.enable(trackIndex, positionUs, playing);
|
||||
enabledRenderers.add(renderer);
|
||||
if (playing) {
|
||||
renderer.start();
|
||||
}
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureStopped(TrackRenderer renderer) throws ExoPlaybackException {
|
||||
|
|
|
|||
|
|
@ -162,8 +162,9 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
super.onEnabled(positionUs, joining);
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
seekToInternal(positionUs);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -241,8 +241,9 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
super.onEnabled(positionUs, joining);
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -261,8 +261,9 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
super.onEnabled(positionUs, joining);
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
renderedFirstFrame = false;
|
||||
if (joining && allowedJoiningTimeUs > 0) {
|
||||
joiningDeadlineUs = SystemClock.elapsedRealtime() * 1000L + allowedJoiningTimeUs;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer;
|
|||
import com.google.android.exoplayer.SampleSource.SampleSourceReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Base class for {@link TrackRenderer} implementations that render samples obtained from a
|
||||
|
|
@ -27,7 +28,9 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
|
|||
|
||||
private final SampleSourceReader source;
|
||||
|
||||
private int trackIndex;
|
||||
private int enabledSourceTrackIndex;
|
||||
private int[] handledSourceTrackIndices;
|
||||
private TrackInfo[] trackInfos;
|
||||
|
||||
/**
|
||||
* @param source The upstream source from which the renderer obtains samples.
|
||||
|
|
@ -37,21 +40,26 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected int doPrepare(long positionUs) throws ExoPlaybackException {
|
||||
protected boolean doPrepare(long positionUs) throws ExoPlaybackException {
|
||||
boolean sourcePrepared = source.prepare(positionUs);
|
||||
if (!sourcePrepared) {
|
||||
return TrackRenderer.STATE_UNPREPARED;
|
||||
return false;
|
||||
}
|
||||
int trackCount = source.getTrackCount();
|
||||
for (int i = 0; i < trackCount; i++) {
|
||||
TrackInfo trackInfo = source.getTrackInfo(i);
|
||||
int handledTrackCount = 0;
|
||||
int sourceTrackCount = source.getTrackCount();
|
||||
int[] trackIndices = new int[sourceTrackCount];
|
||||
TrackInfo[] trackInfos = new TrackInfo[sourceTrackCount];
|
||||
for (int trackIndex = 0; trackIndex < sourceTrackCount; trackIndex++) {
|
||||
TrackInfo trackInfo = source.getTrackInfo(trackIndex);
|
||||
if (handlesTrack(trackInfo)) {
|
||||
trackIndex = i;
|
||||
onTrackSelected(trackInfo);
|
||||
return TrackRenderer.STATE_PREPARED;
|
||||
trackIndices[handledTrackCount] = trackIndex;
|
||||
trackInfos[handledTrackCount] = trackInfo;
|
||||
handledTrackCount++;
|
||||
}
|
||||
}
|
||||
return TrackRenderer.STATE_IGNORE;
|
||||
this.handledSourceTrackIndices = Arrays.copyOf(trackIndices, handledTrackCount);
|
||||
this.trackInfos = Arrays.copyOf(trackInfos, handledTrackCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -72,8 +80,10 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
source.enable(trackIndex, positionUs);
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
this.enabledSourceTrackIndex = handledSourceTrackIndices[track];
|
||||
source.enable(enabledSourceTrackIndex, positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -88,7 +98,7 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
|
|||
|
||||
@Override
|
||||
protected long getDurationUs() {
|
||||
return source.getTrackInfo(trackIndex).durationUs;
|
||||
return source.getTrackInfo(enabledSourceTrackIndex).durationUs;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -102,7 +112,7 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
|
|||
|
||||
@Override
|
||||
protected void onDisabled() throws ExoPlaybackException {
|
||||
source.disable(trackIndex);
|
||||
source.disable(enabledSourceTrackIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -111,13 +121,23 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
|
|||
}
|
||||
|
||||
protected final boolean continueBufferingSource(long positionUs) {
|
||||
return source.continueBuffering(trackIndex, positionUs);
|
||||
return source.continueBuffering(enabledSourceTrackIndex, positionUs);
|
||||
}
|
||||
|
||||
protected final int readSource(long positionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
|
||||
return source.readData(trackIndex, positionUs, formatHolder, sampleHolder,
|
||||
return source.readData(enabledSourceTrackIndex, positionUs, formatHolder, sampleHolder,
|
||||
onlyReadDiscontinuity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final int getTrackCount() {
|
||||
return trackInfos.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final TrackInfo getTrackInfo(int track) {
|
||||
return trackInfos[track];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,14 +31,24 @@ import com.google.android.exoplayer.util.Assertions;
|
|||
*/
|
||||
public abstract class TrackRenderer implements ExoPlayerComponent {
|
||||
|
||||
/**
|
||||
* Represents an unknown time or duration. Equal to {@link C#UNKNOWN_TIME_US}.
|
||||
*/
|
||||
public static final long UNKNOWN_TIME_US = C.UNKNOWN_TIME_US; // -1
|
||||
/**
|
||||
* Represents a time or duration that should match the duration of the longest track whose
|
||||
* duration is known. Equal to {@link C#MATCH_LONGEST_US}.
|
||||
*/
|
||||
public static final long MATCH_LONGEST_US = C.MATCH_LONGEST_US; // -2
|
||||
/**
|
||||
* Represents the time of the end of the track.
|
||||
*/
|
||||
public static final long END_OF_TRACK_US = -3;
|
||||
|
||||
/**
|
||||
* The renderer has been released and should not be used.
|
||||
*/
|
||||
protected static final int STATE_RELEASED = -2;
|
||||
/**
|
||||
* The renderer should be ignored by the player.
|
||||
*/
|
||||
protected static final int STATE_IGNORE = -1;
|
||||
protected static final int STATE_RELEASED = -1;
|
||||
/**
|
||||
* The renderer has not yet been prepared.
|
||||
*/
|
||||
|
|
@ -64,20 +74,6 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
|
|||
*/
|
||||
protected static final int STATE_STARTED = 3;
|
||||
|
||||
/**
|
||||
* Represents an unknown time or duration. Equal to {@link C#UNKNOWN_TIME_US}.
|
||||
*/
|
||||
public static final long UNKNOWN_TIME_US = C.UNKNOWN_TIME_US; // -1
|
||||
/**
|
||||
* Represents a time or duration that should match the duration of the longest track whose
|
||||
* duration is known. Equal to {@link C#MATCH_LONGEST_US}.
|
||||
*/
|
||||
public static final long MATCH_LONGEST_US = C.MATCH_LONGEST_US; // -2
|
||||
/**
|
||||
* Represents the time of the end of the track.
|
||||
*/
|
||||
public static final long END_OF_TRACK_US = -3;
|
||||
|
||||
private int state;
|
||||
|
||||
/**
|
||||
|
|
@ -111,41 +107,65 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
|
|||
*/
|
||||
/* package */ final int prepare(long positionUs) throws ExoPlaybackException {
|
||||
Assertions.checkState(state == STATE_UNPREPARED);
|
||||
state = doPrepare(positionUs);
|
||||
Assertions.checkState(state == STATE_UNPREPARED ||
|
||||
state == STATE_PREPARED ||
|
||||
state == STATE_IGNORE);
|
||||
state = doPrepare(positionUs) ? STATE_PREPARED : STATE_UNPREPARED;
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to make progress when the renderer is in the {@link #STATE_UNPREPARED} state. This
|
||||
* method will be called repeatedly until a value other than {@link #STATE_UNPREPARED} is
|
||||
* returned.
|
||||
* method will be called repeatedly until {@code true} is returned.
|
||||
* <p>
|
||||
* This method should return quickly, and should not block if the renderer is currently unable to
|
||||
* make any useful progress.
|
||||
*
|
||||
* @param positionUs The player's current playback position.
|
||||
* @return The new state of the renderer. One of {@link #STATE_UNPREPARED},
|
||||
* {@link #STATE_PREPARED} and {@link #STATE_IGNORE}.
|
||||
* @return True if the renderer is now prepared. False otherwise.
|
||||
* @throws ExoPlaybackException If an error occurs.
|
||||
*/
|
||||
protected abstract int doPrepare(long positionUs) throws ExoPlaybackException;
|
||||
protected abstract boolean doPrepare(long positionUs) throws ExoPlaybackException;
|
||||
|
||||
/**
|
||||
* Enable the renderer.
|
||||
* Returns the number of tracks exposed by the renderer.
|
||||
* <p>
|
||||
* This method may be called when the renderer is in the following states:
|
||||
* {@link #STATE_PREPARED}, {@link #STATE_ENABLED}, {@link #STATE_STARTED}
|
||||
*
|
||||
* @return The number of tracks.
|
||||
*/
|
||||
// TODO: This method should be abstract. This implementation is provided as an interim step only.
|
||||
protected int getTrackCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about the specified track.
|
||||
* <p>
|
||||
* This method may be called when the renderer is in the following states:
|
||||
* {@link #STATE_PREPARED}, {@link #STATE_ENABLED}, {@link #STATE_STARTED}
|
||||
*
|
||||
* @param track The track index.
|
||||
* @return Information about the specified track.
|
||||
*/
|
||||
// TODO: This method should be abstract. This implementation is provided as an interim step only.
|
||||
protected TrackInfo getTrackInfo(int track) {
|
||||
return new TrackInfo("application/octet-stream", getDurationUs());
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the renderer for a specified track.
|
||||
*
|
||||
* @param track The track for which the renderer is being enabled.
|
||||
* @param positionUs The player's current position.
|
||||
* @param joining Whether this renderer is being enabled to join an ongoing playback. If true
|
||||
* then {@link #start} must be called immediately after this method returns (unless a
|
||||
* {@link ExoPlaybackException} is thrown).
|
||||
* @throws ExoPlaybackException If an error occurs.
|
||||
*/
|
||||
/* package */ final void enable(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
/* package */ final void enable(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
Assertions.checkState(state == STATE_PREPARED);
|
||||
state = STATE_ENABLED;
|
||||
onEnabled(positionUs, joining);
|
||||
onEnabled(track, positionUs, joining);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -153,13 +173,15 @@ 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 positionUs The player's current position.
|
||||
* @param joining Whether this renderer is being enabled to join an ongoing playback. If true
|
||||
* then {@link #onStarted} is guaranteed to be called immediately after this method returns
|
||||
* (unless a {@link ExoPlaybackException} is thrown).
|
||||
* @throws ExoPlaybackException If an error occurs.
|
||||
*/
|
||||
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,8 +93,9 @@ public final class MetadataTrackRenderer<T> extends SampleSourceTrackRenderer im
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
super.onEnabled(positionUs, joining);
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -171,8 +171,9 @@ public final class TextTrackRenderer extends SampleSourceTrackRenderer implement
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
super.onEnabled(positionUs, joining);
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
parserThread = new HandlerThread("textParser");
|
||||
parserThread.start();
|
||||
parserHelper = new SubtitleParserHelper(parserThread.getLooper(), subtitleParsers[parserIndex]);
|
||||
|
|
|
|||
|
|
@ -94,8 +94,9 @@ public final class Eia608TrackRenderer extends SampleSourceTrackRenderer impleme
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
super.onEnabled(positionUs, joining);
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
seekToInternal();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue