From 764d18f68f94eb9588be740051f03ad46f2a78a4 Mon Sep 17 00:00:00 2001 From: eguven Date: Tue, 20 Mar 2018 13:15:01 -0700 Subject: [PATCH] Add ability to play filtered manifests in PlayerActivity ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189796293 --- .../exoplayer2/demo/PlayerActivity.java | 39 +++++++++-- .../exoplayer2/util/ParcelableArray.java | 64 +++++++++++++++++++ .../google/android/exoplayer2/util/Util.java | 14 ++++ .../manifest/FilteringDashManifestParser.java | 15 +++-- .../playlist/FilteringHlsPlaylistParser.java | 11 +++- .../manifest/FilteringSsManifestParser.java | 11 +++- 6 files changed, 136 insertions(+), 18 deletions(-) create mode 100644 library/core/src/main/java/com/google/android/exoplayer2/util/ParcelableArray.java diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 0219b62789..e251a760a4 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -21,9 +21,9 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.text.TextUtils; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; @@ -58,9 +58,14 @@ 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.DefaultDashChunkSource; +import com.google.android.exoplayer2.source.dash.manifest.FilteringDashManifestParser; +import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey; import com.google.android.exoplayer2.source.hls.HlsMediaSource; +import com.google.android.exoplayer2.source.hls.playlist.FilteringHlsPlaylistParser; import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource; import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; +import com.google.android.exoplayer2.source.smoothstreaming.manifest.FilteringSsManifestParser; +import com.google.android.exoplayer2.source.smoothstreaming.manifest.TrackKey; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; @@ -73,11 +78,13 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.util.EventLogger; +import com.google.android.exoplayer2.util.ParcelableArray; import com.google.android.exoplayer2.util.Util; import java.lang.reflect.Constructor; import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookiePolicy; +import java.util.List; import java.util.UUID; /** An activity that plays media using {@link SimpleExoPlayer}. */ @@ -92,11 +99,14 @@ public class PlayerActivity extends Activity public static final String ACTION_VIEW = "com.google.android.exoplayer.demo.action.VIEW"; public static final String EXTENSION_EXTRA = "extension"; + public static final String MANIFEST_FILTER_EXTRA = "manifest_filter"; public static final String ACTION_VIEW_LIST = "com.google.android.exoplayer.demo.action.VIEW_LIST"; public static final String URI_LIST_EXTRA = "uri_list"; public static final String EXTENSION_LIST_EXTRA = "extension_list"; + public static final String MANIFEST_FILTER_LIST_EXTRA = "manifest_filter_list"; + public static final String AD_TAG_URI_EXTRA = "ad_tag_uri"; // For backwards compatibility. @@ -318,9 +328,11 @@ public class PlayerActivity extends Activity String action = intent.getAction(); Uri[] uris; String[] extensions; + Parcelable[] manifestFilters; if (ACTION_VIEW.equals(action)) { uris = new Uri[] {intent.getData()}; extensions = new String[] {intent.getStringExtra(EXTENSION_EXTRA)}; + manifestFilters = new Parcelable[] {intent.getParcelableExtra(MANIFEST_FILTER_EXTRA)}; } else if (ACTION_VIEW_LIST.equals(action)) { String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA); uris = new Uri[uriStrings.length]; @@ -331,6 +343,7 @@ public class PlayerActivity extends Activity if (extensions == null) { extensions = new String[uriStrings.length]; } + manifestFilters = intent.getParcelableArrayExtra(MANIFEST_FILTER_LIST_EXTRA); } else { showToast(getString(R.string.unexpected_intent_action, action)); return; @@ -341,7 +354,9 @@ public class PlayerActivity extends Activity } MediaSource[] mediaSources = new MediaSource[uris.length]; for (int i = 0; i < uris.length; i++) { - mediaSources[i] = buildMediaSource(uris[i], extensions[i]); + ParcelableArray manifestFilter = (ParcelableArray) manifestFilters[i]; + List filter = manifestFilter != null ? manifestFilter.asList() : null; + mediaSources[i] = buildMediaSource(uris[i], extensions[i], filter); } mediaSource = mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources); @@ -372,22 +387,32 @@ public class PlayerActivity extends Activity updateButtonVisibilities(); } - private MediaSource buildMediaSource(Uri uri, String overrideExtension) { - @ContentType int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri) - : Util.inferContentType("." + overrideExtension); + private MediaSource buildMediaSource(Uri uri) { + return buildMediaSource(uri, null, null); + } + + @SuppressWarnings("unchecked") + private MediaSource buildMediaSource( + Uri uri, @Nullable String overrideExtension, @Nullable List manifestFilter) { + @ContentType int type = Util.inferContentType(uri, overrideExtension); switch (type) { case C.TYPE_DASH: return new DashMediaSource.Factory( new DefaultDashChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false)) + .setManifestParser( + new FilteringDashManifestParser((List) manifestFilter)) .createMediaSource(uri); case C.TYPE_SS: return new SsMediaSource.Factory( new DefaultSsChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false)) + .setManifestParser(new FilteringSsManifestParser((List) manifestFilter)) .createMediaSource(uri); case C.TYPE_HLS: - return new HlsMediaSource.Factory(mediaDataSourceFactory).createMediaSource(uri); + return new HlsMediaSource.Factory(mediaDataSourceFactory) + .setPlaylistParser(new FilteringHlsPlaylistParser((List) manifestFilter)) + .createMediaSource(uri); case C.TYPE_OTHER: return new ExtractorMediaSource.Factory(mediaDataSourceFactory).createMediaSource(uri); default: { @@ -483,7 +508,7 @@ public class PlayerActivity extends Activity new AdsMediaSource.MediaSourceFactory() { @Override public MediaSource createMediaSource(Uri uri) { - return PlayerActivity.this.buildMediaSource(uri, /* overrideExtension= */ null); + return PlayerActivity.this.buildMediaSource(uri); } @Override diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/ParcelableArray.java b/library/core/src/main/java/com/google/android/exoplayer2/util/ParcelableArray.java new file mode 100644 index 0000000000..3463f8f813 --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/ParcelableArray.java @@ -0,0 +1,64 @@ +/* + * 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.util; + +import android.os.Parcel; +import android.os.Parcelable; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** A {@link android.os.Parcelable} wrapper around an array. */ +public final class ParcelableArray implements Parcelable { + + @SuppressWarnings("rawtypes") // V cannot be obtained from static context + public static final Creator CREATOR = + new Creator() { + @SuppressWarnings("unchecked") + @Override + public ParcelableArray createFromParcel(Parcel in) { + ClassLoader classLoader = ParcelableArray.class.getClassLoader(); + Parcelable[] elements = in.readParcelableArray(classLoader); + return new ParcelableArray(elements); + } + + @Override + public ParcelableArray[] newArray(int size) { + return new ParcelableArray[size]; + } + }; + + private final V[] elements; + + public ParcelableArray(V[] elements) { + this.elements = elements; + } + + /** Returns an unmodifiable list containing all elements. */ + public List asList() { + return Collections.unmodifiableList(Arrays.asList(elements)); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelableArray(elements, flags); + } +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java index be5e140c5d..f203b98533 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java @@ -1093,6 +1093,20 @@ public final class Util { } } + /** + * Makes a best guess to infer the type from a {@link Uri}. + * + * @param uri The {@link Uri}. + * @param overrideExtension If not null, used to infer the type. + * @return The content type. + */ + @C.ContentType + public static int inferContentType(Uri uri, String overrideExtension) { + return TextUtils.isEmpty(overrideExtension) + ? inferContentType(uri) + : inferContentType("." + overrideExtension); + } + /** * Makes a best guess to infer the type from a {@link Uri}. * diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/FilteringDashManifestParser.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/FilteringDashManifestParser.java index 84c899f6c2..4e45a31183 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/FilteringDashManifestParser.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/FilteringDashManifestParser.java @@ -16,10 +16,11 @@ package com.google.android.exoplayer2.source.dash.manifest; 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.ArrayList; +import java.util.List; /** * A parser of media presentation description files which includes only the representations @@ -28,16 +29,20 @@ import java.util.ArrayList; public final class FilteringDashManifestParser implements Parser { private final DashManifestParser dashManifestParser; - private final ArrayList filter; + private final List filter; - /** @param filter The representation keys that should be retained in the parsed manifests. */ - public FilteringDashManifestParser(ArrayList filter) { + /** + * @param filter The representation keys that should be retained in the parsed manifests. If null, + * all representation are retained. + */ + public FilteringDashManifestParser(@Nullable List filter) { this.dashManifestParser = new DashManifestParser(); this.filter = filter; } @Override public DashManifest parse(Uri uri, InputStream inputStream) throws IOException { - return dashManifestParser.parse(uri, inputStream).copy(filter); + DashManifest manifest = dashManifestParser.parse(uri, inputStream); + return filter != null ? manifest.copy(filter) : manifest; } } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/FilteringHlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/FilteringHlsPlaylistParser.java index 24fa0df7d1..db490f2518 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/FilteringHlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/FilteringHlsPlaylistParser.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.source.hls.playlist; 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; @@ -27,8 +28,11 @@ public final class FilteringHlsPlaylistParser implements Parser { private final HlsPlaylistParser hlsPlaylistParser; private final List filter; - /** @param filter The urls to renditions that should be retained in the parsed playlists. */ - public FilteringHlsPlaylistParser(List filter) { + /** + * @param filter The urls to renditions that should be retained in the parsed playlists. If null, + * all renditions are retained. + */ + public FilteringHlsPlaylistParser(@Nullable List filter) { this.hlsPlaylistParser = new HlsPlaylistParser(); this.filter = filter; } @@ -37,7 +41,8 @@ public final class FilteringHlsPlaylistParser implements Parser { public HlsPlaylist parse(Uri uri, InputStream inputStream) throws IOException { HlsPlaylist hlsPlaylist = hlsPlaylistParser.parse(uri, inputStream); if (hlsPlaylist instanceof HlsMasterPlaylist) { - return ((HlsMasterPlaylist) hlsPlaylist).copy(filter); + HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) hlsPlaylist; + return filter != null ? masterPlaylist.copy(filter) : masterPlaylist; } else { return hlsPlaylist; } diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/FilteringSsManifestParser.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/FilteringSsManifestParser.java index eed040df97..3f88247ac9 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/FilteringSsManifestParser.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/FilteringSsManifestParser.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.source.smoothstreaming.manifest; 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; @@ -29,14 +30,18 @@ public final class FilteringSsManifestParser implements Parser { private final SsManifestParser ssManifestParser; private final List filter; - /** @param filter The track keys that should be retained in the parsed manifests. */ - public FilteringSsManifestParser(List filter) { + /** + * @param filter The track keys that should be retained in the parsed manifests. If null, all + * tracks are retained. + */ + public FilteringSsManifestParser(@Nullable List filter) { this.ssManifestParser = new SsManifestParser(); this.filter = filter; } @Override public SsManifest parse(Uri uri, InputStream inputStream) throws IOException { - return ssManifestParser.parse(uri, inputStream).copy(filter); + SsManifest manifest = ssManifestParser.parse(uri, inputStream); + return filter != null ? manifest.copy(filter) : manifest; } }