Remove Sample from SampleChooserActivity

Unmarshal from json to MediaItem instead of Sample. Further the playlist
of MediaItems is converted to Intent extras which are read by the
PlayerActivity.

PiperOrigin-RevId: 308141231
This commit is contained in:
bachinger 2020-04-23 23:23:17 +01:00 committed by Ian Baker
parent 7c3b461b64
commit 1323dd63d5
6 changed files with 289 additions and 349 deletions

View file

@ -15,6 +15,8 @@
*/
package com.google.android.exoplayer2.demo;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import android.content.Context;
import android.content.DialogInterface;
import android.net.Uri;
@ -23,8 +25,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.demo.Sample.UriSample;
import com.google.android.exoplayer2.offline.Download;
import com.google.android.exoplayer2.offline.DownloadCursor;
import com.google.android.exoplayer2.offline.DownloadHelper;
@ -82,8 +84,8 @@ public class DownloadTracker {
listeners.remove(listener);
}
public boolean isDownloaded(Uri uri) {
Download download = downloads.get(uri);
public boolean isDownloaded(MediaItem mediaItem) {
Download download = downloads.get(checkNotNull(mediaItem.playbackProperties).sourceUri);
return download != null && download.state != Download.STATE_FAILED;
}
@ -93,8 +95,8 @@ public class DownloadTracker {
}
public void toggleDownload(
FragmentManager fragmentManager, UriSample sample, RenderersFactory renderersFactory) {
Download download = downloads.get(sample.uri);
FragmentManager fragmentManager, MediaItem mediaItem, RenderersFactory renderersFactory) {
Download download = downloads.get(checkNotNull(mediaItem.playbackProperties).sourceUri);
if (download != null) {
DownloadService.sendRemoveDownload(
context, DemoDownloadService.class, download.request.id, /* foreground= */ false);
@ -104,9 +106,7 @@ public class DownloadTracker {
}
startDownloadDialogHelper =
new StartDownloadDialogHelper(
fragmentManager,
getDownloadHelper(sample.uri, sample.extension, renderersFactory),
sample);
fragmentManager, getDownloadHelper(mediaItem, renderersFactory), mediaItem);
}
}
@ -121,18 +121,24 @@ public class DownloadTracker {
}
}
private DownloadHelper getDownloadHelper(
Uri uri, String extension, RenderersFactory renderersFactory) {
int type = Util.inferContentType(uri, extension);
private DownloadHelper getDownloadHelper(MediaItem mediaItem, RenderersFactory renderersFactory) {
MediaItem.PlaybackProperties playbackProperties = checkNotNull(mediaItem.playbackProperties);
@C.ContentType
int type =
Util.inferContentTypeWithMimeType(
playbackProperties.sourceUri, playbackProperties.mimeType);
switch (type) {
case C.TYPE_DASH:
return DownloadHelper.forDash(context, uri, dataSourceFactory, renderersFactory);
return DownloadHelper.forDash(
context, playbackProperties.sourceUri, dataSourceFactory, renderersFactory);
case C.TYPE_SS:
return DownloadHelper.forSmoothStreaming(context, uri, dataSourceFactory, renderersFactory);
return DownloadHelper.forSmoothStreaming(
context, playbackProperties.sourceUri, dataSourceFactory, renderersFactory);
case C.TYPE_HLS:
return DownloadHelper.forHls(context, uri, dataSourceFactory, renderersFactory);
return DownloadHelper.forHls(
context, playbackProperties.sourceUri, dataSourceFactory, renderersFactory);
case C.TYPE_OTHER:
return DownloadHelper.forProgressive(context, uri);
return DownloadHelper.forProgressive(context, playbackProperties.sourceUri);
default:
throw new IllegalStateException("Unsupported type: " + type);
}
@ -166,16 +172,16 @@ public class DownloadTracker {
private final FragmentManager fragmentManager;
private final DownloadHelper downloadHelper;
private final UriSample sample;
private final MediaItem mediaItem;
private TrackSelectionDialog trackSelectionDialog;
private MappedTrackInfo mappedTrackInfo;
public StartDownloadDialogHelper(
FragmentManager fragmentManager, DownloadHelper downloadHelper, UriSample sample) {
FragmentManager fragmentManager, DownloadHelper downloadHelper, MediaItem mediaItem) {
this.fragmentManager = fragmentManager;
this.downloadHelper = downloadHelper;
this.sample = sample;
this.mediaItem = mediaItem;
downloadHelper.prepare(this);
}
@ -270,7 +276,8 @@ public class DownloadTracker {
}
private DownloadRequest buildDownloadRequest() {
return downloadHelper.getDownloadRequest(Util.getUtf8Bytes(sample.name));
return downloadHelper.getDownloadRequest(
Util.getUtf8Bytes(checkNotNull(mediaItem.mediaMetadata.title)));
}
}
}

View file

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.demo;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import android.content.Intent;
import android.net.Uri;
@ -23,6 +24,7 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.offline.DownloadRequest;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
@ -32,9 +34,24 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
/** Util to read from an intent. */
/** Util to read from and populate an intent. */
public class IntentUtil {
/** A tag to hold custom playback configuration attributes. */
public static class Tag {
/** Whether the stream is a live stream. */
public final boolean isLive;
/** The spherical stereo mode or null. */
@Nullable public final String sphericalStereoMode;
/** Creates an instance. */
public Tag(boolean isLive, @Nullable String sphericalStereoMode) {
this.isLive = isLive;
this.sphericalStereoMode = sphericalStereoMode;
}
}
// Actions.
public static final String ACTION_VIEW = "com.google.android.exoplayer.demo.action.VIEW";
@ -57,8 +74,10 @@ public class IntentUtil {
// Media item configuration extras.
public static final String URI_EXTRA = "uri";
public static final String EXTENSION_EXTRA = "extension";
public static final String IS_LIVE_EXTRA = "is_live";
public static final String MIME_TYPE_EXTRA = "mime_type";
// For backwards compatibility only.
public static final String EXTENSION_EXTRA = "extension";
public static final String DRM_SCHEME_EXTRA = "drm_scheme";
public static final String DRM_LICENSE_URL_EXTRA = "drm_license_url";
@ -100,15 +119,56 @@ public class IntentUtil {
return mediaItems;
}
/** Populates the intent with the given list of {@link MediaItem media items}. */
public static void addToIntent(List<MediaItem> mediaItems, Intent intent) {
Assertions.checkArgument(!mediaItems.isEmpty());
if (mediaItems.size() == 1) {
MediaItem.PlaybackProperties playbackProperties =
checkNotNull(mediaItems.get(0).playbackProperties);
intent.setAction(IntentUtil.ACTION_VIEW).setData(playbackProperties.sourceUri);
addPlaybackPropertiesToIntent(playbackProperties, intent, /* extrasKeySuffix= */ "");
} else {
intent.setAction(IntentUtil.ACTION_VIEW_LIST);
for (int i = 0; i < mediaItems.size(); i++) {
MediaItem.PlaybackProperties playbackProperties =
checkNotNull(mediaItems.get(i).playbackProperties);
intent.putExtra(IntentUtil.URI_EXTRA + ("_" + i), playbackProperties.sourceUri.toString());
addPlaybackPropertiesToIntent(playbackProperties, intent, /* extrasKeySuffix= */ "_" + i);
}
}
}
/** Makes a best guess to infer the MIME type from a {@link Uri} and an optional extension. */
@Nullable
public static String inferAdaptiveStreamMimeType(Uri uri, @Nullable String extension) {
@C.ContentType int contentType = Util.inferContentType(uri, extension);
switch (contentType) {
case C.TYPE_DASH:
return MimeTypes.APPLICATION_MPD;
case C.TYPE_HLS:
return MimeTypes.APPLICATION_M3U8;
case C.TYPE_SS:
return MimeTypes.APPLICATION_SS;
case C.TYPE_OTHER:
default:
return null;
}
}
private static MediaItem createMediaItemFromIntent(
Uri uri, Intent intent, String extrasKeySuffix, @Nullable DownloadRequest downloadRequest) {
String extension = intent.getStringExtra(EXTENSION_EXTRA + extrasKeySuffix);
String mimeType = intent.getStringExtra(MIME_TYPE_EXTRA + extrasKeySuffix);
if (mimeType == null) {
// Try to use extension for backwards compatibility.
String extension = intent.getStringExtra(EXTENSION_EXTRA + extrasKeySuffix);
mimeType = inferAdaptiveStreamMimeType(uri, extension);
}
MediaItem.Builder builder =
new MediaItem.Builder()
.setSourceUri(uri)
.setStreamKeys(downloadRequest != null ? downloadRequest.streamKeys : null)
.setCustomCacheKey(downloadRequest != null ? downloadRequest.customCacheKey : null)
.setMimeType(inferAdaptiveStreamMimeType(uri, extension))
.setMimeType(mimeType)
.setAdTagUri(intent.getStringExtra(AD_TAG_URI_EXTRA + extrasKeySuffix))
.setSubtitles(createSubtitlesFromIntent(intent, extrasKeySuffix));
return populateDrmPropertiesFromIntent(builder, intent, extrasKeySuffix).build();
@ -178,19 +238,57 @@ public class IntentUtil {
return new ArrayList<>(trackTypes);
}
@Nullable
private static String inferAdaptiveStreamMimeType(Uri uri, @Nullable String extension) {
@C.ContentType int contentType = Util.inferContentType(uri, extension);
switch (contentType) {
case C.TYPE_DASH:
return MimeTypes.APPLICATION_MPD;
case C.TYPE_HLS:
return MimeTypes.APPLICATION_M3U8;
case C.TYPE_SS:
return MimeTypes.APPLICATION_SS;
case C.TYPE_OTHER:
default:
return null;
private static void addPlaybackPropertiesToIntent(
MediaItem.PlaybackProperties playbackProperties, Intent intent, String extrasKeySuffix) {
boolean isLive = false;
String sphericalStereoMode = null;
if (playbackProperties.tag instanceof Tag) {
Tag tag = (Tag) playbackProperties.tag;
isLive = tag.isLive;
sphericalStereoMode = tag.sphericalStereoMode;
}
intent
.putExtra(MIME_TYPE_EXTRA + extrasKeySuffix, playbackProperties.mimeType)
.putExtra(
AD_TAG_URI_EXTRA + extrasKeySuffix,
playbackProperties.adTagUri != null ? playbackProperties.adTagUri.toString() : null)
.putExtra(IS_LIVE_EXTRA + extrasKeySuffix, isLive)
.putExtra(SPHERICAL_STEREO_MODE_EXTRA, sphericalStereoMode);
if (playbackProperties.drmConfiguration != null) {
addDrmConfigurationToIntent(playbackProperties.drmConfiguration, intent, extrasKeySuffix);
}
if (!playbackProperties.subtitles.isEmpty()) {
checkState(playbackProperties.subtitles.size() == 1);
MediaItem.Subtitle subtitle = playbackProperties.subtitles.get(0);
intent.putExtra(SUBTITLE_URI_EXTRA + extrasKeySuffix, subtitle.uri.toString());
intent.putExtra(SUBTITLE_MIME_TYPE_EXTRA + extrasKeySuffix, subtitle.mimeType);
intent.putExtra(SUBTITLE_LANGUAGE_EXTRA + extrasKeySuffix, subtitle.language);
}
}
private static void addDrmConfigurationToIntent(
MediaItem.DrmConfiguration drmConfiguration, Intent intent, String extrasKeySuffix) {
intent.putExtra(DRM_SCHEME_EXTRA + extrasKeySuffix, drmConfiguration.uuid.toString());
intent.putExtra(
DRM_LICENSE_URL_EXTRA + extrasKeySuffix,
checkNotNull(drmConfiguration.licenseUri).toString());
intent.putExtra(DRM_MULTI_SESSION_EXTRA + extrasKeySuffix, drmConfiguration.multiSession);
String[] drmKeyRequestProperties = new String[drmConfiguration.requestHeaders.size() * 2];
int index = 0;
for (Map.Entry<String, String> entry : drmConfiguration.requestHeaders.entrySet()) {
drmKeyRequestProperties[index++] = entry.getKey();
drmKeyRequestProperties[index++] = entry.getValue();
}
intent.putExtra(DRM_KEY_REQUEST_PROPERTIES_EXTRA + extrasKeySuffix, drmKeyRequestProperties);
ArrayList<String> typeStrings = new ArrayList<>();
for (int type : drmConfiguration.sessionForClearTypes) {
// Only audio and video are supported.
Assertions.checkState(type == C.TRACK_TYPE_AUDIO || type == C.TRACK_TYPE_VIDEO);
typeStrings.add(type == C.TRACK_TYPE_AUDIO ? "audio" : "video");
}
intent.putExtra(
DRM_SESSION_FOR_CLEAR_TYPES_EXTRA + extrasKeySuffix, typeStrings.toArray(new String[0]));
}
}

View file

@ -1,191 +0,0 @@
/*
* Copyright (C) 2019 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.demo;
import android.content.Intent;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.UUID;
/* package */ abstract class Sample {
public static final class UriSample extends Sample {
public final Uri uri;
public final String extension;
public final boolean isLive;
public final DrmInfo drmInfo;
public final Uri adTagUri;
@Nullable public final String sphericalStereoMode;
@Nullable SubtitleInfo subtitleInfo;
public UriSample(
String name,
Uri uri,
String extension,
boolean isLive,
DrmInfo drmInfo,
Uri adTagUri,
@Nullable String sphericalStereoMode,
@Nullable SubtitleInfo subtitleInfo) {
super(name);
this.uri = uri;
this.extension = extension;
this.isLive = isLive;
this.drmInfo = drmInfo;
this.adTagUri = adTagUri;
this.sphericalStereoMode = sphericalStereoMode;
this.subtitleInfo = subtitleInfo;
}
@Override
public void addToIntent(Intent intent) {
intent.setAction(IntentUtil.ACTION_VIEW).setData(uri);
intent.putExtra(IntentUtil.IS_LIVE_EXTRA, isLive);
intent.putExtra(IntentUtil.SPHERICAL_STEREO_MODE_EXTRA, sphericalStereoMode);
addPlayerConfigToIntent(intent, /* extrasKeySuffix= */ "");
}
public void addToPlaylistIntent(Intent intent, String extrasKeySuffix) {
intent.putExtra(IntentUtil.URI_EXTRA + extrasKeySuffix, uri.toString());
intent.putExtra(IntentUtil.IS_LIVE_EXTRA + extrasKeySuffix, isLive);
addPlayerConfigToIntent(intent, extrasKeySuffix);
}
private void addPlayerConfigToIntent(Intent intent, String extrasKeySuffix) {
intent
.putExtra(IntentUtil.EXTENSION_EXTRA + extrasKeySuffix, extension)
.putExtra(
IntentUtil.AD_TAG_URI_EXTRA + extrasKeySuffix,
adTagUri != null ? adTagUri.toString() : null);
if (drmInfo != null) {
drmInfo.addToIntent(intent, extrasKeySuffix);
}
if (subtitleInfo != null) {
subtitleInfo.addToIntent(intent, extrasKeySuffix);
}
}
}
public static final class PlaylistSample extends Sample {
public final UriSample[] children;
public PlaylistSample(String name, UriSample... children) {
super(name);
this.children = children;
}
@Override
public void addToIntent(Intent intent) {
intent.setAction(IntentUtil.ACTION_VIEW_LIST);
for (int i = 0; i < children.length; i++) {
children[i].addToPlaylistIntent(intent, /* extrasKeySuffix= */ "_" + i);
}
}
}
public static final class DrmInfo {
public final UUID drmScheme;
public final String drmLicenseUrl;
public final String[] drmKeyRequestProperties;
public final int[] drmSessionForClearTypes;
public final boolean drmMultiSession;
public DrmInfo(
UUID drmScheme,
String drmLicenseUrl,
String[] drmKeyRequestProperties,
int[] drmSessionForClearTypes,
boolean drmMultiSession) {
this.drmScheme = drmScheme;
this.drmLicenseUrl = drmLicenseUrl;
this.drmKeyRequestProperties = drmKeyRequestProperties;
this.drmSessionForClearTypes = drmSessionForClearTypes;
this.drmMultiSession = drmMultiSession;
}
public void addToIntent(Intent intent, String extrasKeySuffix) {
Assertions.checkNotNull(intent);
intent.putExtra(IntentUtil.DRM_SCHEME_EXTRA + extrasKeySuffix, drmScheme.toString());
intent.putExtra(IntentUtil.DRM_LICENSE_URL_EXTRA + extrasKeySuffix, drmLicenseUrl);
intent.putExtra(
IntentUtil.DRM_KEY_REQUEST_PROPERTIES_EXTRA + extrasKeySuffix, drmKeyRequestProperties);
ArrayList<String> typeStrings = new ArrayList<>();
for (int type : drmSessionForClearTypes) {
// Only audio and video are supported.
typeStrings.add(type == C.TRACK_TYPE_AUDIO ? "audio" : "video");
}
intent.putExtra(
IntentUtil.DRM_SESSION_FOR_CLEAR_TYPES_EXTRA + extrasKeySuffix,
typeStrings.toArray(new String[0]));
intent.putExtra(IntentUtil.DRM_MULTI_SESSION_EXTRA + extrasKeySuffix, drmMultiSession);
}
}
public static final class SubtitleInfo {
public final Uri uri;
public final String mimeType;
@Nullable public final String language;
public SubtitleInfo(Uri uri, String mimeType, @Nullable String language) {
this.uri = Assertions.checkNotNull(uri);
this.mimeType = Assertions.checkNotNull(mimeType);
this.language = language;
}
public void addToIntent(Intent intent, String extrasKeySuffix) {
intent.putExtra(IntentUtil.SUBTITLE_URI_EXTRA + extrasKeySuffix, uri.toString());
intent.putExtra(IntentUtil.SUBTITLE_MIME_TYPE_EXTRA + extrasKeySuffix, mimeType);
intent.putExtra(IntentUtil.SUBTITLE_LANGUAGE_EXTRA + extrasKeySuffix, language);
}
}
public static int[] toTrackTypeArray(@Nullable String[] trackTypeStringsArray) {
if (trackTypeStringsArray == null) {
return new int[0];
}
HashSet<Integer> trackTypes = new HashSet<>();
for (String trackTypeString : trackTypeStringsArray) {
switch (Util.toLowerInvariant(trackTypeString)) {
case "audio":
trackTypes.add(C.TRACK_TYPE_AUDIO);
break;
case "video":
trackTypes.add(C.TRACK_TYPE_VIDEO);
break;
default:
throw new IllegalArgumentException("Invalid track type: " + trackTypeString);
}
}
return Util.toArray(new ArrayList<>(trackTypes));
}
public final String name;
public Sample(String name) {
this.name = name;
}
public abstract void addToIntent(Intent intent);
}

View file

@ -15,10 +15,13 @@
*/
package com.google.android.exoplayer2.demo;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.AsyncTask;
@ -39,11 +42,11 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.MediaMetadata;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.demo.Sample.DrmInfo;
import com.google.android.exoplayer2.demo.Sample.PlaylistSample;
import com.google.android.exoplayer2.demo.Sample.UriSample;
import com.google.android.exoplayer2.offline.DownloadService;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSourceInputStream;
@ -58,7 +61,10 @@ import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/** An activity for selecting from a list of media samples. */
public class SampleChooserActivity extends AppCompatActivity
@ -182,7 +188,7 @@ public class SampleChooserActivity extends AppCompatActivity
}
private void loadSample() {
Assertions.checkNotNull(uris);
checkNotNull(uris);
for (int i = 0; i < uris.length; i++) {
Uri uri = Uri.parse(uris[i]);
@ -195,12 +201,12 @@ public class SampleChooserActivity extends AppCompatActivity
loaderTask.execute(uris);
}
private void onSampleGroups(final List<SampleGroup> groups, boolean sawError) {
private void onPlaylistGroups(final List<PlaylistGroup> groups, boolean sawError) {
if (sawError) {
Toast.makeText(getApplicationContext(), R.string.sample_list_load_error, Toast.LENGTH_LONG)
.show();
}
sampleAdapter.setSampleGroups(groups);
sampleAdapter.setPlaylistGroups(groups);
SharedPreferences preferences = getPreferences(MODE_PRIVATE);
@ -227,7 +233,7 @@ public class SampleChooserActivity extends AppCompatActivity
prefEditor.putInt(CHILD_POSITION_PREFERENCE_KEY, childPosition);
prefEditor.apply();
Sample sample = (Sample) view.getTag();
PlaylistHolder playlistHolder = (PlaylistHolder) view.getTag();
Intent intent = new Intent(this, PlayerActivity.class);
intent.putExtra(
IntentUtil.PREFER_EXTENSION_DECODERS_EXTRA,
@ -238,13 +244,13 @@ public class SampleChooserActivity extends AppCompatActivity
: IntentUtil.ABR_ALGORITHM_DEFAULT;
intent.putExtra(IntentUtil.ABR_ALGORITHM_EXTRA, abrAlgorithm);
intent.putExtra(IntentUtil.TUNNELING_EXTRA, isNonNullAndChecked(tunnelingMenuItem));
sample.addToIntent(intent);
IntentUtil.addToIntent(playlistHolder.mediaItems, intent);
startActivity(intent);
return true;
}
private void onSampleDownloadButtonClicked(Sample sample) {
int downloadUnsupportedStringId = getDownloadUnsupportedStringId(sample);
private void onSampleDownloadButtonClicked(PlaylistHolder playlistHolder) {
int downloadUnsupportedStringId = getDownloadUnsupportedStringId(playlistHolder);
if (downloadUnsupportedStringId != 0) {
Toast.makeText(getApplicationContext(), downloadUnsupportedStringId, Toast.LENGTH_LONG)
.show();
@ -253,25 +259,26 @@ public class SampleChooserActivity extends AppCompatActivity
((DemoApplication) getApplication())
.buildRenderersFactory(isNonNullAndChecked(preferExtensionDecodersMenuItem));
downloadTracker.toggleDownload(
getSupportFragmentManager(), (UriSample) sample, renderersFactory);
getSupportFragmentManager(), playlistHolder.mediaItems.get(0), renderersFactory);
}
}
private int getDownloadUnsupportedStringId(Sample sample) {
if (sample instanceof PlaylistSample) {
private int getDownloadUnsupportedStringId(PlaylistHolder playlistHolder) {
if (playlistHolder.mediaItems.size() > 1) {
return R.string.download_playlist_unsupported;
}
UriSample uriSample = (UriSample) sample;
if (uriSample.drmInfo != null) {
MediaItem.PlaybackProperties playbackProperties =
checkNotNull(playlistHolder.mediaItems.get(0).playbackProperties);
if (playbackProperties.drmConfiguration != null) {
return R.string.download_drm_unsupported;
}
if (uriSample.isLive) {
if (((IntentUtil.Tag) checkNotNull(playbackProperties.tag)).isLive) {
return R.string.download_live_unsupported;
}
if (uriSample.adTagUri != null) {
if (playbackProperties.adTagUri != null) {
return R.string.download_ads_unsupported;
}
String scheme = uriSample.uri.getScheme();
String scheme = playbackProperties.sourceUri.getScheme();
if (!("http".equals(scheme) || "https".equals(scheme))) {
return R.string.download_scheme_unsupported;
}
@ -283,13 +290,13 @@ public class SampleChooserActivity extends AppCompatActivity
return menuItem != null && menuItem.isChecked();
}
private final class SampleListLoader extends AsyncTask<String, Void, List<SampleGroup>> {
private final class SampleListLoader extends AsyncTask<String, Void, List<PlaylistGroup>> {
private boolean sawError;
@Override
protected List<SampleGroup> doInBackground(String... uris) {
List<SampleGroup> result = new ArrayList<>();
protected List<PlaylistGroup> doInBackground(String... uris) {
List<PlaylistGroup> result = new ArrayList<>();
Context context = getApplicationContext();
String userAgent = Util.getUserAgent(context, "ExoPlayerDemo");
DataSource dataSource =
@ -298,7 +305,7 @@ public class SampleChooserActivity extends AppCompatActivity
DataSpec dataSpec = new DataSpec(Uri.parse(uri));
InputStream inputStream = new DataSourceInputStream(dataSource, dataSpec);
try {
readSampleGroups(new JsonReader(new InputStreamReader(inputStream, "UTF-8")), result);
readPlaylistGroups(new JsonReader(new InputStreamReader(inputStream, "UTF-8")), result);
} catch (Exception e) {
Log.e(TAG, "Error loading sample list: " + uri, e);
sawError = true;
@ -310,21 +317,23 @@ public class SampleChooserActivity extends AppCompatActivity
}
@Override
protected void onPostExecute(List<SampleGroup> result) {
onSampleGroups(result, sawError);
protected void onPostExecute(List<PlaylistGroup> result) {
onPlaylistGroups(result, sawError);
}
private void readSampleGroups(JsonReader reader, List<SampleGroup> groups) throws IOException {
private void readPlaylistGroups(JsonReader reader, List<PlaylistGroup> groups)
throws IOException {
reader.beginArray();
while (reader.hasNext()) {
readSampleGroup(reader, groups);
readPlaylistGroup(reader, groups);
}
reader.endArray();
}
private void readSampleGroup(JsonReader reader, List<SampleGroup> groups) throws IOException {
private void readPlaylistGroup(JsonReader reader, List<PlaylistGroup> groups)
throws IOException {
String groupName = "";
ArrayList<Sample> samples = new ArrayList<>();
ArrayList<PlaylistHolder> playlistHolders = new ArrayList<>();
reader.beginObject();
while (reader.hasNext()) {
@ -336,7 +345,7 @@ public class SampleChooserActivity extends AppCompatActivity
case "samples":
reader.beginArray();
while (reader.hasNext()) {
samples.add(readEntry(reader, false));
playlistHolders.add(readEntry(reader, false));
}
reader.endArray();
break;
@ -349,34 +358,28 @@ public class SampleChooserActivity extends AppCompatActivity
}
reader.endObject();
SampleGroup group = getGroup(groupName, groups);
group.samples.addAll(samples);
PlaylistGroup group = getGroup(groupName, groups);
group.playlists.addAll(playlistHolders);
}
private Sample readEntry(JsonReader reader, boolean insidePlaylist) throws IOException {
String sampleName = null;
private PlaylistHolder readEntry(JsonReader reader, boolean insidePlaylist) throws IOException {
Uri uri = null;
String extension = null;
String title = null;
boolean isLive = false;
String drmScheme = null;
String drmLicenseUrl = null;
String[] drmKeyRequestProperties = null;
String[] drmSessionForClearTypes = null;
boolean drmMultiSession = false;
ArrayList<UriSample> playlistSamples = null;
String adTagUri = null;
String sphericalStereoMode = null;
List<Sample.SubtitleInfo> subtitleInfos = new ArrayList<>();
ArrayList<PlaylistHolder> children = null;
Uri subtitleUri = null;
String subtitleMimeType = null;
String subtitleLanguage = null;
MediaItem.Builder mediaItem = new MediaItem.Builder();
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
switch (name) {
case "name":
sampleName = reader.nextString();
title = reader.nextString();
break;
case "uri":
uri = Uri.parse(reader.nextString());
@ -385,47 +388,46 @@ public class SampleChooserActivity extends AppCompatActivity
extension = reader.nextString();
break;
case "drm_scheme":
drmScheme = reader.nextString();
mediaItem.setDrmUuid(Util.getDrmUuid(reader.nextString()));
break;
case "is_live":
isLive = reader.nextBoolean();
break;
case "drm_license_url":
drmLicenseUrl = reader.nextString();
mediaItem.setDrmLicenseUri(reader.nextString());
break;
case "drm_key_request_properties":
ArrayList<String> drmKeyRequestPropertiesList = new ArrayList<>();
Map<String, String> requestHeaders = new HashMap<>();
reader.beginObject();
while (reader.hasNext()) {
drmKeyRequestPropertiesList.add(reader.nextName());
drmKeyRequestPropertiesList.add(reader.nextString());
requestHeaders.put(reader.nextName(), reader.nextString());
}
reader.endObject();
drmKeyRequestProperties = drmKeyRequestPropertiesList.toArray(new String[0]);
mediaItem.setDrmLicenseRequestHeaders(requestHeaders);
break;
case "drm_session_for_clear_types":
ArrayList<String> drmSessionForClearTypesList = new ArrayList<>();
HashSet<Integer> drmSessionForClearTypes = new HashSet<>();
reader.beginArray();
while (reader.hasNext()) {
drmSessionForClearTypesList.add(reader.nextString());
drmSessionForClearTypes.add(toTrackType(reader.nextString()));
}
reader.endArray();
drmSessionForClearTypes = drmSessionForClearTypesList.toArray(new String[0]);
mediaItem.setDrmSessionForClearTypes(new ArrayList<>(drmSessionForClearTypes));
break;
case "drm_multi_session":
drmMultiSession = reader.nextBoolean();
mediaItem.setDrmMultiSession(reader.nextBoolean());
break;
case "playlist":
Assertions.checkState(!insidePlaylist, "Invalid nesting of playlists");
playlistSamples = new ArrayList<>();
children = new ArrayList<>();
reader.beginArray();
while (reader.hasNext()) {
playlistSamples.add((UriSample) readEntry(reader, /* insidePlaylist= */ true));
children.add(readEntry(reader, /* insidePlaylist= */ true));
}
reader.endArray();
break;
case "ad_tag_uri":
adTagUri = reader.nextString();
mediaItem.setAdTagUri(reader.nextString());
break;
case "spherical_stereo_mode":
Assertions.checkState(
@ -446,67 +448,71 @@ public class SampleChooserActivity extends AppCompatActivity
}
}
reader.endObject();
DrmInfo drmInfo =
drmScheme == null
? null
: new DrmInfo(
Util.getDrmUuid(drmScheme),
drmLicenseUrl,
drmKeyRequestProperties,
Sample.toTrackTypeArray(drmSessionForClearTypes),
drmMultiSession);
Sample.SubtitleInfo subtitleInfo =
subtitleUri == null
? null
: new Sample.SubtitleInfo(
if (children != null) {
List<MediaItem> mediaItems = new ArrayList<>();
for (int i = 0; i < children.size(); i++) {
mediaItems.addAll(children.get(i).mediaItems);
}
return new PlaylistHolder(title, mediaItems);
} else {
mediaItem
.setSourceUri(uri)
.setMediaMetadata(new MediaMetadata.Builder().setTitle(title).build())
.setMimeType(IntentUtil.inferAdaptiveStreamMimeType(uri, extension))
.setTag(new IntentUtil.Tag(isLive, sphericalStereoMode));
if (subtitleUri != null) {
MediaItem.Subtitle subtitle =
new MediaItem.Subtitle(
subtitleUri,
Assertions.checkNotNull(
checkNotNull(
subtitleMimeType, "subtitle_mime_type is required if subtitle_uri is set."),
subtitleLanguage);
if (playlistSamples != null) {
UriSample[] playlistSamplesArray = playlistSamples.toArray(new UriSample[0]);
return new PlaylistSample(sampleName, playlistSamplesArray);
} else {
return new UriSample(
sampleName,
uri,
extension,
isLive,
drmInfo,
adTagUri != null ? Uri.parse(adTagUri) : null,
sphericalStereoMode,
subtitleInfo);
mediaItem.setSubtitles(Collections.singletonList(subtitle));
}
return new PlaylistHolder(title, Collections.singletonList(mediaItem.build()));
}
}
private SampleGroup getGroup(String groupName, List<SampleGroup> groups) {
private PlaylistGroup getGroup(String groupName, List<PlaylistGroup> groups) {
for (int i = 0; i < groups.size(); i++) {
if (Util.areEqual(groupName, groups.get(i).title)) {
return groups.get(i);
}
}
SampleGroup group = new SampleGroup(groupName);
PlaylistGroup group = new PlaylistGroup(groupName);
groups.add(group);
return group;
}
private int toTrackType(String trackTypeString) {
switch (Util.toLowerInvariant(trackTypeString)) {
case "audio":
return C.TRACK_TYPE_AUDIO;
case "video":
return C.TRACK_TYPE_VIDEO;
default:
throw new IllegalArgumentException("Invalid track type: " + trackTypeString);
}
}
}
private final class SampleAdapter extends BaseExpandableListAdapter implements OnClickListener {
private List<SampleGroup> sampleGroups;
private List<PlaylistGroup> playlistGroups;
public SampleAdapter() {
sampleGroups = Collections.emptyList();
playlistGroups = Collections.emptyList();
}
public void setSampleGroups(List<SampleGroup> sampleGroups) {
this.sampleGroups = sampleGroups;
public void setPlaylistGroups(List<PlaylistGroup> playlistGroups) {
this.playlistGroups = playlistGroups;
notifyDataSetChanged();
}
@Override
public Sample getChild(int groupPosition, int childPosition) {
return getGroup(groupPosition).samples.get(childPosition);
public PlaylistHolder getChild(int groupPosition, int childPosition) {
return getGroup(groupPosition).playlists.get(childPosition);
}
@Override
@ -534,12 +540,12 @@ public class SampleChooserActivity extends AppCompatActivity
@Override
public int getChildrenCount(int groupPosition) {
return getGroup(groupPosition).samples.size();
return getGroup(groupPosition).playlists.size();
}
@Override
public SampleGroup getGroup(int groupPosition) {
return sampleGroups.get(groupPosition);
public PlaylistGroup getGroup(int groupPosition) {
return playlistGroups.get(groupPosition);
}
@Override
@ -562,7 +568,7 @@ public class SampleChooserActivity extends AppCompatActivity
@Override
public int getGroupCount() {
return sampleGroups.size();
return playlistGroups.size();
}
@Override
@ -577,18 +583,19 @@ public class SampleChooserActivity extends AppCompatActivity
@Override
public void onClick(View view) {
onSampleDownloadButtonClicked((Sample) view.getTag());
onSampleDownloadButtonClicked((PlaylistHolder) view.getTag());
}
private void initializeChildView(View view, Sample sample) {
view.setTag(sample);
private void initializeChildView(View view, PlaylistHolder playlistHolder) {
view.setTag(playlistHolder);
TextView sampleTitle = view.findViewById(R.id.sample_title);
sampleTitle.setText(sample.name);
sampleTitle.setText(playlistHolder.title);
boolean canDownload = getDownloadUnsupportedStringId(sample) == 0;
boolean isDownloaded = canDownload && downloadTracker.isDownloaded(((UriSample) sample).uri);
boolean canDownload = getDownloadUnsupportedStringId(playlistHolder) == 0;
boolean isDownloaded =
canDownload && downloadTracker.isDownloaded(playlistHolder.mediaItems.get(0));
ImageButton downloadButton = view.findViewById(R.id.download_button);
downloadButton.setTag(sample);
downloadButton.setTag(playlistHolder);
downloadButton.setColorFilter(
canDownload ? (isDownloaded ? 0xFF42A5F5 : 0xFFBDBDBD) : 0xFF666666);
downloadButton.setImageResource(
@ -596,14 +603,26 @@ public class SampleChooserActivity extends AppCompatActivity
}
}
private static final class SampleGroup {
private static final class PlaylistHolder {
public final String title;
public final List<Sample> samples;
public final List<MediaItem> mediaItems;
public SampleGroup(String title) {
private PlaylistHolder(String title, List<MediaItem> mediaItems) {
Assertions.checkArgument(!mediaItems.isEmpty());
this.title = title;
this.samples = new ArrayList<>();
this.mediaItems = Collections.unmodifiableList(new ArrayList<>(mediaItems));
}
}
private static final class PlaylistGroup {
public final String title;
public final List<PlaylistHolder> playlists;
public PlaylistGroup(String title) {
this.title = title;
this.playlists = new ArrayList<>();
}
}
}

View file

@ -1668,6 +1668,29 @@ public final class Util {
}
}
/**
* Makes a best guess to infer the type from a {@link Uri} and MIME type.
*
* @param uri The {@link Uri}.
* @param mimeType If not null, used to infer the type.
* @return The content type.
*/
public static int inferContentTypeWithMimeType(Uri uri, @Nullable String mimeType) {
if (mimeType == null) {
return Util.inferContentType(uri);
}
switch (mimeType) {
case MimeTypes.APPLICATION_MPD:
return C.TYPE_DASH;
case MimeTypes.APPLICATION_M3U8:
return C.TYPE_HLS;
case MimeTypes.APPLICATION_SS:
return C.TYPE_SS;
default:
return Util.inferContentType(uri);
}
}
/**
* Returns the specified millisecond time formatted as a string.
*

View file

@ -248,7 +248,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
Assertions.checkNotNull(mediaItem.playbackProperties);
@C.ContentType
int type =
inferContentType(
Util.inferContentTypeWithMimeType(
mediaItem.playbackProperties.sourceUri, mediaItem.playbackProperties.mimeType);
@Nullable MediaSourceFactory mediaSourceFactory = mediaSourceFactories.get(type);
Assertions.checkNotNull(
@ -396,20 +396,4 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
factories.put(C.TYPE_OTHER, new ProgressiveMediaSource.Factory(dataSourceFactory));
return factories;
}
private static int inferContentType(Uri sourceUri, @Nullable String mimeType) {
if (mimeType == null) {
return Util.inferContentType(sourceUri);
}
switch (mimeType) {
case MimeTypes.APPLICATION_MPD:
return C.TYPE_DASH;
case MimeTypes.APPLICATION_M3U8:
return C.TYPE_HLS;
case MimeTypes.APPLICATION_SS:
return C.TYPE_SS;
default:
return Util.inferContentType(sourceUri);
}
}
}