Add simplified adaptive media source stream key filter to factory classes.

This change only simplifies the API by using the ManifestParser internally.

PiperOrigin-RevId: 225516193
This commit is contained in:
tonihei 2018-12-14 11:16:33 +00:00 committed by Oliver Woodman
parent 845f54a467
commit 173ddd6ee7
8 changed files with 147 additions and 46 deletions

View file

@ -15,6 +15,8 @@
* Offline:
* Speed up removal of segmented downloads
([#5136](https://github.com/google/ExoPlayer/issues/5136)).
* Add `setStreamKeys` method to factories of DASH, SmoothStreaming and HLS
media sources to simplify filtering by downloaded streams.
* Caching:
* Improve performance of `SimpleCache`.
* Cache data with unknown length by default. The previous flag to opt in to

View file

@ -48,7 +48,6 @@ import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.DecoderInitializationException;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer2.offline.FilteringManifestParser;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.source.BehindLiveWindowException;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
@ -58,11 +57,8 @@ import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.ads.AdsLoader;
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.source.hls.playlist.DefaultHlsPlaylistParserFactory;
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
@ -472,21 +468,19 @@ public class PlayerActivity extends Activity
@SuppressWarnings("unchecked")
private MediaSource buildMediaSource(Uri uri, @Nullable String overrideExtension) {
@ContentType int type = Util.inferContentType(uri, overrideExtension);
List<StreamKey> offlineStreamKeys = getOfflineStreamKeys(uri);
switch (type) {
case C.TYPE_DASH:
return new DashMediaSource.Factory(dataSourceFactory)
.setManifestParser(
new FilteringManifestParser<>(new DashManifestParser(), getOfflineStreamKeys(uri)))
.setStreamKeys(offlineStreamKeys)
.createMediaSource(uri);
case C.TYPE_SS:
return new SsMediaSource.Factory(dataSourceFactory)
.setManifestParser(
new FilteringManifestParser<>(new SsManifestParser(), getOfflineStreamKeys(uri)))
.setStreamKeys(offlineStreamKeys)
.createMediaSource(uri);
case C.TYPE_HLS:
return new HlsMediaSource.Factory(dataSourceFactory)
.setPlaylistParserFactory(
new DefaultHlsPlaylistParserFactory(getOfflineStreamKeys(uri)))
.setStreamKeys(offlineStreamKeys)
.createMediaSource(uri);
case C.TYPE_OTHER:
return new ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(uri);

View file

@ -16,22 +16,27 @@
package com.google.android.exoplayer2.offline;
import android.net.Uri;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.upstream.ParsingLoadable.Parser;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/** A manifest parser that includes only the streams identified by the given stream keys. */
/**
* A manifest parser that includes only the streams identified by the given stream keys.
*
* @param <T> The {@link FilterableManifest} type.
*/
public final class FilteringManifestParser<T extends FilterableManifest<T>> implements Parser<T> {
private final Parser<T> parser;
private final List<StreamKey> streamKeys;
private final Parser<? extends T> parser;
@Nullable private final List<StreamKey> streamKeys;
/**
* @param parser A parser for the manifest that will be filtered.
* @param streamKeys The stream keys. If null or empty then filtering will not occur.
*/
public FilteringManifestParser(Parser<T> parser, List<StreamKey> streamKeys) {
public FilteringManifestParser(Parser<? extends T> parser, @Nullable List<StreamKey> streamKeys) {
this.parser = parser;
this.streamKeys = streamKeys;
}

View file

@ -26,6 +26,8 @@ import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.offline.FilteringManifestParser;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.source.BaseMediaSource;
import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory;
import com.google.android.exoplayer2.source.DefaultCompositeSequenceableLoaderFactory;
@ -59,6 +61,7 @@ import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Matcher;
@ -75,15 +78,16 @@ public final class DashMediaSource extends BaseMediaSource {
public static final class Factory implements AdsMediaSource.MediaSourceFactory {
private final DashChunkSource.Factory chunkSourceFactory;
private final @Nullable DataSource.Factory manifestDataSourceFactory;
@Nullable private final DataSource.Factory manifestDataSourceFactory;
private @Nullable ParsingLoadable.Parser<? extends DashManifest> manifestParser;
@Nullable private ParsingLoadable.Parser<? extends DashManifest> manifestParser;
@Nullable private List<StreamKey> streamKeys;
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private long livePresentationDelayMs;
private boolean livePresentationDelayOverridesManifest;
private boolean isCreateCalled;
private @Nullable Object tag;
@Nullable private Object tag;
/**
* Creates a new factory for {@link DashMediaSource}s.
@ -210,6 +214,19 @@ public final class DashMediaSource extends BaseMediaSource {
return this;
}
/**
* Sets a list of {@link StreamKey stream keys} by which the manifest is filtered.
*
* @param streamKeys A list of {@link StreamKey stream keys}.
* @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
*/
public Factory setStreamKeys(List<StreamKey> streamKeys) {
Assertions.checkState(!isCreateCalled);
this.streamKeys = streamKeys;
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
@ -240,6 +257,9 @@ public final class DashMediaSource extends BaseMediaSource {
public DashMediaSource createMediaSource(DashManifest manifest) {
Assertions.checkArgument(!manifest.dynamic);
isCreateCalled = true;
if (streamKeys != null && !streamKeys.isEmpty()) {
manifest = manifest.copy(streamKeys);
}
return new DashMediaSource(
manifest,
/* manifestUri= */ null,
@ -281,6 +301,9 @@ public final class DashMediaSource extends BaseMediaSource {
if (manifestParser == null) {
manifestParser = new DashManifestParser();
}
if (streamKeys != null) {
manifestParser = new FilteringManifestParser<>(manifestParser, streamKeys);
}
return new DashMediaSource(
/* manifest= */ null,
Assertions.checkNotNull(manifestUri),

View file

@ -22,6 +22,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.source.BaseMediaSource;
import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory;
import com.google.android.exoplayer2.source.DefaultCompositeSequenceableLoaderFactory;
@ -34,6 +35,7 @@ import com.google.android.exoplayer2.source.SinglePeriodTimeline;
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
import com.google.android.exoplayer2.source.hls.playlist.DefaultHlsPlaylistParserFactory;
import com.google.android.exoplayer2.source.hls.playlist.DefaultHlsPlaylistTracker;
import com.google.android.exoplayer2.source.hls.playlist.FilteringHlsPlaylistParserFactory;
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.HlsPlaylistParser;
@ -64,12 +66,13 @@ public final class HlsMediaSource extends BaseMediaSource
private HlsExtractorFactory extractorFactory;
private HlsPlaylistParserFactory playlistParserFactory;
@Nullable private List<StreamKey> streamKeys;
private HlsPlaylistTracker.Factory playlistTrackerFactory;
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private boolean allowChunklessPreparation;
private boolean isCreateCalled;
private @Nullable Object tag;
@Nullable private Object tag;
/**
* Creates a new factory for {@link HlsMediaSource}s.
@ -164,8 +167,8 @@ public final class HlsMediaSource extends BaseMediaSource
}
/**
* Sets the factory from which playlist parsers will be obtained. The default value is created
* by calling {@link DefaultHlsPlaylistParserFactory#DefaultHlsPlaylistParserFactory()}.
* Sets the factory from which playlist parsers will be obtained. The default value is a {@link
* DefaultHlsPlaylistParserFactory}.
*
* @param playlistParserFactory An {@link HlsPlaylistParserFactory}.
* @return This factory, for convenience.
@ -177,6 +180,19 @@ public final class HlsMediaSource extends BaseMediaSource
return this;
}
/**
* Sets a list of {@link StreamKey stream keys} by which the playlists are filtered.
*
* @param streamKeys A list of {@link StreamKey stream keys}.
* @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
*/
public Factory setStreamKeys(List<StreamKey> streamKeys) {
Assertions.checkState(!isCreateCalled);
this.streamKeys = streamKeys;
return this;
}
/**
* Sets the {@link HlsPlaylistTracker} factory. The default value is {@link
* DefaultHlsPlaylistTracker#FACTORY}.
@ -232,6 +248,10 @@ public final class HlsMediaSource extends BaseMediaSource
@Override
public HlsMediaSource createMediaSource(Uri playlistUri) {
isCreateCalled = true;
if (streamKeys != null) {
playlistParserFactory =
new FilteringHlsPlaylistParserFactory(playlistParserFactory, streamKeys);
}
return new HlsMediaSource(
playlistUri,
hlsDataSourceFactory,

View file

@ -15,40 +15,19 @@
*/
package com.google.android.exoplayer2.source.hls.playlist;
import com.google.android.exoplayer2.offline.FilteringManifestParser;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.upstream.ParsingLoadable;
import java.util.Collections;
import java.util.List;
/** Default implementation for {@link HlsPlaylistParserFactory}. */
public final class DefaultHlsPlaylistParserFactory implements HlsPlaylistParserFactory {
private final List<StreamKey> streamKeys;
/** Creates an instance that does not filter any parsing results. */
public DefaultHlsPlaylistParserFactory() {
this(Collections.emptyList());
}
/**
* Creates an instance that filters the parsing results using the given {@code streamKeys}.
*
* @param streamKeys See {@link
* FilteringManifestParser#FilteringManifestParser(ParsingLoadable.Parser, List)}.
*/
public DefaultHlsPlaylistParserFactory(List<StreamKey> streamKeys) {
this.streamKeys = streamKeys;
}
@Override
public ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser() {
return new FilteringManifestParser<>(new HlsPlaylistParser(), streamKeys);
return new HlsPlaylistParser();
}
@Override
public ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser(
HlsMasterPlaylist masterPlaylist) {
return new FilteringManifestParser<>(new HlsPlaylistParser(masterPlaylist), streamKeys);
return new HlsPlaylistParser(masterPlaylist);
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2018 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.hls.playlist;
import com.google.android.exoplayer2.offline.FilteringManifestParser;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.upstream.ParsingLoadable;
import java.util.List;
/**
* A {@link HlsPlaylistParserFactory} that includes only the streams identified by the given stream
* keys.
*/
public final class FilteringHlsPlaylistParserFactory implements HlsPlaylistParserFactory {
private final HlsPlaylistParserFactory hlsPlaylistParserFactory;
private final List<StreamKey> streamKeys;
/**
* @param hlsPlaylistParserFactory A factory for the parsers of the playlists which will be
* filtered.
* @param streamKeys The stream keys. If null or empty then filtering will not occur.
*/
public FilteringHlsPlaylistParserFactory(
HlsPlaylistParserFactory hlsPlaylistParserFactory, List<StreamKey> streamKeys) {
this.hlsPlaylistParserFactory = hlsPlaylistParserFactory;
this.streamKeys = streamKeys;
}
@Override
public ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser() {
return new FilteringManifestParser<>(
hlsPlaylistParserFactory.createPlaylistParser(), streamKeys);
}
@Override
public ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser(
HlsMasterPlaylist masterPlaylist) {
return new FilteringManifestParser<>(
hlsPlaylistParserFactory.createPlaylistParser(masterPlaylist), streamKeys);
}
}

View file

@ -24,6 +24,8 @@ import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.offline.FilteringManifestParser;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.source.BaseMediaSource;
import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory;
import com.google.android.exoplayer2.source.DefaultCompositeSequenceableLoaderFactory;
@ -50,6 +52,7 @@ import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/** A SmoothStreaming {@link MediaSource}. */
public final class SsMediaSource extends BaseMediaSource
@ -63,14 +66,15 @@ public final class SsMediaSource extends BaseMediaSource
public static final class Factory implements AdsMediaSource.MediaSourceFactory {
private final SsChunkSource.Factory chunkSourceFactory;
private final @Nullable DataSource.Factory manifestDataSourceFactory;
@Nullable private final DataSource.Factory manifestDataSourceFactory;
private @Nullable ParsingLoadable.Parser<? extends SsManifest> manifestParser;
@Nullable private ParsingLoadable.Parser<? extends SsManifest> manifestParser;
@Nullable private List<StreamKey> streamKeys;
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private long livePresentationDelayMs;
private boolean isCreateCalled;
private @Nullable Object tag;
@Nullable private Object tag;
/**
* Creates a new factory for {@link SsMediaSource}s.
@ -178,6 +182,19 @@ public final class SsMediaSource extends BaseMediaSource
return this;
}
/**
* Sets a list of {@link StreamKey stream keys} by which the manifest is filtered.
*
* @param streamKeys A list of {@link StreamKey stream keys}.
* @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
*/
public Factory setStreamKeys(List<StreamKey> streamKeys) {
Assertions.checkState(!isCreateCalled);
this.streamKeys = streamKeys;
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
@ -208,6 +225,9 @@ public final class SsMediaSource extends BaseMediaSource
public SsMediaSource createMediaSource(SsManifest manifest) {
Assertions.checkArgument(!manifest.isLive);
isCreateCalled = true;
if (streamKeys != null && !streamKeys.isEmpty()) {
manifest = manifest.copy(streamKeys);
}
return new SsMediaSource(
manifest,
/* manifestUri= */ null,
@ -248,6 +268,9 @@ public final class SsMediaSource extends BaseMediaSource
if (manifestParser == null) {
manifestParser = new SsManifestParser();
}
if (streamKeys != null) {
manifestParser = new FilteringManifestParser<>(manifestParser, streamKeys);
}
return new SsMediaSource(
/* manifest= */ null,
Assertions.checkNotNull(manifestUri),