Clean up chunked Sample/Chunk sources.

- Remove need for SampleSources to ref ChunkSources.
- Correctly propagate errors loading manifests through
  ChunkSource components.
- Fix some misc warnings.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=123732918
This commit is contained in:
olly 2016-06-01 03:07:37 -07:00 committed by Oliver Woodman
parent 7aae5805b8
commit 7f70ee911b
8 changed files with 108 additions and 95 deletions

View File

@ -239,12 +239,12 @@ import java.util.concurrent.atomic.AtomicInteger;
return false; return false;
} }
} catch (ExoPlaybackException e) { } catch (ExoPlaybackException e) {
Log.e(TAG, "Internal track renderer error.", e); Log.e(TAG, "Renderer error.", e);
eventHandler.obtainMessage(MSG_ERROR, e).sendToTarget(); eventHandler.obtainMessage(MSG_ERROR, e).sendToTarget();
stopInternal(); stopInternal();
return true; return true;
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Source track renderer error.", e); Log.e(TAG, "Source error.", e);
eventHandler.obtainMessage(MSG_ERROR, ExoPlaybackException.createForSource(e)).sendToTarget(); eventHandler.obtainMessage(MSG_ERROR, ExoPlaybackException.createForSource(e)).sendToTarget();
stopInternal(); stopInternal();
return true; return true;

View File

@ -84,4 +84,11 @@ public interface ChunkSource {
*/ */
boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e); boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e);
/**
* Releases the source.
* <p>
* This method should be called when the source is no longer required.
*/
void release();
} }

View File

@ -37,10 +37,11 @@ import java.util.List;
/** /**
* A {@link TrackStream} that loads media in {@link Chunk}s, obtained from a {@link ChunkSource}. * A {@link TrackStream} that loads media in {@link Chunk}s, obtained from a {@link ChunkSource}.
*/ */
public class ChunkTrackStream implements TrackStream, Loader.Callback<Chunk> { public class ChunkTrackStream<T extends ChunkSource> implements TrackStream,
Loader.Callback<Chunk> {
private final Loader loader; private final Loader loader;
private final ChunkSource chunkSource; private final T chunkSource;
private final int minLoadableRetryCount; private final int minLoadableRetryCount;
private final LinkedList<BaseMediaChunk> mediaChunks; private final LinkedList<BaseMediaChunk> mediaChunks;
private final List<BaseMediaChunk> readOnlyMediaChunks; private final List<BaseMediaChunk> readOnlyMediaChunks;
@ -72,7 +73,7 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback<Chunk> {
* @param minLoadableRetryCount The minimum number of times that the source should retry a load * @param minLoadableRetryCount The minimum number of times that the source should retry a load
* before propagating an error. * before propagating an error.
*/ */
public ChunkTrackStream(ChunkSource chunkSource, LoadControl loadControl, public ChunkTrackStream(T chunkSource, LoadControl loadControl,
int bufferSizeContribution, long positionUs, Handler eventHandler, int bufferSizeContribution, long positionUs, Handler eventHandler,
ChunkTrackStreamEventListener eventListener, int eventSourceId, int minLoadableRetryCount) { ChunkTrackStreamEventListener eventListener, int eventSourceId, int minLoadableRetryCount) {
this.chunkSource = chunkSource; this.chunkSource = chunkSource;
@ -109,6 +110,15 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback<Chunk> {
} }
} }
/**
* Returns the {@link ChunkSource} used by this stream.
*
* @return The {@link ChunkSource}.
*/
public T getChunkSource() {
return chunkSource;
}
/** /**
* Returns an estimate of the position up to which data is buffered. * Returns an estimate of the position up to which data is buffered.
* *
@ -160,6 +170,7 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback<Chunk> {
* This method should be called when the stream is no longer required. * This method should be called when the stream is no longer required.
*/ */
public void release() { public void release() {
chunkSource.release();
loadControl.unregister(this); loadControl.unregister(this);
if (loader.isLoading()) { if (loader.isLoading()) {
loader.cancelLoading(); loader.cancelLoading();
@ -181,7 +192,9 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback<Chunk> {
@Override @Override
public void maybeThrowError() throws IOException { public void maybeThrowError() throws IOException {
loader.maybeThrowError(); loader.maybeThrowError();
chunkSource.maybeThrowError(); if (!loader.isLoading()) {
chunkSource.maybeThrowError();
}
} }
@Override @Override

View File

@ -42,6 +42,7 @@ import com.google.android.exoplayer.extractor.mkv.MatroskaExtractor;
import com.google.android.exoplayer.extractor.mp4.FragmentedMp4Extractor; import com.google.android.exoplayer.extractor.mp4.FragmentedMp4Extractor;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.Loader;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
@ -56,6 +57,7 @@ import java.util.List;
*/ */
public class DashChunkSource implements ChunkSource { public class DashChunkSource implements ChunkSource {
private final Loader manifestLoader;
private final int adaptationSetIndex; private final int adaptationSetIndex;
private final TrackGroup trackGroup; private final TrackGroup trackGroup;
private final RepresentationHolder[] representationHolders; private final RepresentationHolder[] representationHolders;
@ -72,6 +74,7 @@ public class DashChunkSource implements ChunkSource {
private IOException fatalError; private IOException fatalError;
/** /**
* @param manifestLoader The {@link Loader} being used to load manifests.
* @param manifest The initial manifest. * @param manifest The initial manifest.
* @param adaptationSetIndex The index of the adaptation set in the manifest. * @param adaptationSetIndex The index of the adaptation set in the manifest.
* @param trackGroup The track group corresponding to the adaptation set. * @param trackGroup The track group corresponding to the adaptation set.
@ -82,9 +85,10 @@ public class DashChunkSource implements ChunkSource {
* server-side unix time and {@link SystemClock#elapsedRealtime()} in milliseconds, specified * server-side unix time and {@link SystemClock#elapsedRealtime()} in milliseconds, specified
* as the server's unix time minus the local elapsed time. It unknown, set to 0. * as the server's unix time minus the local elapsed time. It unknown, set to 0.
*/ */
public DashChunkSource(MediaPresentationDescription manifest, int adaptationSetIndex, public DashChunkSource(Loader manifestLoader, MediaPresentationDescription manifest,
TrackGroup trackGroup, int[] tracks, DataSource dataSource, int adaptationSetIndex, TrackGroup trackGroup, int[] tracks, DataSource dataSource,
FormatEvaluator adaptiveFormatEvaluator, long elapsedRealtimeOffsetMs) { FormatEvaluator adaptiveFormatEvaluator, long elapsedRealtimeOffsetMs) {
this.manifestLoader = manifestLoader;
this.manifest = manifest; this.manifest = manifest;
this.adaptationSetIndex = adaptationSetIndex; this.adaptationSetIndex = adaptationSetIndex;
this.trackGroup = trackGroup; this.trackGroup = trackGroup;
@ -131,18 +135,14 @@ public class DashChunkSource implements ChunkSource {
} }
} }
public void release() {
if (adaptiveFormatEvaluator != null) {
adaptiveFormatEvaluator.disable();
}
}
// ChunkSource implementation. // ChunkSource implementation.
@Override @Override
public void maybeThrowError() throws IOException { public void maybeThrowError() throws IOException {
if (fatalError != null) { if (fatalError != null) {
throw fatalError; throw fatalError;
} else {
manifestLoader.maybeThrowError();
} }
} }
@ -273,6 +273,13 @@ public class DashChunkSource implements ChunkSource {
return false; return false;
} }
@Override
public void release() {
if (adaptiveFormatEvaluator != null) {
adaptiveFormatEvaluator.disable();
}
}
// Private methods. // Private methods.
private long getNowUnixTimeUs() { private long getNowUnixTimeUs() {

View File

@ -47,7 +47,6 @@ import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
@ -93,9 +92,7 @@ public final class DashSampleSource implements SampleSource {
private int[] trackGroupAdaptationSetIndices; private int[] trackGroupAdaptationSetIndices;
private boolean pendingReset; private boolean pendingReset;
private long lastSeekPositionUs; private long lastSeekPositionUs;
private ChunkTrackStream<DashChunkSource>[] trackStreams;
private DashChunkSource[] chunkSources;
private ChunkTrackStream[] trackStreams;
public DashSampleSource(Uri manifestUri, DataSourceFactory dataSourceFactory, public DashSampleSource(Uri manifestUri, DataSourceFactory dataSourceFactory,
BandwidthMeter bandwidthMeter, Handler eventHandler, BandwidthMeter bandwidthMeter, Handler eventHandler,
@ -110,8 +107,7 @@ public final class DashSampleSource implements SampleSource {
dataSource = dataSourceFactory.createDataSource(); dataSource = dataSourceFactory.createDataSource();
manifestParser = new MediaPresentationDescriptionParser(); manifestParser = new MediaPresentationDescriptionParser();
manifestCallback = new ManifestCallback(); manifestCallback = new ManifestCallback();
chunkSources = new DashChunkSource[0]; trackStreams = newTrackStreamArray(0);
trackStreams = new ChunkTrackStream[0];
} }
@Override @Override
@ -140,18 +136,15 @@ public final class DashSampleSource implements SampleSource {
public TrackStream[] selectTracks(List<TrackStream> oldStreams, public TrackStream[] selectTracks(List<TrackStream> oldStreams,
List<TrackSelection> newSelections, long positionUs) { List<TrackSelection> newSelections, long positionUs) {
int newEnabledSourceCount = trackStreams.length + newSelections.size() - oldStreams.size(); int newEnabledSourceCount = trackStreams.length + newSelections.size() - oldStreams.size();
DashChunkSource[] newChunkSources = new DashChunkSource[newEnabledSourceCount]; ChunkTrackStream<DashChunkSource>[] newTrackStreams =
ChunkTrackStream[] newTrackStreams = new ChunkTrackStream[newEnabledSourceCount]; newTrackStreamArray(newEnabledSourceCount);
int newEnabledSourceIndex = 0; int newEnabledSourceIndex = 0;
// Iterate over currently enabled streams, either releasing them or adding them to the new list. // Iterate over currently enabled streams, either releasing them or adding them to the new list.
for (int i = 0; i < trackStreams.length; i++) { for (ChunkTrackStream<DashChunkSource> trackStream : trackStreams) {
ChunkTrackStream trackStream = trackStreams[i];
if (oldStreams.contains(trackStream)) { if (oldStreams.contains(trackStream)) {
chunkSources[i].release();
trackStream.release(); trackStream.release();
} else { } else {
newChunkSources[newEnabledSourceIndex] = chunkSources[i];
newTrackStreams[newEnabledSourceIndex++] = trackStream; newTrackStreams[newEnabledSourceIndex++] = trackStream;
} }
} }
@ -159,14 +152,11 @@ public final class DashSampleSource implements SampleSource {
// Instantiate and return new streams. // Instantiate and return new streams.
TrackStream[] streamsToReturn = new TrackStream[newSelections.size()]; TrackStream[] streamsToReturn = new TrackStream[newSelections.size()];
for (int i = 0; i < newSelections.size(); i++) { for (int i = 0; i < newSelections.size(); i++) {
Pair<DashChunkSource, ChunkTrackStream> trackComponents = newTrackStreams[newEnabledSourceIndex] = buildTrackStream(newSelections.get(i), positionUs);
buildTrackStream(newSelections.get(i), positionUs); streamsToReturn[i] = newTrackStreams[newEnabledSourceIndex];
newChunkSources[newEnabledSourceIndex] = trackComponents.first; newEnabledSourceIndex++;
newTrackStreams[newEnabledSourceIndex++] = trackComponents.second;
streamsToReturn[i] = trackComponents.second;
} }
chunkSources = newChunkSources;
trackStreams = newTrackStreams; trackStreams = newTrackStreams;
return streamsToReturn; return streamsToReturn;
} }
@ -187,7 +177,7 @@ public final class DashSampleSource implements SampleSource {
startLoadingManifest(); startLoadingManifest();
} }
} }
for (ChunkTrackStream trackStream : trackStreams) { for (ChunkTrackStream<DashChunkSource> trackStream : trackStreams) {
trackStream.continueBuffering(positionUs); trackStream.continueBuffering(positionUs);
} }
} }
@ -196,7 +186,7 @@ public final class DashSampleSource implements SampleSource {
public long readReset() { public long readReset() {
if (pendingReset) { if (pendingReset) {
pendingReset = false; pendingReset = false;
for (ChunkTrackStream trackStream : trackStreams) { for (ChunkTrackStream<DashChunkSource> trackStream : trackStreams) {
trackStream.setReadingEnabled(true); trackStream.setReadingEnabled(true);
} }
return lastSeekPositionUs; return lastSeekPositionUs;
@ -207,7 +197,7 @@ public final class DashSampleSource implements SampleSource {
@Override @Override
public long getBufferedPositionUs() { public long getBufferedPositionUs() {
long bufferedPositionUs = Long.MAX_VALUE; long bufferedPositionUs = Long.MAX_VALUE;
for (ChunkTrackStream trackStream : trackStreams) { for (ChunkTrackStream<DashChunkSource> trackStream : trackStreams) {
long rendererBufferedPositionUs = trackStream.getBufferedPositionUs(); long rendererBufferedPositionUs = trackStream.getBufferedPositionUs();
if (rendererBufferedPositionUs != C.END_OF_SOURCE_US) { if (rendererBufferedPositionUs != C.END_OF_SOURCE_US) {
bufferedPositionUs = Math.min(bufferedPositionUs, rendererBufferedPositionUs); bufferedPositionUs = Math.min(bufferedPositionUs, rendererBufferedPositionUs);
@ -220,7 +210,7 @@ public final class DashSampleSource implements SampleSource {
public void seekToUs(long positionUs) { public void seekToUs(long positionUs) {
lastSeekPositionUs = positionUs; lastSeekPositionUs = positionUs;
pendingReset = true; pendingReset = true;
for (ChunkTrackStream trackStream : trackStreams) { for (ChunkTrackStream<DashChunkSource> trackStream : trackStreams) {
trackStream.setReadingEnabled(false); trackStream.setReadingEnabled(false);
trackStream.seekToUs(positionUs); trackStream.seekToUs(positionUs);
} }
@ -229,10 +219,7 @@ public final class DashSampleSource implements SampleSource {
@Override @Override
public void release() { public void release() {
loader.release(); loader.release();
for (DashChunkSource chunkSource : chunkSources) { for (ChunkTrackStream<DashChunkSource> trackStream : trackStreams) {
chunkSource.release();
}
for (ChunkTrackStream trackStream : trackStreams) {
trackStream.release(); trackStream.release();
} }
} }
@ -254,8 +241,8 @@ public final class DashSampleSource implements SampleSource {
prepared = true; prepared = true;
} }
} else { } else {
for (DashChunkSource chunkSource : chunkSources) { for (ChunkTrackStream<DashChunkSource> trackStream : trackStreams) {
chunkSource.updateManifest(manifest); trackStream.getChunkSource().updateManifest(manifest);
} }
} }
} }
@ -337,7 +324,7 @@ public final class DashSampleSource implements SampleSource {
trackGroups = new TrackGroupArray(trackGroupArray); trackGroups = new TrackGroupArray(trackGroupArray);
} }
private Pair<DashChunkSource, ChunkTrackStream> buildTrackStream(TrackSelection selection, private ChunkTrackStream<DashChunkSource> buildTrackStream(TrackSelection selection,
long positionUs) { long positionUs) {
int[] selectedTracks = selection.getTracks(); int[] selectedTracks = selection.getTracks();
FormatEvaluator adaptiveEvaluator = selectedTracks.length > 1 FormatEvaluator adaptiveEvaluator = selectedTracks.length > 1
@ -348,14 +335,17 @@ public final class DashSampleSource implements SampleSource {
int adaptationSetType = adaptationSet.type; int adaptationSetType = adaptationSet.type;
int bufferSize = Util.getDefaultBufferSize(adaptationSetType); int bufferSize = Util.getDefaultBufferSize(adaptationSetType);
DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter); DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
DashChunkSource chunkSource = new DashChunkSource(manifest, adaptationSetIndex, DashChunkSource chunkSource = new DashChunkSource(loader, manifest, adaptationSetIndex,
trackGroups.get(selection.group), selectedTracks, dataSource, adaptiveEvaluator, trackGroups.get(selection.group), selectedTracks, dataSource, adaptiveEvaluator,
elapsedRealtimeOffset); elapsedRealtimeOffset);
ChunkTrackStream trackStream = new ChunkTrackStream(chunkSource, loadControl, bufferSize, return new ChunkTrackStream<>(chunkSource, loadControl, bufferSize, positionUs, eventHandler,
positionUs, eventHandler, eventListener, adaptationSetType, MIN_LOADABLE_RETRY_COUNT); eventListener, adaptationSetType, MIN_LOADABLE_RETRY_COUNT);
return Pair.create(chunkSource, trackStream);
} }
@SuppressWarnings("unchecked")
private static ChunkTrackStream<DashChunkSource>[] newTrackStreamArray(int length) {
return new ChunkTrackStream[length];
}
private final class ManifestCallback implements private final class ManifestCallback implements
Loader.Callback<UriLoadable<MediaPresentationDescription>> { Loader.Callback<UriLoadable<MediaPresentationDescription>> {

View File

@ -93,7 +93,7 @@ import java.util.List;
* @param bufferSizeContribution The contribution of this source to the media buffer, in bytes. * @param bufferSizeContribution The contribution of this source to the media buffer, in bytes.
* @param muxedAudioFormat If HLS master playlist indicates that the stream contains muxed audio, * @param muxedAudioFormat If HLS master playlist indicates that the stream contains muxed audio,
* this is the audio {@link Format} as defined by the playlist. * this is the audio {@link Format} as defined by the playlist.
* @param muxedAudioFormat If HLS master playlist indicates that the stream contains muxed * @param muxedCaptionFormat If HLS master playlist indicates that the stream contains muxed
* captions, this is the audio {@link Format} as defined by the playlist. * captions, this is the audio {@link Format} as defined by the playlist.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required. * null if delivery of events is not required.

View File

@ -34,6 +34,7 @@ import com.google.android.exoplayer.extractor.mp4.TrackEncryptionBox;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement; import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.Loader;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
@ -47,6 +48,7 @@ import java.util.List;
*/ */
public class SmoothStreamingChunkSource implements ChunkSource { public class SmoothStreamingChunkSource implements ChunkSource {
private final Loader manifestLoader;
private final int elementIndex; private final int elementIndex;
private final TrackGroup trackGroup; private final TrackGroup trackGroup;
private final ChunkExtractorWrapper[] extractorWrappers; private final ChunkExtractorWrapper[] extractorWrappers;
@ -63,6 +65,7 @@ public class SmoothStreamingChunkSource implements ChunkSource {
private IOException fatalError; private IOException fatalError;
/** /**
* @param manifestLoader The {@link Loader} being used to load manifests.
* @param manifest The initial manifest. * @param manifest The initial manifest.
* @param elementIndex The index of the stream element in the manifest. * @param elementIndex The index of the stream element in the manifest.
* @param trackGroup The track group corresponding to the stream element. * @param trackGroup The track group corresponding to the stream element.
@ -71,9 +74,10 @@ public class SmoothStreamingChunkSource implements ChunkSource {
* @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats. * @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats.
* @param trackEncryptionBoxes Track encryption boxes for the stream. * @param trackEncryptionBoxes Track encryption boxes for the stream.
*/ */
public SmoothStreamingChunkSource(SmoothStreamingManifest manifest, int elementIndex, public SmoothStreamingChunkSource(Loader manifestLoader, SmoothStreamingManifest manifest,
TrackGroup trackGroup, int[] tracks, DataSource dataSource, int elementIndex, TrackGroup trackGroup, int[] tracks, DataSource dataSource,
FormatEvaluator adaptiveFormatEvaluator, TrackEncryptionBox[] trackEncryptionBoxes) { FormatEvaluator adaptiveFormatEvaluator, TrackEncryptionBox[] trackEncryptionBoxes) {
this.manifestLoader = manifestLoader;
this.manifest = manifest; this.manifest = manifest;
this.elementIndex = elementIndex; this.elementIndex = elementIndex;
this.trackGroup = trackGroup; this.trackGroup = trackGroup;
@ -135,18 +139,14 @@ public class SmoothStreamingChunkSource implements ChunkSource {
return needManifestRefresh; return needManifestRefresh;
} }
public void release() {
if (adaptiveFormatEvaluator != null) {
adaptiveFormatEvaluator.disable();
}
}
// ChunkSource implementation. // ChunkSource implementation.
@Override @Override
public void maybeThrowError() throws IOException { public void maybeThrowError() throws IOException {
if (fatalError != null) { if (fatalError != null) {
throw fatalError; throw fatalError;
} else {
manifestLoader.maybeThrowError();
} }
} }
@ -233,6 +233,13 @@ public class SmoothStreamingChunkSource implements ChunkSource {
return false; return false;
} }
@Override
public void release() {
if (adaptiveFormatEvaluator != null) {
adaptiveFormatEvaluator.disable();
}
}
// Private methods. // Private methods.
/** /**

View File

@ -44,7 +44,6 @@ import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Base64; import android.util.Base64;
import android.util.Pair;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -85,8 +84,7 @@ public final class SmoothStreamingSampleSource implements SampleSource,
private boolean pendingReset; private boolean pendingReset;
private long lastSeekPositionUs; private long lastSeekPositionUs;
private SmoothStreamingChunkSource[] chunkSources; private ChunkTrackStream<SmoothStreamingChunkSource>[] trackStreams;
private ChunkTrackStream[] trackStreams;
public SmoothStreamingSampleSource(Uri manifestUri, DataSourceFactory dataSourceFactory, public SmoothStreamingSampleSource(Uri manifestUri, DataSourceFactory dataSourceFactory,
BandwidthMeter bandwidthMeter, Handler eventHandler, BandwidthMeter bandwidthMeter, Handler eventHandler,
@ -97,11 +95,8 @@ public final class SmoothStreamingSampleSource implements SampleSource,
this.bandwidthMeter = bandwidthMeter; this.bandwidthMeter = bandwidthMeter;
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
loadControl = new DefaultLoadControl(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE)); loadControl = new DefaultLoadControl(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
chunkSources = new SmoothStreamingChunkSource[0]; trackStreams = newTrackStreamArray(0);
trackStreams = new ChunkTrackStream[0];
manifestDataSource = dataSourceFactory.createDataSource(); manifestDataSource = dataSourceFactory.createDataSource();
manifestParser = new SmoothStreamingManifestParser(); manifestParser = new SmoothStreamingManifestParser();
manifestLoader = new Loader("Loader:Manifest"); manifestLoader = new Loader("Loader:Manifest");
@ -133,19 +128,15 @@ public final class SmoothStreamingSampleSource implements SampleSource,
public TrackStream[] selectTracks(List<TrackStream> oldStreams, public TrackStream[] selectTracks(List<TrackStream> oldStreams,
List<TrackSelection> newSelections, long positionUs) { List<TrackSelection> newSelections, long positionUs) {
int newEnabledSourceCount = trackStreams.length + newSelections.size() - oldStreams.size(); int newEnabledSourceCount = trackStreams.length + newSelections.size() - oldStreams.size();
SmoothStreamingChunkSource[] newChunkSources = ChunkTrackStream<SmoothStreamingChunkSource>[] newTrackStreams =
new SmoothStreamingChunkSource[newEnabledSourceCount]; newTrackStreamArray(newEnabledSourceCount);
ChunkTrackStream[] newTrackStreams = new ChunkTrackStream[newEnabledSourceCount];
int newEnabledSourceIndex = 0; int newEnabledSourceIndex = 0;
// Iterate over currently enabled streams, either releasing them or adding them to the new list. // Iterate over currently enabled streams, either releasing them or adding them to the new list.
for (int i = 0; i < trackStreams.length; i++) { for (ChunkTrackStream<SmoothStreamingChunkSource> trackStream : trackStreams) {
ChunkTrackStream trackStream = trackStreams[i];
if (oldStreams.contains(trackStream)) { if (oldStreams.contains(trackStream)) {
chunkSources[i].release();
trackStream.release(); trackStream.release();
} else { } else {
newChunkSources[newEnabledSourceIndex] = chunkSources[i];
newTrackStreams[newEnabledSourceIndex++] = trackStream; newTrackStreams[newEnabledSourceIndex++] = trackStream;
} }
} }
@ -153,14 +144,11 @@ public final class SmoothStreamingSampleSource implements SampleSource,
// Instantiate and return new streams. // Instantiate and return new streams.
TrackStream[] streamsToReturn = new TrackStream[newSelections.size()]; TrackStream[] streamsToReturn = new TrackStream[newSelections.size()];
for (int i = 0; i < newSelections.size(); i++) { for (int i = 0; i < newSelections.size(); i++) {
Pair<SmoothStreamingChunkSource, ChunkTrackStream> trackComponents = newTrackStreams[newEnabledSourceIndex] = buildTrackStream(newSelections.get(i), positionUs);
buildTrackStream(newSelections.get(i), positionUs); streamsToReturn[i] = newTrackStreams[newEnabledSourceIndex];
newChunkSources[newEnabledSourceIndex] = trackComponents.first; newEnabledSourceIndex++;
newTrackStreams[newEnabledSourceIndex++] = trackComponents.second;
streamsToReturn[i] = trackComponents.second;
} }
chunkSources = newChunkSources;
trackStreams = newTrackStreams; trackStreams = newTrackStreams;
return streamsToReturn; return streamsToReturn;
} }
@ -170,15 +158,15 @@ public final class SmoothStreamingSampleSource implements SampleSource,
if (manifest.isLive) { if (manifest.isLive) {
if (!manifestLoader.isLoading() && SystemClock.elapsedRealtime() if (!manifestLoader.isLoading() && SystemClock.elapsedRealtime()
> manifestLoadTimestamp + MINIMUM_MANIFEST_REFRESH_PERIOD_MS) { > manifestLoadTimestamp + MINIMUM_MANIFEST_REFRESH_PERIOD_MS) {
for (SmoothStreamingChunkSource chunkSource : chunkSources) { for (ChunkTrackStream<SmoothStreamingChunkSource> trackStream : trackStreams) {
if (chunkSource.needManifestRefresh()) { if (trackStream.getChunkSource().needManifestRefresh()) {
startLoadingManifest(); startLoadingManifest();
break; break;
} }
} }
} }
} }
for (ChunkTrackStream trackStream : trackStreams) { for (ChunkTrackStream<SmoothStreamingChunkSource> trackStream : trackStreams) {
trackStream.continueBuffering(positionUs); trackStream.continueBuffering(positionUs);
} }
} }
@ -187,7 +175,7 @@ public final class SmoothStreamingSampleSource implements SampleSource,
public long readReset() { public long readReset() {
if (pendingReset) { if (pendingReset) {
pendingReset = false; pendingReset = false;
for (ChunkTrackStream trackStream : trackStreams) { for (ChunkTrackStream<SmoothStreamingChunkSource> trackStream : trackStreams) {
trackStream.setReadingEnabled(true); trackStream.setReadingEnabled(true);
} }
return lastSeekPositionUs; return lastSeekPositionUs;
@ -198,7 +186,7 @@ public final class SmoothStreamingSampleSource implements SampleSource,
@Override @Override
public long getBufferedPositionUs() { public long getBufferedPositionUs() {
long bufferedPositionUs = Long.MAX_VALUE; long bufferedPositionUs = Long.MAX_VALUE;
for (ChunkTrackStream trackStream : trackStreams) { for (ChunkTrackStream<SmoothStreamingChunkSource> trackStream : trackStreams) {
long rendererBufferedPositionUs = trackStream.getBufferedPositionUs(); long rendererBufferedPositionUs = trackStream.getBufferedPositionUs();
if (rendererBufferedPositionUs != C.END_OF_SOURCE_US) { if (rendererBufferedPositionUs != C.END_OF_SOURCE_US) {
bufferedPositionUs = Math.min(bufferedPositionUs, rendererBufferedPositionUs); bufferedPositionUs = Math.min(bufferedPositionUs, rendererBufferedPositionUs);
@ -211,7 +199,7 @@ public final class SmoothStreamingSampleSource implements SampleSource,
public void seekToUs(long positionUs) { public void seekToUs(long positionUs) {
lastSeekPositionUs = positionUs; lastSeekPositionUs = positionUs;
pendingReset = true; pendingReset = true;
for (ChunkTrackStream trackStream : trackStreams) { for (ChunkTrackStream<SmoothStreamingChunkSource> trackStream : trackStreams) {
trackStream.setReadingEnabled(false); trackStream.setReadingEnabled(false);
trackStream.seekToUs(positionUs); trackStream.seekToUs(positionUs);
} }
@ -220,10 +208,7 @@ public final class SmoothStreamingSampleSource implements SampleSource,
@Override @Override
public void release() { public void release() {
manifestLoader.release(); manifestLoader.release();
for (SmoothStreamingChunkSource chunkSource : chunkSources) { for (ChunkTrackStream<SmoothStreamingChunkSource> trackStream : trackStreams) {
chunkSource.release();
}
for (ChunkTrackStream trackStream : trackStreams) {
trackStream.release(); trackStream.release();
} }
} }
@ -245,8 +230,8 @@ public final class SmoothStreamingSampleSource implements SampleSource,
} }
prepared = true; prepared = true;
} else { } else {
for (SmoothStreamingChunkSource chunkSource : chunkSources) { for (ChunkTrackStream<SmoothStreamingChunkSource> trackStream : trackStreams) {
chunkSource.updateManifest(manifest); trackStream.getChunkSource().updateManifest(manifest);
} }
} }
} }
@ -291,8 +276,8 @@ public final class SmoothStreamingSampleSource implements SampleSource,
trackGroups = new TrackGroupArray(trackGroupArray); trackGroups = new TrackGroupArray(trackGroupArray);
} }
private Pair<SmoothStreamingChunkSource, ChunkTrackStream> buildTrackStream( private ChunkTrackStream<SmoothStreamingChunkSource> buildTrackStream(TrackSelection selection,
TrackSelection selection, long positionUs) { long positionUs) {
int[] selectedTracks = selection.getTracks(); int[] selectedTracks = selection.getTracks();
FormatEvaluator adaptiveEvaluator = selectedTracks.length > 1 FormatEvaluator adaptiveEvaluator = selectedTracks.length > 1
? new AdaptiveEvaluator(bandwidthMeter) : null; ? new AdaptiveEvaluator(bandwidthMeter) : null;
@ -301,12 +286,16 @@ public final class SmoothStreamingSampleSource implements SampleSource,
int streamElementType = streamElement.type; int streamElementType = streamElement.type;
int bufferSize = Util.getDefaultBufferSize(streamElementType); int bufferSize = Util.getDefaultBufferSize(streamElementType);
DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter); DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
SmoothStreamingChunkSource chunkSource = new SmoothStreamingChunkSource(manifest, SmoothStreamingChunkSource chunkSource = new SmoothStreamingChunkSource(manifestLoader,
streamElementIndex, trackGroups.get(selection.group), selectedTracks, dataSource, manifest, streamElementIndex, trackGroups.get(selection.group), selectedTracks, dataSource,
adaptiveEvaluator, trackEncryptionBoxes); adaptiveEvaluator, trackEncryptionBoxes);
ChunkTrackStream trackStream = new ChunkTrackStream(chunkSource, loadControl, bufferSize, return new ChunkTrackStream<>(chunkSource, loadControl, bufferSize, positionUs, eventHandler,
positionUs, eventHandler, eventListener, streamElementType, MIN_LOADABLE_RETRY_COUNT); eventListener, streamElementType, MIN_LOADABLE_RETRY_COUNT);
return Pair.create(chunkSource, trackStream); }
@SuppressWarnings("unchecked")
private static ChunkTrackStream<SmoothStreamingChunkSource>[] newTrackStreamArray(int length) {
return new ChunkTrackStream[length];
} }
private static byte[] getProtectionElementKeyId(byte[] initData) { private static byte[] getProtectionElementKeyId(byte[] initData) {