Allow renderers to pull tracks from multiple sample sources.

This is needed for several use cases:

- ExtractorSampleSource with option to play both embedded and out-of-band
  subtitles.
- HLS multi-audio and out-of-band-webvtt.
This commit is contained in:
Oliver Woodman 2015-08-19 16:33:01 +01:00
parent ea7caf4f91
commit ff201db981

View File

@ -26,42 +26,80 @@ import java.util.Arrays;
*/ */
public abstract class SampleSourceTrackRenderer extends TrackRenderer { public abstract class SampleSourceTrackRenderer extends TrackRenderer {
private final SampleSourceReader source; private final SampleSourceReader[] sources;
private int enabledSourceTrackIndex; private int[] handledSourceIndices;
private int[] handledSourceTrackIndices; private int[] handledSourceTrackIndices;
private SampleSourceReader enabledSource;
private int enabledSourceTrackIndex;
private long durationUs;
/** /**
* @param source The upstream source from which the renderer obtains samples. * @param sources One or more upstream sources from which the renderer can obtain samples.
*/ */
public SampleSourceTrackRenderer(SampleSource source) { public SampleSourceTrackRenderer(SampleSource... sources) {
this.source = source.register(); this.sources = new SampleSourceReader[sources.length];
for (int i = 0; i < sources.length; i++) {
this.sources[i] = sources[i].register();
}
} }
@Override @Override
protected boolean doPrepare(long positionUs) throws ExoPlaybackException { protected boolean doPrepare(long positionUs) throws ExoPlaybackException {
boolean sourcePrepared = source.prepare(positionUs); boolean allSourcesPrepared = true;
if (!sourcePrepared) { for (int i = 0; i < sources.length; i++) {
allSourcesPrepared &= sources[i].prepare(positionUs);
}
if (!allSourcesPrepared) {
return false; return false;
} }
// The sources are all prepared.
int totalSourceTrackCount = 0;
for (int i = 0; i < sources.length; i++) {
totalSourceTrackCount += sources[i].getTrackCount();
}
long durationUs = 0;
int handledTrackCount = 0; int handledTrackCount = 0;
int sourceTrackCount = source.getTrackCount(); int[] handledSourceIndices = new int[totalSourceTrackCount];
int[] trackIndices = new int[sourceTrackCount]; int[] handledTrackIndices = new int[totalSourceTrackCount];
for (int trackIndex = 0; trackIndex < sourceTrackCount; trackIndex++) { int sourceCount = sources.length;
MediaFormat format = source.getFormat(trackIndex); for (int sourceIndex = 0; sourceIndex < sourceCount; sourceIndex++) {
if (handlesTrack(format)) { SampleSourceReader source = sources[sourceIndex];
trackIndices[handledTrackCount] = trackIndex; int sourceTrackCount = source.getTrackCount();
handledTrackCount++; for (int trackIndex = 0; trackIndex < sourceTrackCount; trackIndex++) {
MediaFormat format = source.getFormat(trackIndex);
if (handlesTrack(format)) {
handledSourceIndices[handledTrackCount] = sourceIndex;
handledTrackIndices[handledTrackCount] = trackIndex;
handledTrackCount++;
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.
} else {
long trackDurationUs = format.durationUs;
if (trackDurationUs == TrackRenderer.UNKNOWN_TIME_US) {
durationUs = TrackRenderer.UNKNOWN_TIME_US;
} else if (trackDurationUs == TrackRenderer.MATCH_LONGEST_US) {
// Do nothing.
} else {
durationUs = Math.max(durationUs, trackDurationUs);
}
}
}
} }
} }
this.handledSourceTrackIndices = Arrays.copyOf(trackIndices, handledTrackCount); this.durationUs = durationUs;
this.handledSourceIndices = Arrays.copyOf(handledSourceIndices, handledTrackCount);
this.handledSourceTrackIndices = Arrays.copyOf(handledTrackIndices, handledTrackCount);
return true; return true;
} }
/** /**
* Returns whether this renderer is capable of handling the provided track. * Returns whether this renderer is capable of handling the provided track.
* *
* @param mediaFormat The track. * @param mediaFormat The format of the track.
* @return True if the renderer can handle the track, false otherwise. * @return True if the renderer can handle the track, false otherwise.
*/ */
protected abstract boolean handlesTrack(MediaFormat mediaFormat); protected abstract boolean handlesTrack(MediaFormat mediaFormat);
@ -69,27 +107,39 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
@Override @Override
protected void onEnabled(int track, long positionUs, boolean joining) protected void onEnabled(int track, long positionUs, boolean joining)
throws ExoPlaybackException { throws ExoPlaybackException {
this.enabledSourceTrackIndex = handledSourceTrackIndices[track]; enabledSource = sources[handledSourceIndices[track]];
source.enable(enabledSourceTrackIndex, positionUs); enabledSourceTrackIndex = handledSourceTrackIndices[track];
enabledSource.enable(enabledSourceTrackIndex, positionUs);
} }
@Override @Override
protected void seekTo(long positionUs) throws ExoPlaybackException { protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs); enabledSource.seekToUs(positionUs);
} }
@Override @Override
protected long getBufferedPositionUs() { protected long getBufferedPositionUs() {
return source.getBufferedPositionUs(); return enabledSource.getBufferedPositionUs();
} }
@Override @Override
protected long getDurationUs() { protected long getDurationUs() {
return source.getFormat(enabledSourceTrackIndex).durationUs; return durationUs;
} }
@Override @Override
protected void maybeThrowError() throws ExoPlaybackException { protected void maybeThrowError() throws ExoPlaybackException {
if (enabledSource != null) {
maybeThrowError(enabledSource);
} else {
int sourceCount = sources.length;
for (int i = 0; i < sourceCount; i++) {
maybeThrowError(sources[i]);
}
}
}
private void maybeThrowError(SampleSourceReader source) throws ExoPlaybackException {
try { try {
source.maybeThrowError(); source.maybeThrowError();
} catch (IOException e) { } catch (IOException e) {
@ -99,21 +149,25 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
@Override @Override
protected void onDisabled() throws ExoPlaybackException { protected void onDisabled() throws ExoPlaybackException {
source.disable(enabledSourceTrackIndex); enabledSource.disable(enabledSourceTrackIndex);
enabledSource = null;
} }
@Override @Override
protected void onReleased() throws ExoPlaybackException { protected void onReleased() throws ExoPlaybackException {
source.release(); int sourceCount = sources.length;
for (int i = 0; i < sourceCount; i++) {
sources[i].release();
}
} }
protected final boolean continueBufferingSource(long positionUs) { protected final boolean continueBufferingSource(long positionUs) {
return source.continueBuffering(enabledSourceTrackIndex, positionUs); return enabledSource.continueBuffering(enabledSourceTrackIndex, positionUs);
} }
protected final int readSource(long positionUs, MediaFormatHolder formatHolder, protected final int readSource(long positionUs, MediaFormatHolder formatHolder,
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) { SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
return source.readData(enabledSourceTrackIndex, positionUs, formatHolder, sampleHolder, return enabledSource.readData(enabledSourceTrackIndex, positionUs, formatHolder, sampleHolder,
onlyReadDiscontinuity); onlyReadDiscontinuity);
} }
@ -124,6 +178,7 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
@Override @Override
protected final MediaFormat getFormat(int track) { protected final MediaFormat getFormat(int track) {
SampleSourceReader source = sources[handledSourceIndices[track]];
return source.getFormat(handledSourceTrackIndices[track]); return source.getFormat(handledSourceTrackIndices[track]);
} }