Commit 79b86de6 by aquilescanta Committed by Oliver Woodman

Use per-media source DRM in the Cast demo app

PiperOrigin-RevId: 261125341
parent cb8983af
...@@ -35,6 +35,7 @@ import android.view.ViewGroup; ...@@ -35,6 +35,7 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ext.cast.MediaItem; import com.google.android.exoplayer2.ext.cast.MediaItem;
...@@ -164,8 +165,23 @@ public class MainActivity extends AppCompatActivity ...@@ -164,8 +165,23 @@ public class MainActivity extends AppCompatActivity
} }
} }
@Override
public void onUnsupportedTrack(int trackType) {
if (trackType == C.TRACK_TYPE_AUDIO) {
showToast(R.string.error_unsupported_audio);
} else if (trackType == C.TRACK_TYPE_VIDEO) {
showToast(R.string.error_unsupported_video);
} else {
// Do nothing.
}
}
// Internal methods. // Internal methods.
private void showToast(int messageId) {
Toast.makeText(getApplicationContext(), messageId, Toast.LENGTH_LONG).show();
}
private View buildSampleListView() { private View buildSampleListView() {
View dialogList = getLayoutInflater().inflate(R.layout.sample_list, null); View dialogList = getLayoutInflater().inflate(R.layout.sample_list, null);
ListView sampleList = dialogList.findViewById(R.id.sample_list); ListView sampleList = dialogList.findViewById(R.id.sample_list);
......
...@@ -28,6 +28,12 @@ import com.google.android.exoplayer2.Player.TimelineChangeReason; ...@@ -28,6 +28,12 @@ import com.google.android.exoplayer2.Player.TimelineChangeReason;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
import com.google.android.exoplayer2.ext.cast.CastPlayer; import com.google.android.exoplayer2.ext.cast.CastPlayer;
import com.google.android.exoplayer2.ext.cast.DefaultMediaItemConverter; import com.google.android.exoplayer2.ext.cast.DefaultMediaItemConverter;
import com.google.android.exoplayer2.ext.cast.MediaItem; import com.google.android.exoplayer2.ext.cast.MediaItem;
...@@ -36,15 +42,21 @@ import com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener; ...@@ -36,15 +42,21 @@ import com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource; import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.dash.DashMediaSource; import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.source.hls.HlsMediaSource; import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
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.gms.cast.MediaQueueItem; import com.google.android.gms.cast.MediaQueueItem;
import com.google.android.gms.cast.framework.CastContext; import com.google.android.gms.cast.framework.CastContext;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.Map;
/** Manages players and an internal media queue for the demo app. */ /** Manages players and an internal media queue for the demo app. */
/* package */ class PlayerManager implements EventListener, SessionAvailabilityListener { /* package */ class PlayerManager implements EventListener, SessionAvailabilityListener {
...@@ -54,6 +66,13 @@ import java.util.ArrayList; ...@@ -54,6 +66,13 @@ import java.util.ArrayList;
/** Called when the currently played item of the media queue changes. */ /** Called when the currently played item of the media queue changes. */
void onQueuePositionChanged(int previousIndex, int newIndex); void onQueuePositionChanged(int previousIndex, int newIndex);
/**
* Called when a track of type {@code trackType} is not supported by the player.
*
* @param trackType One of the {@link C}{@code .TRACK_TYPE_*} constants.
*/
void onUnsupportedTrack(int trackType);
} }
private static final String USER_AGENT = "ExoCastDemoPlayer"; private static final String USER_AGENT = "ExoCastDemoPlayer";
...@@ -62,13 +81,16 @@ import java.util.ArrayList; ...@@ -62,13 +81,16 @@ import java.util.ArrayList;
private final PlayerView localPlayerView; private final PlayerView localPlayerView;
private final PlayerControlView castControlView; private final PlayerControlView castControlView;
private final DefaultTrackSelector trackSelector;
private final SimpleExoPlayer exoPlayer; private final SimpleExoPlayer exoPlayer;
private final CastPlayer castPlayer; private final CastPlayer castPlayer;
private final ArrayList<MediaItem> mediaQueue; private final ArrayList<MediaItem> mediaQueue;
private final Listener listener; private final Listener listener;
private final ConcatenatingMediaSource concatenatingMediaSource; private final ConcatenatingMediaSource concatenatingMediaSource;
private final MediaItemConverter mediaItemConverter; private final MediaItemConverter mediaItemConverter;
private final IdentityHashMap<MediaSource, FrameworkMediaDrm> mediaDrms;
private TrackGroupArray lastSeenTrackGroupArray;
private int currentItemIndex; private int currentItemIndex;
private Player currentPlayer; private Player currentPlayer;
...@@ -94,8 +116,10 @@ import java.util.ArrayList; ...@@ -94,8 +116,10 @@ import java.util.ArrayList;
currentItemIndex = C.INDEX_UNSET; currentItemIndex = C.INDEX_UNSET;
concatenatingMediaSource = new ConcatenatingMediaSource(); concatenatingMediaSource = new ConcatenatingMediaSource();
mediaItemConverter = new DefaultMediaItemConverter(); mediaItemConverter = new DefaultMediaItemConverter();
mediaDrms = new IdentityHashMap<>();
exoPlayer = ExoPlayerFactory.newSimpleInstance(context); trackSelector = new DefaultTrackSelector(context);
exoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector);
exoPlayer.addListener(this); exoPlayer.addListener(this);
localPlayerView.setPlayer(exoPlayer); localPlayerView.setPlayer(exoPlayer);
...@@ -162,7 +186,8 @@ import java.util.ArrayList; ...@@ -162,7 +186,8 @@ import java.util.ArrayList;
if (itemIndex == -1) { if (itemIndex == -1) {
return false; return false;
} }
concatenatingMediaSource.removeMediaSource(itemIndex); MediaSource removedMediaSource = concatenatingMediaSource.removeMediaSource(itemIndex);
releaseMediaDrmOfMediaSource(removedMediaSource);
if (currentPlayer == castPlayer) { if (currentPlayer == castPlayer) {
if (castPlayer.getPlaybackState() != Player.STATE_IDLE) { if (castPlayer.getPlaybackState() != Player.STATE_IDLE) {
Timeline castTimeline = castPlayer.getCurrentTimeline(); Timeline castTimeline = castPlayer.getCurrentTimeline();
...@@ -238,6 +263,9 @@ import java.util.ArrayList; ...@@ -238,6 +263,9 @@ import java.util.ArrayList;
currentItemIndex = C.INDEX_UNSET; currentItemIndex = C.INDEX_UNSET;
mediaQueue.clear(); mediaQueue.clear();
concatenatingMediaSource.clear(); concatenatingMediaSource.clear();
for (FrameworkMediaDrm mediaDrm : mediaDrms.values()) {
mediaDrm.release();
}
castPlayer.setSessionAvailabilityListener(null); castPlayer.setSessionAvailabilityListener(null);
castPlayer.release(); castPlayer.release();
localPlayerView.setPlayer(null); localPlayerView.setPlayer(null);
...@@ -261,6 +289,25 @@ import java.util.ArrayList; ...@@ -261,6 +289,25 @@ import java.util.ArrayList;
updateCurrentItemIndex(); updateCurrentItemIndex();
} }
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
if (currentPlayer == exoPlayer && trackGroups != lastSeenTrackGroupArray) {
MappingTrackSelector.MappedTrackInfo mappedTrackInfo =
trackSelector.getCurrentMappedTrackInfo();
if (mappedTrackInfo != null) {
if (mappedTrackInfo.getTypeSupport(C.TRACK_TYPE_VIDEO)
== MappingTrackSelector.MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
listener.onUnsupportedTrack(C.TRACK_TYPE_VIDEO);
}
if (mappedTrackInfo.getTypeSupport(C.TRACK_TYPE_AUDIO)
== MappingTrackSelector.MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
listener.onUnsupportedTrack(C.TRACK_TYPE_AUDIO);
}
}
lastSeenTrackGroupArray = trackGroups;
}
}
// CastPlayer.SessionAvailabilityListener implementation. // CastPlayer.SessionAvailabilityListener implementation.
@Override @Override
...@@ -360,23 +407,78 @@ import java.util.ArrayList; ...@@ -360,23 +407,78 @@ import java.util.ArrayList;
} }
} }
private static MediaSource buildMediaSource(MediaItem item) { private MediaSource buildMediaSource(MediaItem item) {
Uri uri = item.uri; Uri uri = item.uri;
String mimeType = item.mimeType; String mimeType = item.mimeType;
if (mimeType == null) { if (mimeType == null) {
throw new IllegalArgumentException("mimeType is required"); throw new IllegalArgumentException("mimeType is required");
} }
FrameworkMediaDrm mediaDrm = null;
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager =
DrmSessionManager.getDummyDrmSessionManager();
MediaItem.DrmConfiguration drmConfiguration = item.drmConfiguration;
if (drmConfiguration != null) {
String licenseServerUrl =
drmConfiguration.licenseUri != null ? drmConfiguration.licenseUri.toString() : "";
HttpMediaDrmCallback drmCallback =
new HttpMediaDrmCallback(licenseServerUrl, DATA_SOURCE_FACTORY);
for (Map.Entry<String, String> requestHeader : drmConfiguration.requestHeaders.entrySet()) {
drmCallback.setKeyRequestProperty(requestHeader.getKey(), requestHeader.getValue());
}
try {
mediaDrm = FrameworkMediaDrm.newInstance(drmConfiguration.uuid);
drmSessionManager =
new DefaultDrmSessionManager<>(
drmConfiguration.uuid,
mediaDrm,
drmCallback,
/* optionalKeyRequestParameters= */ null,
/* multiSession= */ true);
} catch (UnsupportedDrmException e) {
// Do nothing. The track selector will avoid selecting the DRM protected tracks.
}
}
MediaSource createdMediaSource;
switch (mimeType) { switch (mimeType) {
case DemoUtil.MIME_TYPE_SS: case DemoUtil.MIME_TYPE_SS:
return new SsMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); createdMediaSource =
new SsMediaSource.Factory(DATA_SOURCE_FACTORY)
.setDrmSessionManager(drmSessionManager)
.createMediaSource(uri);
break;
case DemoUtil.MIME_TYPE_DASH: case DemoUtil.MIME_TYPE_DASH:
return new DashMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); createdMediaSource =
new DashMediaSource.Factory(DATA_SOURCE_FACTORY)
.setDrmSessionManager(drmSessionManager)
.createMediaSource(uri);
break;
case DemoUtil.MIME_TYPE_HLS: case DemoUtil.MIME_TYPE_HLS:
return new HlsMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); createdMediaSource =
new HlsMediaSource.Factory(DATA_SOURCE_FACTORY)
.setDrmSessionManager(drmSessionManager)
.createMediaSource(uri);
break;
case DemoUtil.MIME_TYPE_VIDEO_MP4: case DemoUtil.MIME_TYPE_VIDEO_MP4:
return new ProgressiveMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); createdMediaSource =
new ProgressiveMediaSource.Factory(DATA_SOURCE_FACTORY)
.setDrmSessionManager(drmSessionManager)
.createMediaSource(uri);
break;
default: default:
throw new IllegalArgumentException("mimeType is unsupported: " + mimeType); throw new IllegalArgumentException("mimeType is unsupported: " + mimeType);
} }
if (mediaDrm != null) {
mediaDrms.put(createdMediaSource, mediaDrm);
}
return createdMediaSource;
}
private void releaseMediaDrmOfMediaSource(MediaSource mediaSource) {
FrameworkMediaDrm mediaDrmToRelease = mediaDrms.remove(mediaSource);
if (mediaDrmToRelease != null) {
mediaDrmToRelease.release();
}
} }
} }
...@@ -24,4 +24,8 @@ ...@@ -24,4 +24,8 @@
<string name="cast_context_error">Failed to get Cast context. Try updating Google Play Services and restart the app.</string> <string name="cast_context_error">Failed to get Cast context. Try updating Google Play Services and restart the app.</string>
<string name="error_unsupported_video">Media includes video tracks, but none are playable by this device</string>
<string name="error_unsupported_audio">Media includes audio tracks, but none are playable by this device</string>
</resources> </resources>
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