mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Use per-media source DRM in the Cast demo app
PiperOrigin-RevId: 261125341
This commit is contained in:
parent
cb8983afd1
commit
79b86de619
3 changed files with 129 additions and 7 deletions
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
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;
|
||||||
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;
|
||||||
|
|
||||||
/** 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;
|
||||||
|
|
||||||
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;
|
||||||
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;
|
||||||
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;
|
||||||
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;
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 @@
|
||||||
|
|
||||||
<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>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue