Skip to content

Commit

Permalink
Allow more flexible loading strategy when loading multiple sub streams.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
botaydotcom authored and ojw28 committed Nov 21, 2017
1 parent e459071 commit 0de57cb
Show file tree
Hide file tree
Showing 12 changed files with 244 additions and 52 deletions.
8 changes: 6 additions & 2 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
/**
* 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;
Expand Down Expand Up @@ -53,7 +53,7 @@ public final long getNextLoadPositionUs() {
}

@Override
public final boolean continueLoading(long positionUs) {
public boolean continueLoading(long positionUs) {
boolean madeProgress = false;
boolean madeProgressThisIteration;
do {
Expand Down
Original file line number Diff line number Diff line change
@@ -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);

}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@
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<>();
}
Expand Down Expand Up @@ -124,7 +127,8 @@ public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamF
// Update the local state.
enabledPeriods = new MediaPeriod[enabledPeriodsList.size()];
enabledPeriodsList.toArray(enabledPeriods);
sequenceableLoader = new CompositeSequenceableLoader(enabledPeriods);
compositeSequenceableLoader =
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(enabledPeriods);
return positionUs;
}

Expand All @@ -137,12 +141,12 @@ public void discardBuffer(long positionUs, boolean toKeyframe) {

@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
Expand All @@ -168,7 +172,7 @@ public long readDiscontinuity() {

@Override
public long getBufferedPositionUs() {
return sequenceableLoader.getBufferedPositionUs();
return compositeSequenceableLoader.getBufferedPositionUs();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public IllegalMergeException(@Reason int reason) {
private final MediaSource[] mediaSources;
private final ArrayList<MediaSource> pendingTimelineSources;
private final Timeline.Window window;
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;

private Listener listener;
private Timeline primaryTimeline;
Expand All @@ -85,7 +86,19 @@ public IllegalMergeException(@Reason int reason) {
* @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;
Expand Down Expand Up @@ -121,7 +134,7 @@ public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
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;
Expand Down Expand Up @@ -64,19 +64,21 @@
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;
Expand All @@ -86,9 +88,11 @@ public DashMediaPeriod(int id, DashManifest manifest, int periodIndex,
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,
Expand Down Expand Up @@ -163,7 +167,8 @@ public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamF
primarySampleStreams.values().toArray(sampleStreams);
eventSampleStreams = new EventSampleStream[eventSampleStreamList.size()];
eventSampleStreamList.toArray(eventSampleStreams);
sequenceableLoader = new CompositeSequenceableLoader(sampleStreams);
compositeSequenceableLoader =
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams);
return positionUs;
}

Expand Down Expand Up @@ -267,12 +272,12 @@ public void discardBuffer(long positionUs, boolean toKeyframe) {

@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
Expand All @@ -282,7 +287,7 @@ public long readDiscontinuity() {

@Override
public long getBufferedPositionUs() {
return sequenceableLoader.getBufferedPositionUs();
return compositeSequenceableLoader.getBufferedPositionUs();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@
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;
Expand Down Expand Up @@ -71,6 +74,7 @@ public static final class Builder {
private ParsingLoadable.Parser<? extends DashManifest> manifestParser;
private AdaptiveMediaSourceEventListener eventListener;
private Handler eventHandler;
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;

private int minLoadableRetryCount;
private long livePresentationDelayMs;
Expand Down Expand Up @@ -171,6 +175,22 @@ public Builder setManifestParser(
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>
Expand All @@ -186,9 +206,12 @@ public DashMediaSource build() {
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);
}

}
Expand Down Expand Up @@ -226,6 +249,7 @@ public DashMediaSource build() {
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;
Expand Down Expand Up @@ -280,7 +304,8 @@ public DashMediaSource(DashManifest manifest, DashChunkSource.Factory chunkSourc
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);
}

Expand Down Expand Up @@ -356,14 +381,16 @@ public DashMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFac
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;
Expand All @@ -372,6 +399,7 @@ private DashMediaSource(DashManifest manifest, Uri manifestUri,
this.chunkSourceFactory = chunkSourceFactory;
this.minLoadableRetryCount = minLoadableRetryCount;
this.livePresentationDelayMs = livePresentationDelayMs;
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
sideloadedManifest = manifest != null;
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
manifestUriLock = new Object();
Expand Down Expand Up @@ -438,7 +466,7 @@ public MediaPeriod createPeriod(MediaPeriodId periodId, Allocator allocator) {
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;
}
Expand Down

0 comments on commit 0de57cb

Please sign in to comment.