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();
}