Add ability to play filtered manifests in PlayerActivity

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=189796293
This commit is contained in:
eguven 2018-03-20 13:15:01 -07:00 committed by Oliver Woodman
parent dfa31f02e3
commit 764d18f68f
6 changed files with 136 additions and 18 deletions

View File

@ -21,9 +21,9 @@ import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; 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.ads.AdsMediaSource;
import com.google.android.exoplayer2.source.dash.DashMediaSource; import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource; 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.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.DefaultSsChunkSource;
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; 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.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; 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.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.EventLogger; import com.google.android.exoplayer2.util.EventLogger;
import com.google.android.exoplayer2.util.ParcelableArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.net.CookieHandler; import java.net.CookieHandler;
import java.net.CookieManager; import java.net.CookieManager;
import java.net.CookiePolicy; import java.net.CookiePolicy;
import java.util.List;
import java.util.UUID; import java.util.UUID;
/** An activity that plays media using {@link SimpleExoPlayer}. */ /** 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 ACTION_VIEW = "com.google.android.exoplayer.demo.action.VIEW";
public static final String EXTENSION_EXTRA = "extension"; public static final String EXTENSION_EXTRA = "extension";
public static final String MANIFEST_FILTER_EXTRA = "manifest_filter";
public static final String ACTION_VIEW_LIST = public static final String ACTION_VIEW_LIST =
"com.google.android.exoplayer.demo.action.VIEW_LIST"; "com.google.android.exoplayer.demo.action.VIEW_LIST";
public static final String URI_LIST_EXTRA = "uri_list"; public static final String URI_LIST_EXTRA = "uri_list";
public static final String EXTENSION_LIST_EXTRA = "extension_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"; public static final String AD_TAG_URI_EXTRA = "ad_tag_uri";
// For backwards compatibility. // For backwards compatibility.
@ -318,9 +328,11 @@ public class PlayerActivity extends Activity
String action = intent.getAction(); String action = intent.getAction();
Uri[] uris; Uri[] uris;
String[] extensions; String[] extensions;
Parcelable[] manifestFilters;
if (ACTION_VIEW.equals(action)) { if (ACTION_VIEW.equals(action)) {
uris = new Uri[] {intent.getData()}; uris = new Uri[] {intent.getData()};
extensions = new String[] {intent.getStringExtra(EXTENSION_EXTRA)}; extensions = new String[] {intent.getStringExtra(EXTENSION_EXTRA)};
manifestFilters = new Parcelable[] {intent.getParcelableExtra(MANIFEST_FILTER_EXTRA)};
} else if (ACTION_VIEW_LIST.equals(action)) { } else if (ACTION_VIEW_LIST.equals(action)) {
String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA); String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA);
uris = new Uri[uriStrings.length]; uris = new Uri[uriStrings.length];
@ -331,6 +343,7 @@ public class PlayerActivity extends Activity
if (extensions == null) { if (extensions == null) {
extensions = new String[uriStrings.length]; extensions = new String[uriStrings.length];
} }
manifestFilters = intent.getParcelableArrayExtra(MANIFEST_FILTER_LIST_EXTRA);
} else { } else {
showToast(getString(R.string.unexpected_intent_action, action)); showToast(getString(R.string.unexpected_intent_action, action));
return; return;
@ -341,7 +354,9 @@ public class PlayerActivity extends Activity
} }
MediaSource[] mediaSources = new MediaSource[uris.length]; MediaSource[] mediaSources = new MediaSource[uris.length];
for (int i = 0; i < uris.length; i++) { 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 = mediaSource =
mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources); mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources);
@ -372,22 +387,32 @@ public class PlayerActivity extends Activity
updateButtonVisibilities(); updateButtonVisibilities();
} }
private MediaSource buildMediaSource(Uri uri, String overrideExtension) { private MediaSource buildMediaSource(Uri uri) {
@ContentType int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri) return buildMediaSource(uri, null, null);
: Util.inferContentType("." + overrideExtension); }
@SuppressWarnings("unchecked")
private MediaSource buildMediaSource(
Uri uri, @Nullable String overrideExtension, @Nullable List<?> manifestFilter) {
@ContentType int type = Util.inferContentType(uri, overrideExtension);
switch (type) { switch (type) {
case C.TYPE_DASH: case C.TYPE_DASH:
return new DashMediaSource.Factory( return new DashMediaSource.Factory(
new DefaultDashChunkSource.Factory(mediaDataSourceFactory), new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
buildDataSourceFactory(false)) buildDataSourceFactory(false))
.setManifestParser(
new FilteringDashManifestParser((List<RepresentationKey>) manifestFilter))
.createMediaSource(uri); .createMediaSource(uri);
case C.TYPE_SS: case C.TYPE_SS:
return new SsMediaSource.Factory( return new SsMediaSource.Factory(
new DefaultSsChunkSource.Factory(mediaDataSourceFactory), new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
buildDataSourceFactory(false)) buildDataSourceFactory(false))
.setManifestParser(new FilteringSsManifestParser((List<TrackKey>) manifestFilter))
.createMediaSource(uri); .createMediaSource(uri);
case C.TYPE_HLS: case C.TYPE_HLS:
return new HlsMediaSource.Factory(mediaDataSourceFactory).createMediaSource(uri); return new HlsMediaSource.Factory(mediaDataSourceFactory)
.setPlaylistParser(new FilteringHlsPlaylistParser((List<String>) manifestFilter))
.createMediaSource(uri);
case C.TYPE_OTHER: case C.TYPE_OTHER:
return new ExtractorMediaSource.Factory(mediaDataSourceFactory).createMediaSource(uri); return new ExtractorMediaSource.Factory(mediaDataSourceFactory).createMediaSource(uri);
default: { default: {
@ -483,7 +508,7 @@ public class PlayerActivity extends Activity
new AdsMediaSource.MediaSourceFactory() { new AdsMediaSource.MediaSourceFactory() {
@Override @Override
public MediaSource createMediaSource(Uri uri) { public MediaSource createMediaSource(Uri uri) {
return PlayerActivity.this.buildMediaSource(uri, /* overrideExtension= */ null); return PlayerActivity.this.buildMediaSource(uri);
} }
@Override @Override

View File

@ -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<V extends Parcelable> implements Parcelable {
@SuppressWarnings("rawtypes") // V cannot be obtained from static context
public static final Creator<ParcelableArray> CREATOR =
new Creator<ParcelableArray>() {
@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<V> 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);
}
}

View File

@ -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}. * Makes a best guess to infer the type from a {@link Uri}.
* *

View File

@ -16,10 +16,11 @@
package com.google.android.exoplayer2.source.dash.manifest; package com.google.android.exoplayer2.source.dash.manifest;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.upstream.ParsingLoadable.Parser; import com.google.android.exoplayer2.upstream.ParsingLoadable.Parser;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.List;
/** /**
* A parser of media presentation description files which includes only the representations * 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<DashManifest> { public final class FilteringDashManifestParser implements Parser<DashManifest> {
private final DashManifestParser dashManifestParser; private final DashManifestParser dashManifestParser;
private final ArrayList<RepresentationKey> filter; private final List<RepresentationKey> filter;
/** @param filter The representation keys that should be retained in the parsed manifests. */ /**
public FilteringDashManifestParser(ArrayList<RepresentationKey> filter) { * @param filter The representation keys that should be retained in the parsed manifests. If null,
* all representation are retained.
*/
public FilteringDashManifestParser(@Nullable List<RepresentationKey> filter) {
this.dashManifestParser = new DashManifestParser(); this.dashManifestParser = new DashManifestParser();
this.filter = filter; this.filter = filter;
} }
@Override @Override
public DashManifest parse(Uri uri, InputStream inputStream) throws IOException { 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;
} }
} }

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.hls.playlist; package com.google.android.exoplayer2.source.hls.playlist;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.upstream.ParsingLoadable.Parser; import com.google.android.exoplayer2.upstream.ParsingLoadable.Parser;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -27,8 +28,11 @@ public final class FilteringHlsPlaylistParser implements Parser<HlsPlaylist> {
private final HlsPlaylistParser hlsPlaylistParser; private final HlsPlaylistParser hlsPlaylistParser;
private final List<String> filter; private final List<String> filter;
/** @param filter The urls to renditions that should be retained in the parsed playlists. */ /**
public FilteringHlsPlaylistParser(List<String> 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<String> filter) {
this.hlsPlaylistParser = new HlsPlaylistParser(); this.hlsPlaylistParser = new HlsPlaylistParser();
this.filter = filter; this.filter = filter;
} }
@ -37,7 +41,8 @@ public final class FilteringHlsPlaylistParser implements Parser<HlsPlaylist> {
public HlsPlaylist parse(Uri uri, InputStream inputStream) throws IOException { public HlsPlaylist parse(Uri uri, InputStream inputStream) throws IOException {
HlsPlaylist hlsPlaylist = hlsPlaylistParser.parse(uri, inputStream); HlsPlaylist hlsPlaylist = hlsPlaylistParser.parse(uri, inputStream);
if (hlsPlaylist instanceof HlsMasterPlaylist) { if (hlsPlaylist instanceof HlsMasterPlaylist) {
return ((HlsMasterPlaylist) hlsPlaylist).copy(filter); HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) hlsPlaylist;
return filter != null ? masterPlaylist.copy(filter) : masterPlaylist;
} else { } else {
return hlsPlaylist; return hlsPlaylist;
} }

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.smoothstreaming.manifest; package com.google.android.exoplayer2.source.smoothstreaming.manifest;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.upstream.ParsingLoadable.Parser; import com.google.android.exoplayer2.upstream.ParsingLoadable.Parser;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -29,14 +30,18 @@ public final class FilteringSsManifestParser implements Parser<SsManifest> {
private final SsManifestParser ssManifestParser; private final SsManifestParser ssManifestParser;
private final List<TrackKey> filter; private final List<TrackKey> filter;
/** @param filter The track keys that should be retained in the parsed manifests. */ /**
public FilteringSsManifestParser(List<TrackKey> filter) { * @param filter The track keys that should be retained in the parsed manifests. If null, all
* tracks are retained.
*/
public FilteringSsManifestParser(@Nullable List<TrackKey> filter) {
this.ssManifestParser = new SsManifestParser(); this.ssManifestParser = new SsManifestParser();
this.filter = filter; this.filter = filter;
} }
@Override @Override
public SsManifest parse(Uri uri, InputStream inputStream) throws IOException { 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;
} }
} }