Add basic DRM support to CastPlayer's demo app

PiperOrigin-RevId: 249624829
This commit is contained in:
aquilescanta 2019-05-23 13:29:56 +01:00 committed by Toni
parent 8d329fb41f
commit 3314391932
5 changed files with 108 additions and 34 deletions

View file

@ -2,6 +2,7 @@
### dev-v2 (not yet released) ###
* Add basic DRM support to the Cast demo app.
* Add `ResolvingDataSource` for just-in-time resolution of `DataSpec`s
([#5779](https://github.com/google/ExoPlayer/issues/5779)).
* Assume that encrypted content requires secure decoders in renderer support

View file

@ -44,11 +44,14 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.ui.PlayerControlView;
import com.google.android.exoplayer2.ui.PlayerView;
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.MediaMetadata;
import com.google.android.gms.cast.MediaQueueItem;
import com.google.android.gms.cast.framework.CastContext;
import java.util.ArrayList;
import org.json.JSONException;
import org.json.JSONObject;
/** Manages players and an internal media queue for the ExoPlayer/Cast demo app. */
/* package */ class DefaultReceiverPlayerManager
@ -394,12 +397,47 @@ import java.util.ArrayList;
private static MediaQueueItem buildMediaQueueItem(MediaItem item) {
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
movieMetadata.putString(MediaMetadata.KEY_TITLE, item.title);
MediaInfo mediaInfo =
MediaInfo.Builder mediaInfoBuilder =
new MediaInfo.Builder(item.media.uri.toString())
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType(item.mimeType)
.setMetadata(movieMetadata)
.build();
return new MediaQueueItem.Builder(mediaInfo).build();
.setMetadata(movieMetadata);
if (!item.drmSchemes.isEmpty()) {
MediaItem.DrmScheme scheme = item.drmSchemes.get(0);
try {
// 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.
JSONObject drmConfiguration = getDrmConfigurationJson(scheme);
if (drmConfiguration != null) {
mediaInfoBuilder.setCustomData(drmConfiguration);
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
return new MediaQueueItem.Builder(mediaInfoBuilder.build()).build();
}
@Nullable
private static JSONObject getDrmConfigurationJson(MediaItem.DrmScheme scheme)
throws JSONException {
String drmScheme;
if (C.WIDEVINE_UUID.equals(scheme.uuid)) {
drmScheme = "widevine";
} else if (C.PLAYREADY_UUID.equals(scheme.uuid)) {
drmScheme = "playready";
} else {
return null;
}
MediaItem.UriBundle licenseServer = Assertions.checkNotNull(scheme.licenseServer);
JSONObject exoplayerConfig =
new JSONObject().put("withCredentials", false).put("protectionSystem", drmScheme);
if (!licenseServer.uri.equals(Uri.EMPTY)) {
exoplayerConfig.put("licenseUrl", licenseServer.uri.toString());
}
if (!licenseServer.requestHeaders.isEmpty()) {
exoplayerConfig.put("headers", new JSONObject(licenseServer.requestHeaders));
}
return new JSONObject().put("exoPlayerConfig", exoplayerConfig);
}
}

View file

@ -15,13 +15,13 @@
*/
package com.google.android.exoplayer2.castdemo;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.MimeTypes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/** Utility methods and constants for the Cast demo application. */
@ -30,44 +30,25 @@ import java.util.UUID;
/** Represents a media sample. */
public static final class Sample {
/** The uri of the media content. */
/** 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;
/**
* The {@link UUID} of the DRM scheme that protects the content, or null if the content is not
* DRM-protected.
*/
@Nullable public final UUID drmSchemeUuid;
/**
* The url from which players should obtain DRM licenses, or null if the content is not
* DRM-protected.
*/
@Nullable public final Uri licenseServerUri;
/** Data to configure DRM license acquisition. May be null if content is not DRM-protected. */
@Nullable public final DrmConfiguration drmConfiguration;
/**
* @param uri See {@link #uri}.
* @param name See {@link #name}.
* @param mimeType See {@link #mimeType}.
*/
public Sample(String uri, String name, String mimeType) {
this(uri, name, mimeType, /* drmSchemeUuid= */ null, /* licenseServerUriString= */ null);
this(uri, name, mimeType, /* drmConfiguration= */ null);
}
public Sample(
String uri,
String name,
String mimeType,
@Nullable UUID drmSchemeUuid,
@Nullable String licenseServerUriString) {
String uri, String name, String mimeType, @Nullable DrmConfiguration drmConfiguration) {
this.uri = uri;
this.name = name;
this.mimeType = mimeType;
this.drmSchemeUuid = drmSchemeUuid;
this.licenseServerUri =
licenseServerUriString != null ? Uri.parse(licenseServerUriString) : null;
this.drmConfiguration = drmConfiguration;
}
@Override
@ -76,6 +57,29 @@ import java.util.UUID;
}
}
/** 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_HLS = MimeTypes.APPLICATION_M3U8;
public static final String MIME_TYPE_SS = MimeTypes.APPLICATION_SS;

View file

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.castdemo;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import androidx.core.graphics.ColorUtils;
import androidx.appcompat.app.AlertDialog;
@ -36,6 +37,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ext.cast.DefaultCastOptionsProvider;
import com.google.android.exoplayer2.ext.cast.MediaItem;
import com.google.android.exoplayer2.ui.PlayerControlView;
import com.google.android.exoplayer2.ui.PlayerView;
@ -121,6 +123,7 @@ public class MainActivity extends AppCompatActivity
String applicationId = castContext.getCastOptions().getReceiverApplicationId();
switch (applicationId) {
case CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID:
case DefaultCastOptionsProvider.APP_ID_DEFAULT_RECEIVER_WITH_DRM:
playerManager =
new DefaultReceiverPlayerManager(
/* listener= */ this,
@ -202,11 +205,17 @@ public class MainActivity extends AppCompatActivity
.setMedia(sample.uri)
.setTitle(sample.name)
.setMimeType(sample.mimeType);
if (sample.drmSchemeUuid != null) {
DemoUtil.DrmConfiguration drmConfiguration = sample.drmConfiguration;
if (drmConfiguration != null) {
mediaItemBuilder.setDrmSchemes(
Collections.singletonList(
new MediaItem.DrmScheme(
sample.drmSchemeUuid, new MediaItem.UriBundle(sample.licenseServerUri))));
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);

View file

@ -27,11 +27,33 @@ import java.util.List;
*/
public final class DefaultCastOptionsProvider implements OptionsProvider {
/**
* App id of the Default Media Receiver app. Apps that do not require DRM support may use this
* receiver receiver app ID.
*
* <p>See https://developers.google.com/cast/docs/caf_receiver/#default_media_receiver.
*/
public static final String APP_ID_DEFAULT_RECEIVER =
CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID;
/**
* App id for receiver app with rudimentary support for DRM.
*
* <p>This app id is only suitable for ExoPlayer's Cast Demo app, and it is not intended for
* production use. In order to use DRM, custom receiver apps should be used. For environments that
* do not require DRM, the default receiver app should be used (see {@link
* #APP_ID_DEFAULT_RECEIVER}).
*/
// TODO: Add a documentation resource link for DRM support in the receiver app [Internal ref:
// b/128603245].
public static final String APP_ID_DEFAULT_RECEIVER_WITH_DRM = "A12D4273";
@Override
public CastOptions getCastOptions(Context context) {
return new CastOptions.Builder()
.setReceiverApplicationId(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID)
.setStopReceiverApplicationWhenEndingSession(true).build();
.setReceiverApplicationId(APP_ID_DEFAULT_RECEIVER_WITH_DRM)
.setStopReceiverApplicationWhenEndingSession(true)
.build();
}
@Override