Skip to content

Commit 0de57cb

Browse files
botaydotcomojw28
authored andcommitted
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
1 parent e459071 commit 0de57cb

File tree

12 files changed

+244
-52
lines changed

12 files changed

+244
-52
lines changed

RELEASENOTES.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
### dev-v2 (not yet released) ###
44

5-
* Add Builder to ExtractorMediaSource, HlsMediaSource, SsMediaSource,
6-
DashMediaSource, SingleSampleMediaSource.
5+
* Allow more flexible loading strategy when playing media containing multiple
6+
sub-streams, by allowing injection of custom `CompositeSequenceableLoader`
7+
factories through `DashMediaSource.Builder`, `HlsMediaSource.Builder`,
8+
`SsMediaSource.Builder`, and `MergingMediaSource`.
9+
* Add Builder to `ExtractorMediaSource`, `HlsMediaSource`, `SsMediaSource`,
10+
`DashMediaSource`, `SingleSampleMediaSource`.
711
* DASH:
812
* Support in-MPD EventStream.
913
* Allow a back-buffer of media to be retained behind the current playback

library/core/src/main/java/com/google/android/exoplayer2/source/CompositeSequenceableLoader.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
/**
2121
* A {@link SequenceableLoader} that encapsulates multiple other {@link SequenceableLoader}s.
2222
*/
23-
public final class CompositeSequenceableLoader implements SequenceableLoader {
23+
public class CompositeSequenceableLoader implements SequenceableLoader {
2424

25-
private final SequenceableLoader[] loaders;
25+
protected final SequenceableLoader[] loaders;
2626

2727
public CompositeSequenceableLoader(SequenceableLoader[] loaders) {
2828
this.loaders = loaders;
@@ -53,7 +53,7 @@ public final long getNextLoadPositionUs() {
5353
}
5454

5555
@Override
56-
public final boolean continueLoading(long positionUs) {
56+
public boolean continueLoading(long positionUs) {
5757
boolean madeProgress = false;
5858
boolean madeProgressThisIteration;
5959
do {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (C) 2017 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.android.exoplayer2.source;
17+
18+
/**
19+
* A factory to create composite {@link SequenceableLoader}s.
20+
*/
21+
public interface CompositeSequenceableLoaderFactory {
22+
23+
/**
24+
* Creates a composite {@link SequenceableLoader}.
25+
*
26+
* @param loaders The sub-loaders that make up the {@link SequenceableLoader} to be built.
27+
* @return A composite {@link SequenceableLoader} that comprises the given loaders.
28+
*/
29+
SequenceableLoader createCompositeSequenceableLoader(SequenceableLoader... loaders);
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (C) 2017 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.android.exoplayer2.source;
17+
18+
/**
19+
* Default implementation of {@link CompositeSequenceableLoaderFactory}.
20+
*/
21+
public final class DefaultCompositeSequenceableLoaderFactory
22+
implements CompositeSequenceableLoaderFactory {
23+
24+
@Override
25+
public SequenceableLoader createCompositeSequenceableLoader(SequenceableLoader... loaders) {
26+
return new CompositeSequenceableLoader(loaders);
27+
}
28+
29+
}

library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaPeriod.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,18 @@
3030
public final MediaPeriod[] periods;
3131

3232
private final IdentityHashMap<SampleStream, Integer> streamPeriodIndices;
33+
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
3334

3435
private Callback callback;
3536
private int pendingChildPrepareCount;
3637
private TrackGroupArray trackGroups;
3738

3839
private MediaPeriod[] enabledPeriods;
39-
private SequenceableLoader sequenceableLoader;
40+
private SequenceableLoader compositeSequenceableLoader;
4041

41-
public MergingMediaPeriod(MediaPeriod... periods) {
42+
public MergingMediaPeriod(CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
43+
MediaPeriod... periods) {
44+
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
4245
this.periods = periods;
4346
streamPeriodIndices = new IdentityHashMap<>();
4447
}
@@ -124,7 +127,8 @@ public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamF
124127
// Update the local state.
125128
enabledPeriods = new MediaPeriod[enabledPeriodsList.size()];
126129
enabledPeriodsList.toArray(enabledPeriods);
127-
sequenceableLoader = new CompositeSequenceableLoader(enabledPeriods);
130+
compositeSequenceableLoader =
131+
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(enabledPeriods);
128132
return positionUs;
129133
}
130134

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

138142
@Override
139143
public boolean continueLoading(long positionUs) {
140-
return sequenceableLoader.continueLoading(positionUs);
144+
return compositeSequenceableLoader.continueLoading(positionUs);
141145
}
142146

143147
@Override
144148
public long getNextLoadPositionUs() {
145-
return sequenceableLoader.getNextLoadPositionUs();
149+
return compositeSequenceableLoader.getNextLoadPositionUs();
146150
}
147151

148152
@Override
@@ -168,7 +172,7 @@ public long readDiscontinuity() {
168172

169173
@Override
170174
public long getBufferedPositionUs() {
171-
return sequenceableLoader.getBufferedPositionUs();
175+
return compositeSequenceableLoader.getBufferedPositionUs();
172176
}
173177

174178
@Override

library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public IllegalMergeException(@Reason int reason) {
7474
private final MediaSource[] mediaSources;
7575
private final ArrayList<MediaSource> pendingTimelineSources;
7676
private final Timeline.Window window;
77+
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
7778

7879
private Listener listener;
7980
private Timeline primaryTimeline;
@@ -85,7 +86,19 @@ public IllegalMergeException(@Reason int reason) {
8586
* @param mediaSources The {@link MediaSource}s to merge.
8687
*/
8788
public MergingMediaSource(MediaSource... mediaSources) {
89+
this(new DefaultCompositeSequenceableLoaderFactory(), mediaSources);
90+
}
91+
92+
/**
93+
* @param compositeSequenceableLoaderFactory A factory to create composite
94+
* {@link SequenceableLoader}s for when this media source loads data from multiple streams
95+
* (video, audio etc...).
96+
* @param mediaSources The {@link MediaSource}s to merge.
97+
*/
98+
public MergingMediaSource(CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
99+
MediaSource... mediaSources) {
88100
this.mediaSources = mediaSources;
101+
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
89102
pendingTimelineSources = new ArrayList<>(Arrays.asList(mediaSources));
90103
window = new Timeline.Window();
91104
periodCount = PERIOD_COUNT_UNSET;
@@ -121,7 +134,7 @@ public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
121134
for (int i = 0; i < periods.length; i++) {
122135
periods[i] = mediaSources[i].createPeriod(id, allocator);
123136
}
124-
return new MergingMediaPeriod(periods);
137+
return new MergingMediaPeriod(compositeSequenceableLoaderFactory, periods);
125138
}
126139

127140
@Override

library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import com.google.android.exoplayer2.C;
2222
import com.google.android.exoplayer2.Format;
2323
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
24-
import com.google.android.exoplayer2.source.CompositeSequenceableLoader;
24+
import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory;
2525
import com.google.android.exoplayer2.source.EmptySampleStream;
2626
import com.google.android.exoplayer2.source.MediaPeriod;
2727
import com.google.android.exoplayer2.source.SampleStream;
@@ -64,19 +64,21 @@
6464
private final Allocator allocator;
6565
private final TrackGroupArray trackGroups;
6666
private final TrackGroupInfo[] trackGroupInfos;
67+
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
6768

6869
private Callback callback;
6970
private ChunkSampleStream<DashChunkSource>[] sampleStreams;
7071
private EventSampleStream[] eventSampleStreams;
71-
private CompositeSequenceableLoader sequenceableLoader;
72+
private SequenceableLoader compositeSequenceableLoader;
7273
private DashManifest manifest;
7374
private int periodIndex;
7475
private List<EventStream> eventStreams;
7576

7677
public DashMediaPeriod(int id, DashManifest manifest, int periodIndex,
77-
DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount,
78+
DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount,
7879
EventDispatcher eventDispatcher, long elapsedRealtimeOffset,
79-
LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator) {
80+
LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator,
81+
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory) {
8082
this.id = id;
8183
this.manifest = manifest;
8284
this.periodIndex = periodIndex;
@@ -86,9 +88,11 @@ public DashMediaPeriod(int id, DashManifest manifest, int periodIndex,
8688
this.elapsedRealtimeOffset = elapsedRealtimeOffset;
8789
this.manifestLoaderErrorThrower = manifestLoaderErrorThrower;
8890
this.allocator = allocator;
91+
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
8992
sampleStreams = newSampleStreamArray(0);
9093
eventSampleStreams = new EventSampleStream[0];
91-
sequenceableLoader = new CompositeSequenceableLoader(sampleStreams);
94+
compositeSequenceableLoader =
95+
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams);
9296
Period period = manifest.getPeriod(periodIndex);
9397
eventStreams = period.eventStreams;
9498
Pair<TrackGroupArray, TrackGroupInfo[]> result = buildTrackGroups(period.adaptationSets,
@@ -163,7 +167,8 @@ public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamF
163167
primarySampleStreams.values().toArray(sampleStreams);
164168
eventSampleStreams = new EventSampleStream[eventSampleStreamList.size()];
165169
eventSampleStreamList.toArray(eventSampleStreams);
166-
sequenceableLoader = new CompositeSequenceableLoader(sampleStreams);
170+
compositeSequenceableLoader =
171+
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams);
167172
return positionUs;
168173
}
169174

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

268273
@Override
269274
public boolean continueLoading(long positionUs) {
270-
return sequenceableLoader.continueLoading(positionUs);
275+
return compositeSequenceableLoader.continueLoading(positionUs);
271276
}
272277

273278
@Override
274279
public long getNextLoadPositionUs() {
275-
return sequenceableLoader.getNextLoadPositionUs();
280+
return compositeSequenceableLoader.getNextLoadPositionUs();
276281
}
277282

278283
@Override
@@ -282,7 +287,7 @@ public long readDiscontinuity() {
282287

283288
@Override
284289
public long getBufferedPositionUs() {
285-
return sequenceableLoader.getBufferedPositionUs();
290+
return compositeSequenceableLoader.getBufferedPositionUs();
286291
}
287292

288293
@Override

library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@
2828
import com.google.android.exoplayer2.Timeline;
2929
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
3030
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
31+
import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory;
32+
import com.google.android.exoplayer2.source.DefaultCompositeSequenceableLoaderFactory;
3133
import com.google.android.exoplayer2.source.MediaPeriod;
3234
import com.google.android.exoplayer2.source.MediaSource;
35+
import com.google.android.exoplayer2.source.SequenceableLoader;
3336
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
3437
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
3538
import com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement;
@@ -71,6 +74,7 @@ public static final class Builder {
7174
private ParsingLoadable.Parserextends DashManifest> manifestParser;
7275
private AdaptiveMediaSourceEventListener eventListener;
7376
private Handler eventHandler;
77+
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
7478

7579
private int minLoadableRetryCount;
7680
private long livePresentationDelayMs;
@@ -171,6 +175,22 @@ public Builder setManifestParser(
171175
return this;
172176
}
173177

178+
/**
179+
* Sets the factory to create composite {@link SequenceableLoader}s for when this media source
180+
* loads data from multiple streams (video, audio etc...). The default is an instance of
181+
* {@link DefaultCompositeSequenceableLoaderFactory}.
182+
*
183+
* @param compositeSequenceableLoaderFactory A factory to create composite
184+
* {@link SequenceableLoader}s for when this media source loads data from multiple streams
185+
* (video, audio etc...).
186+
* @return This builder.
187+
*/
188+
public Builder setCompositeSequenceableLoaderFactory(
189+
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory) {
190+
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
191+
return this;
192+
}
193+
174194
/**
175195
* Builds a new {@link DashMediaSource} using the current parameters.
176196
*

@@ -186,9 +206,12 @@ public DashMediaSource build() {
186206
if (loadableManifestUri && manifestParser == null) {
187207
manifestParser = new DashManifestParser();
188208
}
209+
if (compositeSequenceableLoaderFactory == null) {
210+
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory();
211+
}
189212
return new DashMediaSource(manifest, manifestUri, manifestDataSourceFactory, manifestParser,
190-
chunkSourceFactory, minLoadableRetryCount, livePresentationDelayMs, eventHandler,
191-
eventListener);
213+
chunkSourceFactory, compositeSequenceableLoaderFactory, minLoadableRetryCount,
214+
livePresentationDelayMs, eventHandler, eventListener);
192215
}
193216

194217
}
@@ -226,6 +249,7 @@ public DashMediaSource build() {
226249
private final boolean sideloadedManifest;
227250
private final DataSource.Factory manifestDataSourceFactory;
228251
private final DashChunkSource.Factory chunkSourceFactory;
252+
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
229253
private final int minLoadableRetryCount;
230254
private final long livePresentationDelayMs;
231255
private final EventDispatcher eventDispatcher;
@@ -280,7 +304,8 @@ public DashMediaSource(DashManifest manifest, DashChunkSource.Factory chunkSourc
280304
public DashMediaSource(DashManifest manifest, DashChunkSource.Factory chunkSourceFactory,
281305
int minLoadableRetryCount, Handler eventHandler, AdaptiveMediaSourceEventListener
282306
eventListener) {
283-
this(manifest, null, null, null, chunkSourceFactory, minLoadableRetryCount,
307+
this(manifest, null, null, null, chunkSourceFactory,
308+
new DefaultCompositeSequenceableLoaderFactory(), minLoadableRetryCount,
284309
DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS, eventHandler, eventListener);
285310
}
286311

@@ -356,14 +381,16 @@ public DashMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFac
356381
long livePresentationDelayMs, Handler eventHandler,
357382
AdaptiveMediaSourceEventListener eventListener) {
358383
this(null, manifestUri, manifestDataSourceFactory, manifestParser, chunkSourceFactory,
359-
minLoadableRetryCount, livePresentationDelayMs, eventHandler, eventListener);
384+
new DefaultCompositeSequenceableLoaderFactory(), minLoadableRetryCount,
385+
livePresentationDelayMs, eventHandler, eventListener);
360386
}
361387

362388
private DashMediaSource(DashManifest manifest, Uri manifestUri,
363389
DataSource.Factory manifestDataSourceFactory,
364390
ParsingLoadable.Parserextends DashManifest> manifestParser,
365-
DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount,
366-
long livePresentationDelayMs, Handler eventHandler,
391+
DashChunkSource.Factory chunkSourceFactory,
392+
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
393+
int minLoadableRetryCount, long livePresentationDelayMs, Handler eventHandler,
367394
AdaptiveMediaSourceEventListener eventListener) {
368395
this.manifest = manifest;
369396
this.manifestUri = manifestUri;
@@ -372,6 +399,7 @@ private DashMediaSource(DashManifest manifest, Uri manifestUri,
372399
this.chunkSourceFactory = chunkSourceFactory;
373400
this.minLoadableRetryCount = minLoadableRetryCount;
374401
this.livePresentationDelayMs = livePresentationDelayMs;
402+
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
375403
sideloadedManifest = manifest != null;
376404
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
377405
manifestUriLock = new Object();
@@ -438,7 +466,7 @@ public MediaPeriod createPeriod(MediaPeriodId periodId, Allocator allocator) {
438466
manifest.getPeriod(periodIndex).startMs);
439467
DashMediaPeriod mediaPeriod = new DashMediaPeriod(firstPeriodId + periodIndex, manifest,
440468
periodIndex, chunkSourceFactory, minLoadableRetryCount, periodEventDispatcher,
441-
elapsedRealtimeOffsetMs, loaderErrorThrower, allocator);
469+
elapsedRealtimeOffsetMs, loaderErrorThrower, allocator, compositeSequenceableLoaderFactory);
442470
periodsById.put(mediaPeriod.id, mediaPeriod);
443471
return mediaPeriod;
444472
}

0 commit comments

Comments
 (0)