From 0de57cbfae7165dd3bb829e323d089cd312b4b1b Mon Sep 17 00:00:00 2001 From: hoangtc Date: Mon, 20 Nov 2017 08:22:19 -0800 Subject: [PATCH] Allow more flexible loading strategy when loading multiple sub streams. Currently for a DASH ChunkSource that consists of multiple sub-streams, we always use a CompositeSequenceableLoader, which only allows the furthest behind loader or any loader that are behind current playback position to continue loading. This changes allow clients to have more flexibility when deciding the loading strategy: - They can construct a different kind of composite SequenceableLoader from the sub-loaders, and use it by injecting a different CompositeSequeableLoaderFactory accordingly. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=176363870 --- RELEASENOTES.md | 8 +++- .../source/CompositeSequenceableLoader.java | 6 +-- .../CompositeSequenceableLoaderFactory.java | 31 +++++++++++++ ...ultCompositeSequenceableLoaderFactory.java | 29 ++++++++++++ .../exoplayer2/source/MergingMediaPeriod.java | 16 ++++--- .../exoplayer2/source/MergingMediaSource.java | 15 ++++++- .../source/dash/DashMediaPeriod.java | 23 ++++++---- .../source/dash/DashMediaSource.java | 42 ++++++++++++++--- .../exoplayer2/source/hls/HlsMediaPeriod.java | 20 ++++++--- .../exoplayer2/source/hls/HlsMediaSource.java | 42 ++++++++++++++++- .../source/smoothstreaming/SsMediaPeriod.java | 19 +++++--- .../source/smoothstreaming/SsMediaSource.java | 45 +++++++++++++++---- 12 files changed, 244 insertions(+), 52 deletions(-) create mode 100644 library/core/src/main/java/com/google/android/exoplayer2/source/CompositeSequenceableLoaderFactory.java create mode 100644 library/core/src/main/java/com/google/android/exoplayer2/source/DefaultCompositeSequenceableLoaderFactory.java diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d9ed3e5d2a..41748fa10d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,8 +2,12 @@ ### dev-v2 (not yet released) ### -* Add Builder to ExtractorMediaSource, HlsMediaSource, SsMediaSource, - DashMediaSource, SingleSampleMediaSource. +* Allow more flexible loading strategy when playing media containing multiple + sub-streams, by allowing injection of custom `CompositeSequenceableLoader` + factories through `DashMediaSource.Builder`, `HlsMediaSource.Builder`, + `SsMediaSource.Builder`, and `MergingMediaSource`. +* Add Builder to `ExtractorMediaSource`, `HlsMediaSource`, `SsMediaSource`, + `DashMediaSource`, `SingleSampleMediaSource`. * DASH: * Support in-MPD EventStream. * Allow a back-buffer of media to be retained behind the current playback diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeSequenceableLoader.java b/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeSequenceableLoader.java index a85d589762..e9a187a747 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeSequenceableLoader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeSequenceableLoader.java @@ -20,9 +20,9 @@ import com.google.android.exoplayer2.C; /** * A {@link SequenceableLoader} that encapsulates multiple other {@link SequenceableLoader}s. */ -public final class CompositeSequenceableLoader implements SequenceableLoader { +public class CompositeSequenceableLoader implements SequenceableLoader { - private final SequenceableLoader[] loaders; + protected final SequenceableLoader[] loaders; public CompositeSequenceableLoader(SequenceableLoader[] loaders) { this.loaders = loaders; @@ -53,7 +53,7 @@ public final class CompositeSequenceableLoader implements SequenceableLoader { } @Override - public final boolean continueLoading(long positionUs) { + public boolean continueLoading(long positionUs) { boolean madeProgress = false; boolean madeProgressThisIteration; do { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeSequenceableLoaderFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeSequenceableLoaderFactory.java new file mode 100644 index 0000000000..b4a266feef --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeSequenceableLoaderFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.source; + +/** + * A factory to create composite {@link SequenceableLoader}s. + */ +public interface CompositeSequenceableLoaderFactory { + + /** + * Creates a composite {@link SequenceableLoader}. + * + * @param loaders The sub-loaders that make up the {@link SequenceableLoader} to be built. + * @return A composite {@link SequenceableLoader} that comprises the given loaders. + */ + SequenceableLoader createCompositeSequenceableLoader(SequenceableLoader... loaders); + +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultCompositeSequenceableLoaderFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultCompositeSequenceableLoaderFactory.java new file mode 100644 index 0000000000..759b0824af --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultCompositeSequenceableLoaderFactory.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.source; + +/** + * Default implementation of {@link CompositeSequenceableLoaderFactory}. + */ +public final class DefaultCompositeSequenceableLoaderFactory + implements CompositeSequenceableLoaderFactory { + + @Override + public SequenceableLoader createCompositeSequenceableLoader(SequenceableLoader... loaders) { + return new CompositeSequenceableLoader(loaders); + } + +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaPeriod.java index 786a4693d0..bd37b5efec 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaPeriod.java @@ -30,15 +30,18 @@ import java.util.IdentityHashMap; public final MediaPeriod[] periods; private final IdentityHashMap streamPeriodIndices; + private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private Callback callback; private int pendingChildPrepareCount; private TrackGroupArray trackGroups; private MediaPeriod[] enabledPeriods; - private SequenceableLoader sequenceableLoader; + private SequenceableLoader compositeSequenceableLoader; - public MergingMediaPeriod(MediaPeriod... periods) { + public MergingMediaPeriod(CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, + MediaPeriod... periods) { + this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; this.periods = periods; streamPeriodIndices = new IdentityHashMap<>(); } @@ -124,7 +127,8 @@ import java.util.IdentityHashMap; // Update the local state. enabledPeriods = new MediaPeriod[enabledPeriodsList.size()]; enabledPeriodsList.toArray(enabledPeriods); - sequenceableLoader = new CompositeSequenceableLoader(enabledPeriods); + compositeSequenceableLoader = + compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(enabledPeriods); return positionUs; } @@ -137,12 +141,12 @@ import java.util.IdentityHashMap; @Override public boolean continueLoading(long positionUs) { - return sequenceableLoader.continueLoading(positionUs); + return compositeSequenceableLoader.continueLoading(positionUs); } @Override public long getNextLoadPositionUs() { - return sequenceableLoader.getNextLoadPositionUs(); + return compositeSequenceableLoader.getNextLoadPositionUs(); } @Override @@ -168,7 +172,7 @@ import java.util.IdentityHashMap; @Override public long getBufferedPositionUs() { - return sequenceableLoader.getBufferedPositionUs(); + return compositeSequenceableLoader.getBufferedPositionUs(); } @Override diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java index 1550970e47..ea0274796f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java @@ -74,6 +74,7 @@ public final class MergingMediaSource implements MediaSource { private final MediaSource[] mediaSources; private final ArrayList pendingTimelineSources; private final Timeline.Window window; + private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private Listener listener; private Timeline primaryTimeline; @@ -85,7 +86,19 @@ public final class MergingMediaSource implements MediaSource { * @param mediaSources The {@link MediaSource}s to merge. */ public MergingMediaSource(MediaSource... mediaSources) { + this(new DefaultCompositeSequenceableLoaderFactory(), mediaSources); + } + + /** + * @param compositeSequenceableLoaderFactory A factory to create composite + * {@link SequenceableLoader}s for when this media source loads data from multiple streams + * (video, audio etc...). + * @param mediaSources The {@link MediaSource}s to merge. + */ + public MergingMediaSource(CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, + MediaSource... mediaSources) { this.mediaSources = mediaSources; + this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; pendingTimelineSources = new ArrayList<>(Arrays.asList(mediaSources)); window = new Timeline.Window(); periodCount = PERIOD_COUNT_UNSET; @@ -121,7 +134,7 @@ public final class MergingMediaSource implements MediaSource { for (int i = 0; i < periods.length; i++) { periods[i] = mediaSources[i].createPeriod(id, allocator); } - return new MergingMediaPeriod(periods); + return new MergingMediaPeriod(compositeSequenceableLoaderFactory, periods); } @Override diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java index 5a60ee46ae..70fba4dd00 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java @@ -21,7 +21,7 @@ import android.util.SparseIntArray; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; -import com.google.android.exoplayer2.source.CompositeSequenceableLoader; +import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.EmptySampleStream; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.SampleStream; @@ -64,19 +64,21 @@ import java.util.Map; private final Allocator allocator; private final TrackGroupArray trackGroups; private final TrackGroupInfo[] trackGroupInfos; + private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private Callback callback; private ChunkSampleStream[] sampleStreams; private EventSampleStream[] eventSampleStreams; - private CompositeSequenceableLoader sequenceableLoader; + private SequenceableLoader compositeSequenceableLoader; private DashManifest manifest; private int periodIndex; private List eventStreams; public DashMediaPeriod(int id, DashManifest manifest, int periodIndex, - DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, + DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, EventDispatcher eventDispatcher, long elapsedRealtimeOffset, - LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator) { + LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator, + CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory) { this.id = id; this.manifest = manifest; this.periodIndex = periodIndex; @@ -86,9 +88,11 @@ import java.util.Map; this.elapsedRealtimeOffset = elapsedRealtimeOffset; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.allocator = allocator; + this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; sampleStreams = newSampleStreamArray(0); eventSampleStreams = new EventSampleStream[0]; - sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); + compositeSequenceableLoader = + compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams); Period period = manifest.getPeriod(periodIndex); eventStreams = period.eventStreams; Pair result = buildTrackGroups(period.adaptationSets, @@ -163,7 +167,8 @@ import java.util.Map; primarySampleStreams.values().toArray(sampleStreams); eventSampleStreams = new EventSampleStream[eventSampleStreamList.size()]; eventSampleStreamList.toArray(eventSampleStreams); - sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); + compositeSequenceableLoader = + compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams); return positionUs; } @@ -267,12 +272,12 @@ import java.util.Map; @Override public boolean continueLoading(long positionUs) { - return sequenceableLoader.continueLoading(positionUs); + return compositeSequenceableLoader.continueLoading(positionUs); } @Override public long getNextLoadPositionUs() { - return sequenceableLoader.getNextLoadPositionUs(); + return compositeSequenceableLoader.getNextLoadPositionUs(); } @Override @@ -282,7 +287,7 @@ import java.util.Map; @Override public long getBufferedPositionUs() { - return sequenceableLoader.getBufferedPositionUs(); + return compositeSequenceableLoader.getBufferedPositionUs(); } @Override diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index a82b5af583..68d39b5a18 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -28,8 +28,11 @@ import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; +import com.google.android.exoplayer2.source.DefaultCompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.SequenceableLoader; import com.google.android.exoplayer2.source.dash.manifest.DashManifest; import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser; import com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement; @@ -71,6 +74,7 @@ public final class DashMediaSource implements MediaSource { private ParsingLoadable.Parser manifestParser; private AdaptiveMediaSourceEventListener eventListener; private Handler eventHandler; + private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private int minLoadableRetryCount; private long livePresentationDelayMs; @@ -171,6 +175,22 @@ public final class DashMediaSource implements MediaSource { return this; } + /** + * Sets the factory to create composite {@link SequenceableLoader}s for when this media source + * loads data from multiple streams (video, audio etc...). The default is an instance of + * {@link DefaultCompositeSequenceableLoaderFactory}. + * + * @param compositeSequenceableLoaderFactory A factory to create composite + * {@link SequenceableLoader}s for when this media source loads data from multiple streams + * (video, audio etc...). + * @return This builder. + */ + public Builder setCompositeSequenceableLoaderFactory( + CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory) { + this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; + return this; + } + /** * Builds a new {@link DashMediaSource} using the current parameters. *

@@ -186,9 +206,12 @@ public final class DashMediaSource implements MediaSource { if (loadableManifestUri && manifestParser == null) { manifestParser = new DashManifestParser(); } + if (compositeSequenceableLoaderFactory == null) { + compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); + } return new DashMediaSource(manifest, manifestUri, manifestDataSourceFactory, manifestParser, - chunkSourceFactory, minLoadableRetryCount, livePresentationDelayMs, eventHandler, - eventListener); + chunkSourceFactory, compositeSequenceableLoaderFactory, minLoadableRetryCount, + livePresentationDelayMs, eventHandler, eventListener); } } @@ -226,6 +249,7 @@ public final class DashMediaSource implements MediaSource { private final boolean sideloadedManifest; private final DataSource.Factory manifestDataSourceFactory; private final DashChunkSource.Factory chunkSourceFactory; + private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private final int minLoadableRetryCount; private final long livePresentationDelayMs; private final EventDispatcher eventDispatcher; @@ -280,7 +304,8 @@ public final class DashMediaSource implements MediaSource { public DashMediaSource(DashManifest manifest, DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) { - this(manifest, null, null, null, chunkSourceFactory, minLoadableRetryCount, + this(manifest, null, null, null, chunkSourceFactory, + new DefaultCompositeSequenceableLoaderFactory(), minLoadableRetryCount, DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS, eventHandler, eventListener); } @@ -356,14 +381,16 @@ public final class DashMediaSource implements MediaSource { long livePresentationDelayMs, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) { this(null, manifestUri, manifestDataSourceFactory, manifestParser, chunkSourceFactory, - minLoadableRetryCount, livePresentationDelayMs, eventHandler, eventListener); + new DefaultCompositeSequenceableLoaderFactory(), minLoadableRetryCount, + livePresentationDelayMs, eventHandler, eventListener); } private DashMediaSource(DashManifest manifest, Uri manifestUri, DataSource.Factory manifestDataSourceFactory, ParsingLoadable.Parser manifestParser, - DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, - long livePresentationDelayMs, Handler eventHandler, + DashChunkSource.Factory chunkSourceFactory, + CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, + int minLoadableRetryCount, long livePresentationDelayMs, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) { this.manifest = manifest; this.manifestUri = manifestUri; @@ -372,6 +399,7 @@ public final class DashMediaSource implements MediaSource { this.chunkSourceFactory = chunkSourceFactory; this.minLoadableRetryCount = minLoadableRetryCount; this.livePresentationDelayMs = livePresentationDelayMs; + this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; sideloadedManifest = manifest != null; eventDispatcher = new EventDispatcher(eventHandler, eventListener); manifestUriLock = new Object(); @@ -438,7 +466,7 @@ public final class DashMediaSource implements MediaSource { manifest.getPeriod(periodIndex).startMs); DashMediaPeriod mediaPeriod = new DashMediaPeriod(firstPeriodId + periodIndex, manifest, periodIndex, chunkSourceFactory, minLoadableRetryCount, periodEventDispatcher, - elapsedRealtimeOffsetMs, loaderErrorThrower, allocator); + elapsedRealtimeOffsetMs, loaderErrorThrower, allocator, compositeSequenceableLoaderFactory); periodsById.put(mediaPeriod.id, mediaPeriod); return mediaPeriod; } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java index bc2b92cfe8..b6c74d61bb 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java @@ -20,9 +20,10 @@ import android.text.TextUtils; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; -import com.google.android.exoplayer2.source.CompositeSequenceableLoader; +import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.SampleStream; +import com.google.android.exoplayer2.source.SequenceableLoader; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist; @@ -53,23 +54,26 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper private final IdentityHashMap streamWrapperIndices; private final TimestampAdjusterProvider timestampAdjusterProvider; private final Handler continueLoadingHandler; + private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private Callback callback; private int pendingPrepareCount; private TrackGroupArray trackGroups; private HlsSampleStreamWrapper[] sampleStreamWrappers; private HlsSampleStreamWrapper[] enabledSampleStreamWrappers; - private CompositeSequenceableLoader sequenceableLoader; + private SequenceableLoader compositeSequenceableLoader; public HlsMediaPeriod(HlsExtractorFactory extractorFactory, HlsPlaylistTracker playlistTracker, HlsDataSourceFactory dataSourceFactory, int minLoadableRetryCount, - EventDispatcher eventDispatcher, Allocator allocator) { + EventDispatcher eventDispatcher, Allocator allocator, + CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory) { this.extractorFactory = extractorFactory; this.playlistTracker = playlistTracker; this.dataSourceFactory = dataSourceFactory; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; this.allocator = allocator; + this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; streamWrapperIndices = new IdentityHashMap<>(); timestampAdjusterProvider = new TimestampAdjusterProvider(); continueLoadingHandler = new Handler(); @@ -178,7 +182,9 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper // Update the local state. enabledSampleStreamWrappers = Arrays.copyOf(newEnabledSampleStreamWrappers, newEnabledSampleStreamWrapperCount); - sequenceableLoader = new CompositeSequenceableLoader(enabledSampleStreamWrappers); + compositeSequenceableLoader = + compositeSequenceableLoaderFactory.createCompositeSequenceableLoader( + enabledSampleStreamWrappers); return positionUs; } @@ -191,12 +197,12 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper @Override public boolean continueLoading(long positionUs) { - return sequenceableLoader.continueLoading(positionUs); + return compositeSequenceableLoader.continueLoading(positionUs); } @Override public long getNextLoadPositionUs() { - return sequenceableLoader.getNextLoadPositionUs(); + return compositeSequenceableLoader.getNextLoadPositionUs(); } @Override @@ -206,7 +212,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper @Override public long getBufferedPositionUs() { - return sequenceableLoader.getBufferedPositionUs(); + return compositeSequenceableLoader.getBufferedPositionUs(); } @Override diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index 3f28981f0e..a412b8c3e9 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -23,8 +23,11 @@ import com.google.android.exoplayer2.ExoPlayerLibraryInfo; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; +import com.google.android.exoplayer2.source.DefaultCompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.SequenceableLoader; import com.google.android.exoplayer2.source.SinglePeriodTimeline; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist; @@ -59,6 +62,8 @@ public final class HlsMediaSource implements MediaSource, private ParsingLoadable.Parser playlistParser; private AdaptiveMediaSourceEventListener eventListener; private Handler eventHandler; + private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; + private int minLoadableRetryCount; private boolean isBuildCalled; @@ -150,6 +155,22 @@ public final class HlsMediaSource implements MediaSource, return this; } + /** + * Sets the factory to create composite {@link SequenceableLoader}s for when this media source + * loads data from multiple streams (video, audio etc...). The default is an instance of + * {@link DefaultCompositeSequenceableLoaderFactory}. + * + * @param compositeSequenceableLoaderFactory A factory to create composite + * {@link SequenceableLoader}s for when this media source loads data from multiple streams + * (video, audio etc...). + * @return This builder. + */ + public Builder setCompositeSequenceableLoaderFactory( + CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory) { + this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; + return this; + } + /** * Builds a new {@link HlsMediaSource} using the current parameters. *

@@ -167,8 +188,12 @@ public final class HlsMediaSource implements MediaSource, if (playlistParser == null) { playlistParser = new HlsPlaylistParser(); } + if (compositeSequenceableLoaderFactory == null) { + compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); + } return new HlsMediaSource(manifestUri, hlsDataSourceFactory, extractorFactory, - minLoadableRetryCount, eventHandler, eventListener, playlistParser); + compositeSequenceableLoaderFactory, minLoadableRetryCount, eventHandler, eventListener, + playlistParser); } } @@ -181,6 +206,7 @@ public final class HlsMediaSource implements MediaSource, private final HlsExtractorFactory extractorFactory; private final Uri manifestUri; private final HlsDataSourceFactory dataSourceFactory; + private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private final int minLoadableRetryCount; private final EventDispatcher eventDispatcher; private final ParsingLoadable.Parser playlistParser; @@ -242,11 +268,23 @@ public final class HlsMediaSource implements MediaSource, HlsExtractorFactory extractorFactory, int minLoadableRetryCount, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener, ParsingLoadable.Parser playlistParser) { + this(manifestUri, dataSourceFactory, extractorFactory, + new DefaultCompositeSequenceableLoaderFactory(), minLoadableRetryCount, eventHandler, + eventListener, playlistParser); + } + + private HlsMediaSource(Uri manifestUri, HlsDataSourceFactory dataSourceFactory, + HlsExtractorFactory extractorFactory, + CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, + int minLoadableRetryCount, Handler eventHandler, + AdaptiveMediaSourceEventListener eventListener, + ParsingLoadable.Parser playlistParser) { this.manifestUri = manifestUri; this.dataSourceFactory = dataSourceFactory; this.extractorFactory = extractorFactory; this.minLoadableRetryCount = minLoadableRetryCount; this.playlistParser = playlistParser; + this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; eventDispatcher = new EventDispatcher(eventHandler, eventListener); } @@ -268,7 +306,7 @@ public final class HlsMediaSource implements MediaSource, public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { Assertions.checkArgument(id.periodIndex == 0); return new HlsMediaPeriod(extractorFactory, playlistTracker, dataSourceFactory, - minLoadableRetryCount, eventDispatcher, allocator); + minLoadableRetryCount, eventDispatcher, allocator, compositeSequenceableLoaderFactory); } @Override diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java index 3c51abcd49..c079a36d62 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java @@ -19,7 +19,7 @@ import android.util.Base64; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; -import com.google.android.exoplayer2.source.CompositeSequenceableLoader; +import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SequenceableLoader; @@ -49,13 +49,15 @@ import java.util.ArrayList; private final Allocator allocator; private final TrackGroupArray trackGroups; private final TrackEncryptionBox[] trackEncryptionBoxes; + private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private Callback callback; private SsManifest manifest; private ChunkSampleStream[] sampleStreams; - private CompositeSequenceableLoader sequenceableLoader; + private SequenceableLoader compositeSequenceableLoader; public SsMediaPeriod(SsManifest manifest, SsChunkSource.Factory chunkSourceFactory, + CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, int minLoadableRetryCount, EventDispatcher eventDispatcher, LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator) { this.chunkSourceFactory = chunkSourceFactory; @@ -63,6 +65,7 @@ import java.util.ArrayList; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; this.allocator = allocator; + this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; trackGroups = buildTrackGroups(manifest); ProtectionElement protectionElement = manifest.protectionElement; @@ -76,7 +79,8 @@ import java.util.ArrayList; } this.manifest = manifest; sampleStreams = newSampleStreamArray(0); - sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); + compositeSequenceableLoader = + compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams); } public void updateManifest(SsManifest manifest) { @@ -133,7 +137,8 @@ import java.util.ArrayList; } sampleStreams = newSampleStreamArray(sampleStreamsList.size()); sampleStreamsList.toArray(sampleStreams); - sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); + compositeSequenceableLoader = + compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams); return positionUs; } @@ -146,12 +151,12 @@ import java.util.ArrayList; @Override public boolean continueLoading(long positionUs) { - return sequenceableLoader.continueLoading(positionUs); + return compositeSequenceableLoader.continueLoading(positionUs); } @Override public long getNextLoadPositionUs() { - return sequenceableLoader.getNextLoadPositionUs(); + return compositeSequenceableLoader.getNextLoadPositionUs(); } @Override @@ -161,7 +166,7 @@ import java.util.ArrayList; @Override public long getBufferedPositionUs() { - return sequenceableLoader.getBufferedPositionUs(); + return compositeSequenceableLoader.getBufferedPositionUs(); } @Override diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java index 5a93847428..a4b601aafe 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java @@ -26,8 +26,11 @@ import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; +import com.google.android.exoplayer2.source.DefaultCompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.SequenceableLoader; import com.google.android.exoplayer2.source.SinglePeriodTimeline; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement; @@ -65,6 +68,7 @@ public final class SsMediaSource implements MediaSource, private ParsingLoadable.Parser manifestParser; private AdaptiveMediaSourceEventListener eventListener; private Handler eventHandler; + private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private int minLoadableRetryCount; private long livePresentationDelayMs; @@ -162,6 +166,22 @@ public final class SsMediaSource implements MediaSource, return this; } + /** + * Sets the factory to create composite {@link SequenceableLoader}s for when this media source + * loads data from multiple streams (video, audio etc...). The default is an instance of + * {@link DefaultCompositeSequenceableLoaderFactory}. + * + * @param compositeSequenceableLoaderFactory A factory to create composite + * {@link SequenceableLoader}s for when this media source loads data from multiple streams + * (video, audio etc...). + * @return This builder. + */ + public Builder setCompositeSequenceableLoaderFactory( + CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory) { + this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; + return this; + } + /** * Builds a new {@link SsMediaSource} using the current parameters. *

@@ -177,9 +197,12 @@ public final class SsMediaSource implements MediaSource, if (loadableManifestUri && manifestParser == null) { manifestParser = new SsManifestParser(); } + if (compositeSequenceableLoaderFactory == null) { + compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); + } return new SsMediaSource(manifest, manifestUri, manifestDataSourceFactory, manifestParser, - chunkSourceFactory, minLoadableRetryCount, livePresentationDelayMs, eventHandler, - eventListener); + chunkSourceFactory, compositeSequenceableLoaderFactory, minLoadableRetryCount, + livePresentationDelayMs, eventHandler, eventListener); } } @@ -206,6 +229,7 @@ public final class SsMediaSource implements MediaSource, private final Uri manifestUri; private final DataSource.Factory manifestDataSourceFactory; private final SsChunkSource.Factory chunkSourceFactory; + private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private final int minLoadableRetryCount; private final long livePresentationDelayMs; private final EventDispatcher eventDispatcher; @@ -252,7 +276,8 @@ public final class SsMediaSource implements MediaSource, public SsMediaSource(SsManifest manifest, SsChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) { - this(manifest, null, null, null, chunkSourceFactory, minLoadableRetryCount, + this(manifest, null, null, null, chunkSourceFactory, + new DefaultCompositeSequenceableLoaderFactory(), minLoadableRetryCount, DEFAULT_LIVE_PRESENTATION_DELAY_MS, eventHandler, eventListener); } @@ -324,14 +349,16 @@ public final class SsMediaSource implements MediaSource, long livePresentationDelayMs, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) { this(null, manifestUri, manifestDataSourceFactory, manifestParser, chunkSourceFactory, - minLoadableRetryCount, livePresentationDelayMs, eventHandler, eventListener); + new DefaultCompositeSequenceableLoaderFactory(), minLoadableRetryCount, + livePresentationDelayMs, eventHandler, eventListener); } private SsMediaSource(SsManifest manifest, Uri manifestUri, DataSource.Factory manifestDataSourceFactory, ParsingLoadable.Parser manifestParser, - SsChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, - long livePresentationDelayMs, Handler eventHandler, + SsChunkSource.Factory chunkSourceFactory, + CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, + int minLoadableRetryCount, long livePresentationDelayMs, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) { Assertions.checkState(manifest == null || !manifest.isLive); this.manifest = manifest; @@ -341,6 +368,7 @@ public final class SsMediaSource implements MediaSource, this.manifestDataSourceFactory = manifestDataSourceFactory; this.manifestParser = manifestParser; this.chunkSourceFactory = chunkSourceFactory; + this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; this.minLoadableRetryCount = minLoadableRetryCount; this.livePresentationDelayMs = livePresentationDelayMs; this.eventDispatcher = new EventDispatcher(eventHandler, eventListener); @@ -372,8 +400,9 @@ public final class SsMediaSource implements MediaSource, @Override public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { Assertions.checkArgument(id.periodIndex == 0); - SsMediaPeriod period = new SsMediaPeriod(manifest, chunkSourceFactory, minLoadableRetryCount, - eventDispatcher, manifestLoaderErrorThrower, allocator); + SsMediaPeriod period = new SsMediaPeriod(manifest, chunkSourceFactory, + compositeSequenceableLoaderFactory, minLoadableRetryCount, eventDispatcher, + manifestLoaderErrorThrower, allocator); mediaPeriods.add(period); return period; }