mirror of
https://github.com/androidx/media.git
synced 2025-05-04 14:10:40 +08:00
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:
parent
7aae5805b8
commit
7f70ee911b
@ -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;
|
||||||
|
@ -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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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,8 +192,10 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback<Chunk> {
|
|||||||
@Override
|
@Override
|
||||||
public void maybeThrowError() throws IOException {
|
public void maybeThrowError() throws IOException {
|
||||||
loader.maybeThrowError();
|
loader.maybeThrowError();
|
||||||
|
if (!loader.isLoading()) {
|
||||||
chunkSource.maybeThrowError();
|
chunkSource.maybeThrowError();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
||||||
|
@ -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() {
|
||||||
|
@ -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>> {
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user