diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java index 71fbf48cb0..574f370486 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java @@ -135,9 +135,6 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call if (prepared) { return true; } - if (!chunkSource.prepare()) { - return false; - } TrackGroup tracks = chunkSource.getTracks(); if (tracks != null) { trackGroups = new TrackGroupArray(tracks); @@ -207,7 +204,6 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call @Override public void continueBuffering(long positionUs) { downstreamPositionUs = positionUs; - chunkSource.continueBuffering(); if (!loader.isLoading()) { maybeStartLoading(); } diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSource.java b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSource.java index fc3ec1307e..6d8171b30d 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSource.java @@ -41,16 +41,6 @@ public interface ChunkSource { */ void maybeThrowError() throws IOException; - /** - * Prepares the source. - *

- * The method can be called repeatedly until the return value indicates success. - * - * @return True if the source was prepared, false otherwise. - * @throws IOException If an error occurs preparing the source. - */ - boolean prepare() throws IOException; - /** * Gets the duration of the source in microseconds. *

@@ -80,13 +70,6 @@ public interface ChunkSource { */ void enable(int[] tracks); - /** - * Indicates to the source that it should still be checking for updates to the stream. - *

- * This method should only be called when the source is enabled. - */ - void continueBuffering(); - /** * Evaluates whether {@link MediaChunk}s should be removed from the back of the queue. *

diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java index ededceb322..2c01193853 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java @@ -43,7 +43,6 @@ import com.google.android.exoplayer.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer.extractor.mp4.FragmentedMp4Extractor; import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSpec; -import com.google.android.exoplayer.util.ManifestFetcher; import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.Util; @@ -71,7 +70,6 @@ public class DashChunkSource implements ChunkSource { private final DataSource dataSource; private final FormatEvaluator adaptiveFormatEvaluator; private final Evaluation evaluation; - private final ManifestFetcher manifestFetcher; // Properties of the initial manifest. private boolean live; @@ -93,15 +91,13 @@ public class DashChunkSource implements ChunkSource { private boolean[] adaptiveFormatBlacklistFlags; /** - * @param manifestFetcher A fetcher for the manifest. * @param adaptationSetType The type of the adaptation set exposed by this source. One of * {@link C#TRACK_TYPE_AUDIO}, {@link C#TRACK_TYPE_VIDEO} and {@link C#TRACK_TYPE_TEXT}. * @param dataSource A {@link DataSource} suitable for loading the media data. * @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats. */ - public DashChunkSource(ManifestFetcher manifestFetcher, - int adaptationSetType, DataSource dataSource, FormatEvaluator adaptiveFormatEvaluator) { - this.manifestFetcher = manifestFetcher; + public DashChunkSource(int adaptationSetType, DataSource dataSource, + FormatEvaluator adaptiveFormatEvaluator) { this.adaptationSetType = adaptationSetType; this.dataSource = dataSource; this.adaptiveFormatEvaluator = adaptiveFormatEvaluator; @@ -114,24 +110,12 @@ public class DashChunkSource implements ChunkSource { public void maybeThrowError() throws IOException { if (fatalError != null) { throw fatalError; - } else if (live) { - manifestFetcher.maybeThrowError(); } } - @Override - public boolean prepare() throws IOException { - if (currentManifest == null) { - currentManifest = manifestFetcher.getManifest(); - if (currentManifest == null) { - manifestFetcher.maybeThrowError(); - manifestFetcher.requestRefresh(); - return false; - } else { - initForManifest(currentManifest); - } - } - return true; + public void init(MediaPresentationDescription initialManifest) { + currentManifest = initialManifest; + initForManifest(currentManifest); } @Override @@ -155,33 +139,10 @@ public class DashChunkSource implements ChunkSource { adaptiveFormatEvaluator.enable(enabledFormats); adaptiveFormatBlacklistFlags = new boolean[tracks.length]; } - processManifest(manifestFetcher.getManifest()); } - @Override - public void continueBuffering() { - if (!currentManifest.dynamic || fatalError != null) { - return; - } - - MediaPresentationDescription newManifest = manifestFetcher.getManifest(); - if (newManifest != null && newManifest != currentManifest) { - processManifest(newManifest); - } - - // TODO: This is a temporary hack to avoid constantly refreshing the MPD in cases where - // minUpdatePeriod is set to 0. In such cases we shouldn't refresh unless there is explicit - // signaling in the stream, according to: - // http://azure.microsoft.com/blog/2014/09/13/dash-live-streaming-with-azure-media-service/ - long minUpdatePeriod = currentManifest.minUpdatePeriod; - if (minUpdatePeriod == 0) { - minUpdatePeriod = 5000; - } - - if (android.os.SystemClock.elapsedRealtime() - > manifestFetcher.getManifestLoadStartTimestamp() + minUpdatePeriod) { - manifestFetcher.requestRefresh(); - } + public void updateManifest(MediaPresentationDescription newManifest) { + processManifest(newManifest); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashSampleSource.java b/library/src/main/java/com/google/android/exoplayer/dash/DashSampleSource.java index b0b2273b98..9a10a267d7 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/DashSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/DashSampleSource.java @@ -25,7 +25,6 @@ import com.google.android.exoplayer.TrackSelection; import com.google.android.exoplayer.TrackStream; import com.google.android.exoplayer.chunk.ChunkSampleSource; import com.google.android.exoplayer.chunk.ChunkSampleSourceEventListener; -import com.google.android.exoplayer.chunk.ChunkSource; import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator; import com.google.android.exoplayer.dash.mpd.MediaPresentationDescription; import com.google.android.exoplayer.dash.mpd.MediaPresentationDescriptionParser; @@ -38,6 +37,7 @@ import com.google.android.exoplayer.util.ManifestFetcher; import android.net.Uri; import android.os.Handler; +import android.os.SystemClock; import android.util.Pair; import java.io.IOException; @@ -50,10 +50,13 @@ import java.util.List; */ public final class DashSampleSource implements SampleSource { + private final ManifestFetcher manifestFetcher; + private final DashChunkSource[] chunkSources; private final SampleSource[] sources; private final IdentityHashMap trackStreamSources; private final int[] selectedTrackCounts; + private MediaPresentationDescription currentManifest; private boolean prepared; private boolean seenFirstTrackSelection; private long durationUs; @@ -65,34 +68,32 @@ public final class DashSampleSource implements SampleSource { ChunkSampleSourceEventListener eventListener) { MediaPresentationDescriptionParser parser = new MediaPresentationDescriptionParser(); DataSource manifestDataSource = dataSourceFactory.createDataSource(); - // TODO[REFACTOR]: This needs releasing. - ManifestFetcher manifestFetcher = new ManifestFetcher<>(uri, - manifestDataSource, parser); + manifestFetcher = new ManifestFetcher<>(uri, manifestDataSource, parser); LoadControl loadControl = new DefaultLoadControl( new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE)); // Build the video renderer. DataSource videoDataSource = dataSourceFactory.createDataSource(bandwidthMeter); - ChunkSource videoChunkSource = new DashChunkSource(manifestFetcher, C.TRACK_TYPE_VIDEO, - videoDataSource, new AdaptiveEvaluator(bandwidthMeter)); + DashChunkSource videoChunkSource = new DashChunkSource(C.TRACK_TYPE_VIDEO, videoDataSource, + new AdaptiveEvaluator(bandwidthMeter)); ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, C.DEFAULT_VIDEO_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_VIDEO); // Build the audio renderer. DataSource audioDataSource = dataSourceFactory.createDataSource(bandwidthMeter); - ChunkSource audioChunkSource = new DashChunkSource(manifestFetcher, C.TRACK_TYPE_AUDIO, - audioDataSource, null); + DashChunkSource audioChunkSource = new DashChunkSource(C.TRACK_TYPE_AUDIO, audioDataSource, + null); ChunkSampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl, C.DEFAULT_AUDIO_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_AUDIO); // Build the text renderer. DataSource textDataSource = dataSourceFactory.createDataSource(bandwidthMeter); - ChunkSource textChunkSource = new DashChunkSource(manifestFetcher, C.TRACK_TYPE_TEXT, - textDataSource, null); + DashChunkSource textChunkSource = new DashChunkSource(C.TRACK_TYPE_TEXT, textDataSource, null); ChunkSampleSource textSampleSource = new ChunkSampleSource(textChunkSource, loadControl, C.DEFAULT_TEXT_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_TEXT); + chunkSources = new DashChunkSource[] {videoChunkSource, audioChunkSource, textChunkSource}; sources = new SampleSource[] {videoSampleSource, audioSampleSource, textSampleSource}; trackStreamSources = new IdentityHashMap<>(); selectedTrackCounts = new int[sources.length]; @@ -103,6 +104,20 @@ public final class DashSampleSource implements SampleSource { if (prepared) { return true; } + + if (currentManifest == null) { + currentManifest = manifestFetcher.getManifest(); + if (currentManifest == null) { + manifestFetcher.maybeThrowError(); + manifestFetcher.requestRefresh(); + return false; + } else { + for (DashChunkSource chunkSource : chunkSources) { + chunkSource.init(currentManifest); + } + } + } + boolean sourcesPrepared = true; for (SampleSource source : sources) { sourcesPrepared &= source.prepare(positionUs); @@ -171,6 +186,30 @@ public final class DashSampleSource implements SampleSource { @Override public void continueBuffering(long positionUs) { + if (currentManifest.dynamic) { + MediaPresentationDescription newManifest = manifestFetcher.getManifest(); + if (newManifest != currentManifest) { + currentManifest = newManifest; + for (DashChunkSource chunkSource : chunkSources) { + chunkSource.updateManifest(newManifest); + } + } + + long minUpdatePeriod = currentManifest.minUpdatePeriod; + if (minUpdatePeriod == 0) { + // TODO: This is a temporary hack to avoid constantly refreshing the MPD in cases where + // minUpdatePeriod is set to 0. In such cases we shouldn't refresh unless there is explicit + // signaling in the stream, according to: + // http://azure.microsoft.com/blog/2014/09/13/dash-live-streaming-with-azure-media-service/ + minUpdatePeriod = 5000; + } + + if (SystemClock.elapsedRealtime() > manifestFetcher.getManifestLoadStartTimestamp() + + minUpdatePeriod) { + manifestFetcher.requestRefresh(); + } + } + for (SampleSource source : enabledSources) { source.continueBuffering(positionUs); } @@ -215,6 +254,7 @@ public final class DashSampleSource implements SampleSource { @Override public void release() { + manifestFetcher.release(); for (SampleSource source : sources) { source.release(); } diff --git a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java index dfcea4283e..a3adf3a0df 100644 --- a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java @@ -37,11 +37,9 @@ import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.Prot import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement; import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSpec; -import com.google.android.exoplayer.util.ManifestFetcher; import com.google.android.exoplayer.util.MimeTypes; import android.net.Uri; -import android.os.SystemClock; import android.text.TextUtils; import android.util.Base64; @@ -55,16 +53,13 @@ import java.util.List; // TODO[REFACTOR]: Handle multiple stream elements of the same type (at a higher level). public class SmoothStreamingChunkSource implements ChunkSource { - private static final int MINIMUM_MANIFEST_REFRESH_PERIOD_MS = 5000; private static final int INITIALIZATION_VECTOR_SIZE = 8; private final int streamElementType; private final DataSource dataSource; private final Evaluation evaluation; - private final ManifestFetcher manifestFetcher; private final FormatEvaluator adaptiveFormatEvaluator; - private boolean live; private long durationUs; private TrackEncryptionBox[] trackEncryptionBoxes; private DrmInitData.Mapped drmInitData; @@ -84,59 +79,48 @@ public class SmoothStreamingChunkSource implements ChunkSource { private IOException fatalError; /** - * @param manifestFetcher A fetcher for the manifest. * @param streamElementType The type of stream element exposed by this source. One of * {@link C#TRACK_TYPE_VIDEO}, {@link C#TRACK_TYPE_AUDIO} and {@link C#TRACK_TYPE_TEXT}. * @param dataSource A {@link DataSource} suitable for loading the media data. * @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats. */ - public SmoothStreamingChunkSource(ManifestFetcher manifestFetcher, - int streamElementType, DataSource dataSource, FormatEvaluator adaptiveFormatEvaluator) { - this.manifestFetcher = manifestFetcher; + public SmoothStreamingChunkSource(int streamElementType, DataSource dataSource, + FormatEvaluator adaptiveFormatEvaluator) { this.streamElementType = streamElementType; this.dataSource = dataSource; this.adaptiveFormatEvaluator = adaptiveFormatEvaluator; evaluation = new Evaluation(); } + public boolean needManifestRefresh() { + return needManifestRefresh; + } + // ChunkSource implementation. @Override public void maybeThrowError() throws IOException { if (fatalError != null) { throw fatalError; - } else if (live) { - manifestFetcher.maybeThrowError(); } } - @Override - public boolean prepare() throws IOException { - if (currentManifest == null) { - currentManifest = manifestFetcher.getManifest(); - if (currentManifest == null) { - manifestFetcher.maybeThrowError(); - manifestFetcher.requestRefresh(); - return false; - } else { - live = currentManifest.isLive; - durationUs = currentManifest.durationUs; - ProtectionElement protectionElement = currentManifest.protectionElement; - if (protectionElement != null) { - byte[] keyId = getProtectionElementKeyId(protectionElement.data); - trackEncryptionBoxes = new TrackEncryptionBox[1]; - trackEncryptionBoxes[0] = new TrackEncryptionBox(true, INITIALIZATION_VECTOR_SIZE, keyId); - drmInitData = new DrmInitData.Mapped(); - drmInitData.put(protectionElement.uuid, - new SchemeInitData(MimeTypes.VIDEO_MP4, protectionElement.data)); - } else { - trackEncryptionBoxes = null; - drmInitData = null; - } - initForManifest(currentManifest); - } + public void init(SmoothStreamingManifest initialManifest) { + currentManifest = initialManifest; + durationUs = currentManifest.durationUs; + ProtectionElement protectionElement = currentManifest.protectionElement; + if (protectionElement != null) { + byte[] keyId = getProtectionElementKeyId(protectionElement.data); + trackEncryptionBoxes = new TrackEncryptionBox[1]; + trackEncryptionBoxes[0] = new TrackEncryptionBox(true, INITIALIZATION_VECTOR_SIZE, keyId); + drmInitData = new DrmInitData.Mapped(); + drmInitData.put(protectionElement.uuid, + new SchemeInitData(MimeTypes.VIDEO_MP4, protectionElement.data)); + } else { + trackEncryptionBoxes = null; + drmInitData = null; } - return true; + initForManifest(currentManifest); } @Override @@ -162,40 +146,27 @@ public class SmoothStreamingChunkSource implements ChunkSource { } } - @Override - public void continueBuffering() { - if (!currentManifest.isLive || fatalError != null) { - return; - } - - SmoothStreamingManifest newManifest = manifestFetcher.getManifest(); - if (currentManifest != newManifest && newManifest != null) { - StreamElement currentElement = currentManifest.streamElements[elementIndex]; - int currentElementChunkCount = currentElement.chunkCount; - StreamElement newElement = newManifest.streamElements[elementIndex]; - if (currentElementChunkCount == 0 || newElement.chunkCount == 0) { - // There's no overlap between the old and new elements because at least one is empty. + public void updateManifest(SmoothStreamingManifest newManifest) { + StreamElement currentElement = currentManifest.streamElements[elementIndex]; + int currentElementChunkCount = currentElement.chunkCount; + StreamElement newElement = newManifest.streamElements[elementIndex]; + if (currentElementChunkCount == 0 || newElement.chunkCount == 0) { + // There's no overlap between the old and new elements because at least one is empty. + currentManifestChunkOffset += currentElementChunkCount; + } else { + long currentElementEndTimeUs = currentElement.getStartTimeUs(currentElementChunkCount - 1) + + currentElement.getChunkDurationUs(currentElementChunkCount - 1); + long newElementStartTimeUs = newElement.getStartTimeUs(0); + if (currentElementEndTimeUs <= newElementStartTimeUs) { + // There's no overlap between the old and new elements. currentManifestChunkOffset += currentElementChunkCount; } else { - long currentElementEndTimeUs = currentElement.getStartTimeUs(currentElementChunkCount - 1) - + currentElement.getChunkDurationUs(currentElementChunkCount - 1); - long newElementStartTimeUs = newElement.getStartTimeUs(0); - if (currentElementEndTimeUs <= newElementStartTimeUs) { - // There's no overlap between the old and new elements. - currentManifestChunkOffset += currentElementChunkCount; - } else { - // The new element overlaps with the old one. - currentManifestChunkOffset += currentElement.getChunkIndex(newElementStartTimeUs); - } + // The new element overlaps with the old one. + currentManifestChunkOffset += currentElement.getChunkIndex(newElementStartTimeUs); } - currentManifest = newManifest; - needManifestRefresh = false; - } - - if (needManifestRefresh && (SystemClock.elapsedRealtime() - > manifestFetcher.getManifestLoadStartTimestamp() + MINIMUM_MANIFEST_REFRESH_PERIOD_MS)) { - manifestFetcher.requestRefresh(); } + currentManifest = newManifest; + needManifestRefresh = false; } @Override diff --git a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingSampleSource.java b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingSampleSource.java index e846cbd006..bb0cc8a2ea 100644 --- a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingSampleSource.java @@ -25,7 +25,6 @@ import com.google.android.exoplayer.TrackSelection; import com.google.android.exoplayer.TrackStream; import com.google.android.exoplayer.chunk.ChunkSampleSource; import com.google.android.exoplayer.chunk.ChunkSampleSourceEventListener; -import com.google.android.exoplayer.chunk.ChunkSource; import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator; import com.google.android.exoplayer.upstream.BandwidthMeter; import com.google.android.exoplayer.upstream.DataSource; @@ -37,6 +36,7 @@ import com.google.android.exoplayer.util.Util; import android.net.Uri; import android.os.Handler; +import android.os.SystemClock; import android.util.Pair; import java.io.IOException; @@ -49,10 +49,15 @@ import java.util.List; */ public final class SmoothStreamingSampleSource implements SampleSource { + private static final int MINIMUM_MANIFEST_REFRESH_PERIOD_MS = 5000; + + private final ManifestFetcher manifestFetcher; + private final SmoothStreamingChunkSource[] chunkSources; private final SampleSource[] sources; private final IdentityHashMap trackStreamSources; private final int[] selectedTrackCounts; + private SmoothStreamingManifest currentManifest; private boolean prepared; private boolean seenFirstTrackSelection; private long durationUs; @@ -67,33 +72,33 @@ public final class SmoothStreamingSampleSource implements SampleSource { } SmoothStreamingManifestParser parser = new SmoothStreamingManifestParser(); DataSource manifestDataSource = dataSourceFactory.createDataSource(); - // TODO[REFACTOR]: This needs releasing. - ManifestFetcher manifestFetcher = new ManifestFetcher<>(uri, - manifestDataSource, parser); + manifestFetcher = new ManifestFetcher<>(uri, manifestDataSource, parser); LoadControl loadControl = new DefaultLoadControl( new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE)); // Build the video renderer. DataSource videoDataSource = dataSourceFactory.createDataSource(bandwidthMeter); - ChunkSource videoChunkSource = new SmoothStreamingChunkSource(manifestFetcher, - C.TRACK_TYPE_VIDEO, videoDataSource, new AdaptiveEvaluator(bandwidthMeter)); + SmoothStreamingChunkSource videoChunkSource = new SmoothStreamingChunkSource(C.TRACK_TYPE_VIDEO, + videoDataSource, new AdaptiveEvaluator(bandwidthMeter)); ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, C.DEFAULT_VIDEO_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_VIDEO); // Build the audio renderer. DataSource audioDataSource = dataSourceFactory.createDataSource(bandwidthMeter); - ChunkSource audioChunkSource = new SmoothStreamingChunkSource(manifestFetcher, - C.TRACK_TYPE_AUDIO, audioDataSource, null); + SmoothStreamingChunkSource audioChunkSource = new SmoothStreamingChunkSource(C.TRACK_TYPE_AUDIO, + audioDataSource, null); ChunkSampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl, C.DEFAULT_AUDIO_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_AUDIO); // Build the text renderer. DataSource textDataSource = dataSourceFactory.createDataSource(bandwidthMeter); - ChunkSource textChunkSource = new SmoothStreamingChunkSource(manifestFetcher, C.TRACK_TYPE_TEXT, + SmoothStreamingChunkSource textChunkSource = new SmoothStreamingChunkSource(C.TRACK_TYPE_TEXT, textDataSource, null); ChunkSampleSource textSampleSource = new ChunkSampleSource(textChunkSource, loadControl, C.DEFAULT_TEXT_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_TEXT); + chunkSources = new SmoothStreamingChunkSource[] {videoChunkSource, audioChunkSource, + textChunkSource}; sources = new SampleSource[] {videoSampleSource, audioSampleSource, textSampleSource}; trackStreamSources = new IdentityHashMap<>(); selectedTrackCounts = new int[sources.length]; @@ -104,6 +109,20 @@ public final class SmoothStreamingSampleSource implements SampleSource { if (prepared) { return true; } + + if (currentManifest == null) { + currentManifest = manifestFetcher.getManifest(); + if (currentManifest == null) { + manifestFetcher.maybeThrowError(); + manifestFetcher.requestRefresh(); + return false; + } else { + for (SmoothStreamingChunkSource chunkSource : chunkSources) { + chunkSource.init(currentManifest); + } + } + } + boolean sourcesPrepared = true; for (SampleSource source : sources) { sourcesPrepared &= source.prepare(positionUs); @@ -172,6 +191,26 @@ public final class SmoothStreamingSampleSource implements SampleSource { @Override public void continueBuffering(long positionUs) { + if (currentManifest.isLive) { + SmoothStreamingManifest newManifest = manifestFetcher.getManifest(); + if (newManifest != currentManifest) { + currentManifest = newManifest; + for (SmoothStreamingChunkSource chunkSource : chunkSources) { + chunkSource.updateManifest(newManifest); + } + } + + if (SystemClock.elapsedRealtime() + > manifestFetcher.getManifestLoadStartTimestamp() + MINIMUM_MANIFEST_REFRESH_PERIOD_MS) { + for (SmoothStreamingChunkSource chunkSource : chunkSources) { + if (chunkSource.needManifestRefresh()) { + manifestFetcher.requestRefresh(); + break; + } + } + } + } + for (SampleSource source : enabledSources) { source.continueBuffering(positionUs); } @@ -216,6 +255,7 @@ public final class SmoothStreamingSampleSource implements SampleSource { @Override public void release() { + manifestFetcher.release(); for (SampleSource source : sources) { source.release(); }