Commit f1fe1c40 by aquilescanta Committed by Oliver Woodman

Extract HlsPlaylistTracker interface

This allows injection of custom implementations and configuration of
DefaultHlsPlaylistTracker without modifying the HlsMediaSource interface.

Issue:#2844

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=198846607
parent 798b29e3
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
extractor for this ([#4297](https://github.com/google/ExoPlayer/issues/4297)). extractor for this ([#4297](https://github.com/google/ExoPlayer/issues/4297)).
* DASH: Fix playback getting stuck when playing representations that have both * DASH: Fix playback getting stuck when playing representations that have both
sidx atoms and non-zero presentationTimeOffset values. sidx atoms and non-zero presentationTimeOffset values.
* HLS: Fix adaptation in live playlists with EXT-X-PROGRAM-DATE-TIME tags. * HLS:
* Allow injection of custom playlist trackers.
* Fix adaptation in live playlists with EXT-X-PROGRAM-DATE-TIME tags.
* Mitigate memory leaks when `MediaSource` loads are slow to cancel * Mitigate memory leaks when `MediaSource` loads are slow to cancel
([#4249](https://github.com/google/ExoPlayer/issues/4249)). ([#4249](https://github.com/google/ExoPlayer/issues/4249)).
* Fix inconsistent `Player.EventListener` invocations for recursive player state * Fix inconsistent `Player.EventListener` invocations for recursive player state
......
...@@ -32,6 +32,7 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispat ...@@ -32,6 +32,7 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispat
import com.google.android.exoplayer2.source.SequenceableLoader; import com.google.android.exoplayer2.source.SequenceableLoader;
import com.google.android.exoplayer2.source.SinglePeriodTimeline; import com.google.android.exoplayer2.source.SinglePeriodTimeline;
import com.google.android.exoplayer2.source.ads.AdsMediaSource; import com.google.android.exoplayer2.source.ads.AdsMediaSource;
import com.google.android.exoplayer2.source.hls.playlist.DefaultHlsPlaylistTracker;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser; import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser;
...@@ -58,6 +59,7 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -58,6 +59,7 @@ public final class HlsMediaSource extends BaseMediaSource
private HlsExtractorFactory extractorFactory; private HlsExtractorFactory extractorFactory;
private @Nullable ParsingLoadable.Parser<HlsPlaylist> playlistParser; private @Nullable ParsingLoadable.Parser<HlsPlaylist> playlistParser;
private @Nullable HlsPlaylistTracker playlistTracker;
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private int minLoadableRetryCount; private int minLoadableRetryCount;
private boolean allowChunklessPreparation; private boolean allowChunklessPreparation;
...@@ -136,17 +138,38 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -136,17 +138,38 @@ public final class HlsMediaSource extends BaseMediaSource
* Sets the parser to parse HLS playlists. The default is an instance of {@link * Sets the parser to parse HLS playlists. The default is an instance of {@link
* HlsPlaylistParser}. * HlsPlaylistParser}.
* *
* <p>Must not be called after calling {@link #setPlaylistTracker} on the same builder.
*
* @param playlistParser A {@link ParsingLoadable.Parser} for HLS playlists. * @param playlistParser A {@link ParsingLoadable.Parser} for HLS playlists.
* @return This factory, for convenience. * @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called. * @throws IllegalStateException If one of the {@code create} methods has already been called.
*/ */
public Factory setPlaylistParser(ParsingLoadable.Parser<HlsPlaylist> playlistParser) { public Factory setPlaylistParser(ParsingLoadable.Parser<HlsPlaylist> playlistParser) {
Assertions.checkState(!isCreateCalled); Assertions.checkState(!isCreateCalled);
Assertions.checkState(playlistTracker == null, "A playlist tracker has already been set.");
this.playlistParser = Assertions.checkNotNull(playlistParser); this.playlistParser = Assertions.checkNotNull(playlistParser);
return this; return this;
} }
/** /**
* Sets the HLS playlist tracker. The default is an instance of {@link
* DefaultHlsPlaylistTracker}. Playlist trackers must not be shared by {@link HlsMediaSource}
* instances.
*
* <p>Must not be called after calling {@link #setPlaylistParser} on the same builder.
*
* @param playlistTracker A tracker for HLS playlists.
* @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
*/
public Factory setPlaylistTracker(HlsPlaylistTracker playlistTracker) {
Assertions.checkState(!isCreateCalled);
Assertions.checkState(playlistParser == null, "A playlist parser has already been set.");
this.playlistTracker = Assertions.checkNotNull(playlistTracker);
return this;
}
/**
* Sets the factory to create composite {@link SequenceableLoader}s for when this media source * 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 * loads data from multiple streams (video, audio etc...). The default is an instance of {@link
* DefaultCompositeSequenceableLoaderFactory}. * DefaultCompositeSequenceableLoaderFactory}.
...@@ -187,8 +210,12 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -187,8 +210,12 @@ public final class HlsMediaSource extends BaseMediaSource
@Override @Override
public HlsMediaSource createMediaSource(Uri playlistUri) { public HlsMediaSource createMediaSource(Uri playlistUri) {
isCreateCalled = true; isCreateCalled = true;
if (playlistParser == null) { if (playlistTracker == null) {
playlistParser = new HlsPlaylistParser(); playlistTracker =
new DefaultHlsPlaylistTracker(
hlsDataSourceFactory,
minLoadableRetryCount,
playlistParser != null ? playlistParser : new HlsPlaylistParser());
} }
return new HlsMediaSource( return new HlsMediaSource(
playlistUri, playlistUri,
...@@ -196,7 +223,7 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -196,7 +223,7 @@ public final class HlsMediaSource extends BaseMediaSource
extractorFactory, extractorFactory,
compositeSequenceableLoaderFactory, compositeSequenceableLoaderFactory,
minLoadableRetryCount, minLoadableRetryCount,
playlistParser, playlistTracker,
allowChunklessPreparation, allowChunklessPreparation,
tag); tag);
} }
...@@ -233,12 +260,10 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -233,12 +260,10 @@ public final class HlsMediaSource extends BaseMediaSource
private final HlsDataSourceFactory dataSourceFactory; private final HlsDataSourceFactory dataSourceFactory;
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private final int minLoadableRetryCount; private final int minLoadableRetryCount;
private final ParsingLoadable.Parser<HlsPlaylist> playlistParser;
private final boolean allowChunklessPreparation; private final boolean allowChunklessPreparation;
private final HlsPlaylistTracker playlistTracker;
private final @Nullable Object tag; private final @Nullable Object tag;
private HlsPlaylistTracker playlistTracker;
/** /**
* @param manifestUri The {@link Uri} of the HLS manifest. * @param manifestUri The {@link Uri} of the HLS manifest.
* @param dataSourceFactory An {@link HlsDataSourceFactory} for {@link DataSource}s for manifests, * @param dataSourceFactory An {@link HlsDataSourceFactory} for {@link DataSource}s for manifests,
...@@ -276,8 +301,13 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -276,8 +301,13 @@ public final class HlsMediaSource extends BaseMediaSource
int minLoadableRetryCount, int minLoadableRetryCount,
Handler eventHandler, Handler eventHandler,
MediaSourceEventListener eventListener) { MediaSourceEventListener eventListener) {
this(manifestUri, new DefaultHlsDataSourceFactory(dataSourceFactory), this(
HlsExtractorFactory.DEFAULT, minLoadableRetryCount, eventHandler, eventListener, manifestUri,
new DefaultHlsDataSourceFactory(dataSourceFactory),
HlsExtractorFactory.DEFAULT,
minLoadableRetryCount,
eventHandler,
eventListener,
new HlsPlaylistParser()); new HlsPlaylistParser());
} }
...@@ -309,7 +339,8 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -309,7 +339,8 @@ public final class HlsMediaSource extends BaseMediaSource
extractorFactory, extractorFactory,
new DefaultCompositeSequenceableLoaderFactory(), new DefaultCompositeSequenceableLoaderFactory(),
minLoadableRetryCount, minLoadableRetryCount,
playlistParser, new DefaultHlsPlaylistTracker(
dataSourceFactory, minLoadableRetryCount, new HlsPlaylistParser()),
/* allowChunklessPreparation= */ false, /* allowChunklessPreparation= */ false,
/* tag= */ null); /* tag= */ null);
if (eventHandler != null && eventListener != null) { if (eventHandler != null && eventListener != null) {
...@@ -323,7 +354,7 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -323,7 +354,7 @@ public final class HlsMediaSource extends BaseMediaSource
HlsExtractorFactory extractorFactory, HlsExtractorFactory extractorFactory,
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
int minLoadableRetryCount, int minLoadableRetryCount,
ParsingLoadable.Parser<HlsPlaylist> playlistParser, HlsPlaylistTracker playlistTracker,
boolean allowChunklessPreparation, boolean allowChunklessPreparation,
@Nullable Object tag) { @Nullable Object tag) {
this.manifestUri = manifestUri; this.manifestUri = manifestUri;
...@@ -331,7 +362,7 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -331,7 +362,7 @@ public final class HlsMediaSource extends BaseMediaSource
this.extractorFactory = extractorFactory; this.extractorFactory = extractorFactory;
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
this.minLoadableRetryCount = minLoadableRetryCount; this.minLoadableRetryCount = minLoadableRetryCount;
this.playlistParser = playlistParser; this.playlistTracker = playlistTracker;
this.allowChunklessPreparation = allowChunklessPreparation; this.allowChunklessPreparation = allowChunklessPreparation;
this.tag = tag; this.tag = tag;
} }
...@@ -339,9 +370,7 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -339,9 +370,7 @@ public final class HlsMediaSource extends BaseMediaSource
@Override @Override
public void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) { public void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
EventDispatcher eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null); EventDispatcher eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null);
playlistTracker = new HlsPlaylistTracker(manifestUri, dataSourceFactory, eventDispatcher, playlistTracker.start(manifestUri, eventDispatcher, /* listener= */ this);
minLoadableRetryCount, this, playlistParser);
playlistTracker.start();
} }
@Override @Override
...@@ -373,7 +402,6 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -373,7 +402,6 @@ public final class HlsMediaSource extends BaseMediaSource
public void releaseSourceInternal() { public void releaseSourceInternal() {
if (playlistTracker != null) { if (playlistTracker != null) {
playlistTracker.release(); playlistTracker.release();
playlistTracker = null;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment