Commit 596be3b7 by olly Committed by Oliver Woodman

Cast: Simplify MediaItem/Sample to a single MediaItem class

PiperOrigin-RevId: 260021990
parent 1b9e2497
...@@ -15,97 +15,49 @@ ...@@ -15,97 +15,49 @@
*/ */
package com.google.android.exoplayer2.castdemo; package com.google.android.exoplayer2.castdemo;
import androidx.annotation.Nullable; import android.net.Uri;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ext.cast.MediaItem;
import com.google.android.exoplayer2.ext.cast.MediaItem.DrmConfiguration;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID;
/** Utility methods and constants for the Cast demo application. */ /** Utility methods and constants for the Cast demo application. */
/* package */ final class DemoUtil { /* package */ final class DemoUtil {
/** Represents a media sample. */
public static final class Sample {
/** The URI of the media content. */
public final String uri;
/** The name of the sample. */
public final String name;
/** The mime type of the sample media content. */
public final String mimeType;
/** Data to configure DRM license acquisition. May be null if content is not DRM-protected. */
@Nullable public final DrmConfiguration drmConfiguration;
public Sample(String uri, String name, String mimeType) {
this(uri, name, mimeType, /* drmConfiguration= */ null);
}
public Sample(
String uri, String name, String mimeType, @Nullable DrmConfiguration drmConfiguration) {
this.uri = uri;
this.name = name;
this.mimeType = mimeType;
this.drmConfiguration = drmConfiguration;
}
@Override
public String toString() {
return name;
}
}
/** Holds information required to play DRM-protected content. */
public static final class DrmConfiguration {
/** The {@link UUID} of the DRM scheme that protects the content. */
public final UUID drmSchemeUuid;
/**
* The URI from which players should obtain DRM licenses. May be null if the license server URI
* is provided as part of the media.
*/
@Nullable public final String licenseServerUri;
/** HTTP request headers to include the in DRM license requests. */
public final Map<String, String> httpRequestHeaders;
public DrmConfiguration(
UUID drmSchemeUuid,
@Nullable String licenseServerUri,
Map<String, String> httpRequestHeaders) {
this.drmSchemeUuid = drmSchemeUuid;
this.licenseServerUri = licenseServerUri;
this.httpRequestHeaders = httpRequestHeaders;
}
}
public static final String MIME_TYPE_DASH = MimeTypes.APPLICATION_MPD; public static final String MIME_TYPE_DASH = MimeTypes.APPLICATION_MPD;
public static final String MIME_TYPE_HLS = MimeTypes.APPLICATION_M3U8; public static final String MIME_TYPE_HLS = MimeTypes.APPLICATION_M3U8;
public static final String MIME_TYPE_SS = MimeTypes.APPLICATION_SS; public static final String MIME_TYPE_SS = MimeTypes.APPLICATION_SS;
public static final String MIME_TYPE_VIDEO_MP4 = MimeTypes.VIDEO_MP4; public static final String MIME_TYPE_VIDEO_MP4 = MimeTypes.VIDEO_MP4;
/** The list of samples available in the cast demo app. */ /** The list of samples available in the cast demo app. */
public static final List<Sample> SAMPLES; public static final List<MediaItem> SAMPLES;
static { static {
// App samples. // App samples.
ArrayList<Sample> samples = new ArrayList<>(); ArrayList<MediaItem> samples = new ArrayList<>();
// Clear content. // Clear content.
samples.add( samples.add(
new Sample( new MediaItem.Builder()
"https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd", .setUri("https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd")
"Clear DASH: Tears", .setTitle("Clear DASH: Tears")
MIME_TYPE_DASH)); .setMimeType(MIME_TYPE_DASH)
.build());
samples.add( samples.add(
new Sample( new MediaItem.Builder()
"https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8", .setUri("https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8")
"Clear HLS: Angel one", .setTitle("Clear HLS: Angel one")
MIME_TYPE_HLS)); .setMimeType(MIME_TYPE_HLS)
.build());
samples.add( samples.add(
new Sample( new MediaItem.Builder()
"https://html5demos.com/assets/dizzy.mp4", "Clear MP4: Dizzy", MIME_TYPE_VIDEO_MP4)); .setUri("https://html5demos.com/assets/dizzy.mp4")
.setTitle("Clear MP4: Dizzy")
.setMimeType(MIME_TYPE_VIDEO_MP4)
.build());
SAMPLES = Collections.unmodifiableList(samples); SAMPLES = Collections.unmodifiableList(samples);
} }
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
package com.google.android.exoplayer2.castdemo; package com.google.android.exoplayer2.castdemo;
import android.content.Context; import android.content.Context;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import androidx.core.graphics.ColorUtils; import androidx.core.graphics.ColorUtils;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
...@@ -42,7 +41,6 @@ import com.google.android.exoplayer2.ui.PlayerView; ...@@ -42,7 +41,6 @@ import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.gms.cast.framework.CastButtonFactory; import com.google.android.gms.cast.framework.CastButtonFactory;
import com.google.android.gms.cast.framework.CastContext; import com.google.android.gms.cast.framework.CastContext;
import com.google.android.gms.dynamite.DynamiteModule; import com.google.android.gms.dynamite.DynamiteModule;
import java.util.Collections;
/** /**
* An activity that plays video using {@link SimpleExoPlayer} and supports casting using ExoPlayer's * An activity that plays video using {@link SimpleExoPlayer} and supports casting using ExoPlayer's
...@@ -172,25 +170,7 @@ public class MainActivity extends AppCompatActivity ...@@ -172,25 +170,7 @@ public class MainActivity extends AppCompatActivity
sampleList.setAdapter(new SampleListAdapter(this)); sampleList.setAdapter(new SampleListAdapter(this));
sampleList.setOnItemClickListener( sampleList.setOnItemClickListener(
(parent, view, position, id) -> { (parent, view, position, id) -> {
DemoUtil.Sample sample = DemoUtil.SAMPLES.get(position); playerManager.addItem(DemoUtil.SAMPLES.get(position));
MediaItem.Builder mediaItemBuilder =
new MediaItem.Builder()
.setMedia(sample.uri)
.setTitle(sample.name)
.setMimeType(sample.mimeType);
DemoUtil.DrmConfiguration drmConfiguration = sample.drmConfiguration;
if (drmConfiguration != null) {
mediaItemBuilder.setDrmSchemes(
Collections.singletonList(
new MediaItem.DrmScheme(
drmConfiguration.drmSchemeUuid,
new MediaItem.UriBundle(
drmConfiguration.licenseServerUri != null
? Uri.parse(drmConfiguration.licenseServerUri)
: Uri.EMPTY,
drmConfiguration.httpRequestHeaders))));
}
playerManager.addItem(mediaItemBuilder.build());
mediaQueueListAdapter.notifyItemInserted(playerManager.getMediaQueueSize() - 1); mediaQueueListAdapter.notifyItemInserted(playerManager.getMediaQueueSize() - 1);
}); });
return dialogList; return dialogList;
...@@ -213,7 +193,9 @@ public class MainActivity extends AppCompatActivity ...@@ -213,7 +193,9 @@ public class MainActivity extends AppCompatActivity
TextView view = holder.textView; TextView view = holder.textView;
view.setText(holder.item.title); view.setText(holder.item.title);
// TODO: Solve coloring using the theme's ColorStateList. // TODO: Solve coloring using the theme's ColorStateList.
view.setTextColor(ColorUtils.setAlphaComponent(view.getCurrentTextColor(), view.setTextColor(
ColorUtils.setAlphaComponent(
view.getCurrentTextColor(),
position == playerManager.getCurrentItemIndex() ? 255 : 100)); position == playerManager.getCurrentItemIndex() ? 255 : 100));
} }
...@@ -294,11 +276,10 @@ public class MainActivity extends AppCompatActivity ...@@ -294,11 +276,10 @@ public class MainActivity extends AppCompatActivity
} }
} }
private static final class SampleListAdapter extends ArrayAdapter<DemoUtil.Sample> { private static final class SampleListAdapter extends ArrayAdapter<MediaItem> {
public SampleListAdapter(Context context) { public SampleListAdapter(Context context) {
super(context, android.R.layout.simple_list_item_1, DemoUtil.SAMPLES); super(context, android.R.layout.simple_list_item_1, DemoUtil.SAMPLES);
} }
} }
} }
...@@ -44,7 +44,6 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; ...@@ -44,7 +44,6 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.ui.PlayerControlView; import com.google.android.exoplayer2.ui.PlayerControlView;
import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.gms.cast.MediaInfo; import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata; import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.cast.MediaQueueItem; import com.google.android.gms.cast.MediaQueueItem;
...@@ -368,8 +367,12 @@ import org.json.JSONObject; ...@@ -368,8 +367,12 @@ import org.json.JSONObject;
} }
private static MediaSource buildMediaSource(MediaItem item) { private static MediaSource buildMediaSource(MediaItem item) {
Uri uri = item.media.uri; Uri uri = item.uri;
switch (item.mimeType) { String mimeType = item.mimeType;
if (mimeType == null) {
throw new IllegalArgumentException("mimeType is required");
}
switch (mimeType) {
case DemoUtil.MIME_TYPE_SS: case DemoUtil.MIME_TYPE_SS:
return new SsMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); return new SsMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri);
case DemoUtil.MIME_TYPE_DASH: case DemoUtil.MIME_TYPE_DASH:
...@@ -379,7 +382,7 @@ import org.json.JSONObject; ...@@ -379,7 +382,7 @@ import org.json.JSONObject;
case DemoUtil.MIME_TYPE_VIDEO_MP4: case DemoUtil.MIME_TYPE_VIDEO_MP4:
return new ProgressiveMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); return new ProgressiveMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri);
default: default:
throw new IllegalStateException("Unsupported type: " + item.mimeType); throw new IllegalArgumentException("mimeType is unsupported: " + mimeType);
} }
} }
...@@ -387,18 +390,18 @@ import org.json.JSONObject; ...@@ -387,18 +390,18 @@ import org.json.JSONObject;
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
movieMetadata.putString(MediaMetadata.KEY_TITLE, item.title); movieMetadata.putString(MediaMetadata.KEY_TITLE, item.title);
MediaInfo.Builder mediaInfoBuilder = MediaInfo.Builder mediaInfoBuilder =
new MediaInfo.Builder(item.media.uri.toString()) new MediaInfo.Builder(item.uri.toString())
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType(item.mimeType) .setContentType(item.mimeType)
.setMetadata(movieMetadata); .setMetadata(movieMetadata);
if (!item.drmSchemes.isEmpty()) { MediaItem.DrmConfiguration drmConfiguration = item.drmConfiguration;
MediaItem.DrmScheme scheme = item.drmSchemes.get(0); if (drmConfiguration != null) {
try { try {
// This configuration is only intended for testing and should *not* be used in production // This configuration is only intended for testing and should *not* be used in production
// environments. See comment in the Cast Demo app's options provider. // environments. See comment in the Cast Demo app's options provider.
JSONObject drmConfiguration = getDrmConfigurationJson(scheme); JSONObject drmConfigurationJson = getDrmConfigurationJson(drmConfiguration);
if (drmConfiguration != null) { if (drmConfigurationJson != null) {
mediaInfoBuilder.setCustomData(drmConfiguration); mediaInfoBuilder.setCustomData(drmConfigurationJson);
} }
} catch (JSONException e) { } catch (JSONException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
...@@ -408,24 +411,23 @@ import org.json.JSONObject; ...@@ -408,24 +411,23 @@ import org.json.JSONObject;
} }
@Nullable @Nullable
private static JSONObject getDrmConfigurationJson(MediaItem.DrmScheme scheme) private static JSONObject getDrmConfigurationJson(MediaItem.DrmConfiguration drmConfiguration)
throws JSONException { throws JSONException {
String drmScheme; String drmScheme;
if (C.WIDEVINE_UUID.equals(scheme.uuid)) { if (C.WIDEVINE_UUID.equals(drmConfiguration.uuid)) {
drmScheme = "widevine"; drmScheme = "widevine";
} else if (C.PLAYREADY_UUID.equals(scheme.uuid)) { } else if (C.PLAYREADY_UUID.equals(drmConfiguration.uuid)) {
drmScheme = "playready"; drmScheme = "playready";
} else { } else {
return null; return null;
} }
MediaItem.UriBundle licenseServer = Assertions.checkNotNull(scheme.licenseServer);
JSONObject exoplayerConfig = JSONObject exoplayerConfig =
new JSONObject().put("withCredentials", false).put("protectionSystem", drmScheme); new JSONObject().put("withCredentials", false).put("protectionSystem", drmScheme);
if (!licenseServer.uri.equals(Uri.EMPTY)) { if (drmConfiguration.licenseUri != null) {
exoplayerConfig.put("licenseUrl", licenseServer.uri.toString()); exoplayerConfig.put("licenseUrl", drmConfiguration.licenseUri);
} }
if (!licenseServer.requestHeaders.isEmpty()) { if (!drmConfiguration.requestHeaders.isEmpty()) {
exoplayerConfig.put("headers", new JSONObject(licenseServer.requestHeaders)); exoplayerConfig.put("headers", new JSONObject(drmConfiguration.requestHeaders));
} }
return new JSONObject().put("exoPlayerConfig", exoplayerConfig); return new JSONObject().put("exoPlayerConfig", exoplayerConfig);
} }
......
...@@ -21,9 +21,7 @@ import android.net.Uri; ...@@ -21,9 +21,7 @@ import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -36,7 +34,7 @@ public class MediaItemTest { ...@@ -36,7 +34,7 @@ public class MediaItemTest {
MediaItem.Builder builder = new MediaItem.Builder(); MediaItem.Builder builder = new MediaItem.Builder();
MediaItem item1 = MediaItem item1 =
builder builder
.setMedia("http://example.com") .setUri(Uri.parse("http://example.com"))
.setTitle("title") .setTitle("title")
.setMimeType(MimeTypes.AUDIO_MP4) .setMimeType(MimeTypes.AUDIO_MP4)
.build(); .build();
...@@ -45,18 +43,19 @@ public class MediaItemTest { ...@@ -45,18 +43,19 @@ public class MediaItemTest {
} }
@Test @Test
public void buildMediaItem_assertDefaultValues() {
assertDefaultValues(new MediaItem.Builder().build());
}
@Test
public void equals_withEqualDrmSchemes_returnsTrue() { public void equals_withEqualDrmSchemes_returnsTrue() {
MediaItem.Builder builder1 = new MediaItem.Builder(); MediaItem.Builder builder1 = new MediaItem.Builder();
MediaItem mediaItem1 = MediaItem mediaItem1 =
builder1.setMedia("www.google.com").setDrmSchemes(createDummyDrmSchemes(1)).build(); builder1
.setUri(Uri.parse("www.google.com"))
.setDrmConfiguration(buildDrmConfiguration(1))
.build();
MediaItem.Builder builder2 = new MediaItem.Builder(); MediaItem.Builder builder2 = new MediaItem.Builder();
MediaItem mediaItem2 = MediaItem mediaItem2 =
builder2.setMedia("www.google.com").setDrmSchemes(createDummyDrmSchemes(1)).build(); builder2
.setUri(Uri.parse("www.google.com"))
.setDrmConfiguration(buildDrmConfiguration(1))
.build();
assertThat(mediaItem1).isEqualTo(mediaItem2); assertThat(mediaItem1).isEqualTo(mediaItem2);
} }
...@@ -64,33 +63,24 @@ public class MediaItemTest { ...@@ -64,33 +63,24 @@ public class MediaItemTest {
public void equals_withDifferentDrmRequestHeaders_returnsFalse() { public void equals_withDifferentDrmRequestHeaders_returnsFalse() {
MediaItem.Builder builder1 = new MediaItem.Builder(); MediaItem.Builder builder1 = new MediaItem.Builder();
MediaItem mediaItem1 = MediaItem mediaItem1 =
builder1.setMedia("www.google.com").setDrmSchemes(createDummyDrmSchemes(1)).build(); builder1
.setUri(Uri.parse("www.google.com"))
.setDrmConfiguration(buildDrmConfiguration(1))
.build();
MediaItem.Builder builder2 = new MediaItem.Builder(); MediaItem.Builder builder2 = new MediaItem.Builder();
MediaItem mediaItem2 = MediaItem mediaItem2 =
builder2.setMedia("www.google.com").setDrmSchemes(createDummyDrmSchemes(2)).build(); builder2
.setUri(Uri.parse("www.google.com"))
.setDrmConfiguration(buildDrmConfiguration(2))
.build();
assertThat(mediaItem1).isNotEqualTo(mediaItem2); assertThat(mediaItem1).isNotEqualTo(mediaItem2);
} }
private static void assertDefaultValues(MediaItem item) { private static MediaItem.DrmConfiguration buildDrmConfiguration(int seed) {
assertThat(item.title).isEmpty(); HashMap<String, String> requestHeaders = new HashMap<>();
assertThat(item.media.uri).isEqualTo(Uri.EMPTY); requestHeaders.put("key1", "value1");
assertThat(item.drmSchemes).isEmpty(); requestHeaders.put("key2", "value2" + seed);
assertThat(item.mimeType).isEmpty(); return new MediaItem.DrmConfiguration(
} C.WIDEVINE_UUID, Uri.parse("www.uri1.com"), requestHeaders);
private static List<MediaItem.DrmScheme> createDummyDrmSchemes(int seed) {
HashMap<String, String> requestHeaders1 = new HashMap<>();
requestHeaders1.put("key1", "value1");
requestHeaders1.put("key2", "value1");
MediaItem.UriBundle uriBundle1 =
new MediaItem.UriBundle(Uri.parse("www.uri1.com"), requestHeaders1);
MediaItem.DrmScheme drmScheme1 = new MediaItem.DrmScheme(C.WIDEVINE_UUID, uriBundle1);
HashMap<String, String> requestHeaders2 = new HashMap<>();
requestHeaders2.put("key3", "value3");
requestHeaders2.put("key4", "valueWithSeed" + seed);
MediaItem.UriBundle uriBundle2 =
new MediaItem.UriBundle(Uri.parse("www.uri2.com"), requestHeaders2);
MediaItem.DrmScheme drmScheme2 = new MediaItem.DrmScheme(C.PLAYREADY_UUID, uriBundle2);
return Arrays.asList(drmScheme1, drmScheme2);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment