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
This commit is contained in:
hoangtc 2017-11-20 08:22:19 -08:00 committed by Oliver Woodman
parent e45907193c
commit 0de57cbfae
12 changed files with 244 additions and 52 deletions

View File

@ -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

View File

@ -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 {

View File

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

View File

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

View File

@ -30,15 +30,18 @@ import java.util.IdentityHashMap;
public final MediaPeriod[] periods;
private final IdentityHashMap<SampleStream, Integer> 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

View File

@ -74,6 +74,7 @@ public final class MergingMediaSource implements MediaSource {
private final MediaSource[] mediaSources;
private final ArrayList<MediaSource> 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

View File

@ -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<DashChunkSource>[] sampleStreams;
private EventSampleStream[] eventSampleStreams;
private CompositeSequenceableLoader sequenceableLoader;
private SequenceableLoader compositeSequenceableLoader;
private DashManifest manifest;
private int periodIndex;
private List<EventStream> 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<TrackGroupArray, TrackGroupInfo[]> 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

View File

@ -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<? extends DashManifest> 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.
* <p>
@ -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<? extends DashManifest> 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;
}

View File

@ -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<SampleStream, Integer> 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

View File

@ -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<HlsPlaylist> 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.
* <p>
@ -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<HlsPlaylist> playlistParser;
@ -242,11 +268,23 @@ public final class HlsMediaSource implements MediaSource,
HlsExtractorFactory extractorFactory, int minLoadableRetryCount, Handler eventHandler,
AdaptiveMediaSourceEventListener eventListener,
ParsingLoadable.Parser<HlsPlaylist> 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<HlsPlaylist> 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

View File

@ -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<SsChunkSource>[] 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

View File

@ -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<? extends SsManifest> 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.
* <p>
@ -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<? extends SsManifest> 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;
}