From f0f25aef83a21446889706f9f4fb2648a997808e Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 5 Jul 2016 03:34:02 -0700 Subject: [PATCH] Support playlists in exolist.json. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=126622342 --- .../src/main/assets/sample_media.exolist.json | 27 ++++ .../exoplayer/demo/PlayerActivity.java | 17 ++- .../exoplayer/demo/SampleChooserActivity.java | 120 ++++++++++++++---- 3 files changed, 135 insertions(+), 29 deletions(-) diff --git a/demo/src/main/assets/sample_media.exolist.json b/demo/src/main/assets/sample_media.exolist.json index eb0ea01b90..6ea5069a91 100644 --- a/demo/src/main/assets/sample_media.exolist.json +++ b/demo/src/main/assets/sample_media.exolist.json @@ -330,5 +330,32 @@ "uri": "http://vod.leasewebcdn.com/bbb.flv?ri=1024&rs=150&start=0" } ] + }, + { + "name": "Playlists", + "samples": [ + { + "name": "Cats and dogs", + "playlist": [ + { + "uri": "http://html5demos.com/assets/dizzy.mp4" + }, + { + "uri": "http://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv" + } + ] + }, + { + "name": "Audio then Video", + "playlist": [ + { + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/audio-141.mp4" + }, + { + "uri": "http://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv" + } + ] + } + ] } ] diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java index 6bfa6d13cb..407d70e850 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java @@ -87,15 +87,18 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ExoPlayer.EventListener, SimpleExoPlayer.VideoListener, SimpleExoPlayer.CaptionListener, SimpleExoPlayer.Id3MetadataListener, DefaultTrackSelector.EventListener { - public static final String URIS_LIST_EXTRA = "uris"; - public static final String CONTENT_EXT_EXTRA = "extension"; public static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid"; public static final String DRM_CONTENT_ID_EXTRA = "drm_content_id"; public static final String DRM_PROVIDER_EXTRA = "drm_provider"; public static final String PREFER_EXTENSION_DECODERS = "prefer_extension_decoders"; + public static final String ACTION_VIEW = "com.google.android.exoplayer.demo.action.VIEW"; + public static final String EXTENSION_EXTRA = "extension"; + 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"; private static final String TAG = "PlayerActivity"; @@ -285,14 +288,20 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, if (playerNeedsSource) { String action = intent.getAction(); Uri[] uris; + String[] extensions; if (ACTION_VIEW.equals(action)) { uris = new Uri[] {intent.getData()}; + extensions = new String[] {intent.getStringExtra(EXTENSION_EXTRA)}; } else if (ACTION_VIEW_LIST.equals(action)) { - String[] uriStrings = intent.getStringArrayExtra(URIS_LIST_EXTRA); + String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA); uris = new Uri[uriStrings.length]; for (int i = 0; i < uriStrings.length; i++) { uris[i] = Uri.parse(uriStrings[i]); } + extensions = intent.getStringArrayExtra(EXTENSION_LIST_EXTRA); + if (extensions == null) { + extensions = new String[uriStrings.length]; + } } else { Log.w(TAG, "Unexpected intent action: " + action); return; @@ -305,7 +314,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, UriSampleSourceProvider[] providers = new UriSampleSourceProvider[uris.length]; for (int i = 0; i < uris.length; i++) { providers[i] = new UriSampleSourceProvider(player.getBandwidthMeter(), dataSourceFactory, - uris[i], intent.getStringExtra(CONTENT_EXT_EXTRA), mainHandler, eventLogger); + uris[i], extensions[i], mainHandler, eventLogger); } SampleSourceProvider sourceProvider = providers.length == 1 ? providers[0] : new ConcatenatingSampleSourceProvider(providers); diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/SampleChooserActivity.java b/demo/src/main/java/com/google/android/exoplayer/demo/SampleChooserActivity.java index bcfafd9d64..b37afe3ecf 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/SampleChooserActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/SampleChooserActivity.java @@ -21,6 +21,7 @@ import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSourceInputStream; import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DefaultDataSource; +import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Util; import android.app.Activity; @@ -90,15 +91,7 @@ public class SampleChooserActivity extends Activity { } private void onSampleSelected(Sample sample) { - Intent intent = new Intent(this, PlayerActivity.class) - .setAction(PlayerActivity.ACTION_VIEW) - .setData(Uri.parse(sample.uri)) - .putExtra(PlayerActivity.CONTENT_EXT_EXTRA, sample.extension) - .putExtra(PlayerActivity.DRM_SCHEME_UUID_EXTRA, sample.drmSchemeUuid) - .putExtra(PlayerActivity.DRM_CONTENT_ID_EXTRA, sample.drmContentId) - .putExtra(PlayerActivity.DRM_PROVIDER_EXTRA, sample.drmProvider) - .putExtra(PlayerActivity.PREFER_EXTENSION_DECODERS, sample.preferExtensionDecoders); - startActivity(intent); + startActivity(sample.buildIntent(this)); } private final class SampleListLoader extends AsyncTask> { @@ -116,7 +109,7 @@ public class SampleChooserActivity extends Activity { InputStream inputStream = new DataSourceInputStream(dataSource, dataSpec); try { readSampleGroups(new JsonReader(new InputStreamReader(inputStream, "UTF-8")), result); - } catch (IOException e) { + } catch (Exception e) { Log.e(TAG, "Error loading sample list: " + uri, e); sawError = true; } finally { @@ -152,7 +145,7 @@ public class SampleChooserActivity extends Activity { case "samples": reader.beginArray(); while (reader.hasNext()) { - samples.add(readSample(reader)); + samples.add(readEntry(reader, false)); } reader.endArray(); break; @@ -164,7 +157,7 @@ public class SampleChooserActivity extends Activity { group.samples.addAll(samples); } - private Sample readSample(JsonReader reader) throws IOException { + private Sample readEntry(JsonReader reader, boolean insidePlaylist) throws IOException { String sampleName = null; String uri = null; String extension = null; @@ -172,10 +165,12 @@ public class SampleChooserActivity extends Activity { String drmContentId = null; String drmProvider = null; boolean preferExtensionDecoders = false; + ArrayList playlistSamples = null; reader.beginObject(); while (reader.hasNext()) { - switch (reader.nextName()) { + String name = reader.nextName(); + switch (name) { case "name": sampleName = reader.nextString(); break; @@ -194,15 +189,30 @@ public class SampleChooserActivity extends Activity { case "prefer_extension_decoders": preferExtensionDecoders = reader.nextBoolean(); break; + case "playlist": + Assertions.checkState(!insidePlaylist, "Nested playlists are invalid"); + playlistSamples = new ArrayList<>(); + reader.beginArray(); + while (reader.hasNext()) { + playlistSamples.add((UriSample) readEntry(reader, true)); + } + reader.endArray(); + break; + default: + throw new ParserException("Unsupported attribute name: " + name); } } reader.endObject(); - if (sampleName == null || uri == null) { - throw new ParserException("Invalid sample (name or uri missing)"); + if (playlistSamples != null) { + UriSample[] playlistSamplesArray = playlistSamples.toArray( + new UriSample[playlistSamples.size()]); + return new PlaylistSample(sampleName, drmUuid, drmContentId, drmProvider, + preferExtensionDecoders, playlistSamplesArray); + } else { + return new UriSample(sampleName, drmUuid, drmContentId, drmProvider, + preferExtensionDecoders, uri, extension); } - return new Sample(sampleName, uri, extension, drmUuid, drmContentId, drmProvider, - preferExtensionDecoders); } private SampleGroup getGroup(String groupName, List groups) { @@ -317,27 +327,87 @@ public class SampleChooserActivity extends Activity { } - private static class Sample { + private abstract static class Sample { public final String name; - public final String uri; - public final String extension; + public final boolean preferExtensionDecoders; + + // TODO: DRM properties should be specified on UriSample only. This requires changes to + // PlayerActivity and beyond to be able to handle playlists containing multiple DRM protected + // items that have different DRM properties. public final UUID drmSchemeUuid; public final String drmContentId; public final String drmProvider; - public final boolean preferExtensionDecoders; - public Sample(String name, String uri, String extension, UUID drmSchemeUuid, - String drmContentId, String drmProvider, boolean preferExtensionDecoders) { + public Sample(String name, UUID drmSchemeUuid, String drmContentId, String drmProvider, + boolean preferExtensionDecoders) { this.name = name; - this.uri = uri; - this.extension = extension; this.drmSchemeUuid = drmSchemeUuid; this.drmContentId = drmContentId; this.drmProvider = drmProvider; this.preferExtensionDecoders = preferExtensionDecoders; } + public abstract Intent buildIntent(Context context); + + } + + private static final class UriSample extends Sample { + + public final String uri; + public final String extension; + + public UriSample(String name, UUID drmSchemeUuid, String drmContentId, String drmProvider, + boolean preferExtensionDecoders, String uri, String extension) { + super(name, drmSchemeUuid, drmContentId, drmProvider, preferExtensionDecoders); + this.uri = uri; + this.extension = extension; + } + + @Override + public Intent buildIntent(Context context) { + return new Intent(context, PlayerActivity.class) + .setAction(PlayerActivity.ACTION_VIEW) + .putExtra(PlayerActivity.DRM_SCHEME_UUID_EXTRA, drmSchemeUuid) + .putExtra(PlayerActivity.DRM_CONTENT_ID_EXTRA, drmContentId) + .putExtra(PlayerActivity.DRM_PROVIDER_EXTRA, drmProvider) + .putExtra(PlayerActivity.PREFER_EXTENSION_DECODERS, preferExtensionDecoders) + .setData(Uri.parse(uri)) + .putExtra(PlayerActivity.EXTENSION_EXTRA, extension) + .setAction(PlayerActivity.ACTION_VIEW); + } + + } + + private static final class PlaylistSample extends Sample { + + public final UriSample[] children; + + public PlaylistSample(String name, UUID drmSchemeUuid, String drmContentId, String drmProvider, + boolean preferExtensionDecoders, UriSample... children) { + super(name, drmSchemeUuid, drmContentId, drmProvider, preferExtensionDecoders); + this.children = children; + } + + @Override + public Intent buildIntent(Context context) { + String[] uris = new String[children.length]; + String[] extensions = new String[children.length]; + for (int i = 0; i < children.length; i++) { + uris[i] = children[i].uri; + extensions[i] = children[i].extension; + } + return new Intent(context, PlayerActivity.class) + .setAction(PlayerActivity.ACTION_VIEW) + .putExtra(PlayerActivity.DRM_SCHEME_UUID_EXTRA, drmSchemeUuid) + .putExtra(PlayerActivity.DRM_CONTENT_ID_EXTRA, drmContentId) + .putExtra(PlayerActivity.DRM_PROVIDER_EXTRA, drmProvider) + .putExtra(PlayerActivity.PREFER_EXTENSION_DECODERS, preferExtensionDecoders) + .putExtra(PlayerActivity.URI_LIST_EXTRA, uris) + .putExtra(PlayerActivity.EXTENSION_LIST_EXTRA, extensions) + .setAction(PlayerActivity.ACTION_VIEW_LIST); + } + } }