Remove cast media item

PiperOrigin-RevId: 301224632
This commit is contained in:
bachinger 2020-03-16 20:18:15 +00:00 committed by Oliver Woodman
parent 5f8cc71d1b
commit 510f5edd1d
12 changed files with 231 additions and 422 deletions

View File

@ -17,8 +17,8 @@ package com.google.android.exoplayer2.castdemo;
import android.net.Uri; 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.MediaItem;
import com.google.android.exoplayer2.ext.cast.MediaItem.DrmConfiguration; import com.google.android.exoplayer2.MediaMetadata;
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;
@ -41,60 +41,53 @@ import java.util.List;
// Clear content. // Clear content.
samples.add( samples.add(
new MediaItem.Builder() new MediaItem.Builder()
.setUri("https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd") .setSourceUri("https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd")
.setTitle("Clear DASH: Tears") .setMediaMetadata(new MediaMetadata.Builder().setTitle("Clear DASH: Tears").build())
.setMimeType(MIME_TYPE_DASH) .setMimeType(MIME_TYPE_DASH)
.build()); .build());
samples.add( samples.add(
new MediaItem.Builder() new MediaItem.Builder()
.setUri("https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8") .setSourceUri("https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8")
.setTitle("Clear HLS: Angel one") .setMediaMetadata(new MediaMetadata.Builder().setTitle("Clear HLS: Angel one").build())
.setMimeType(MIME_TYPE_HLS) .setMimeType(MIME_TYPE_HLS)
.build()); .build());
samples.add( samples.add(
new MediaItem.Builder() new MediaItem.Builder()
.setUri("https://html5demos.com/assets/dizzy.mp4") .setSourceUri("https://html5demos.com/assets/dizzy.mp4")
.setTitle("Clear MP4: Dizzy") .setMediaMetadata(new MediaMetadata.Builder().setTitle("Clear MP4: Dizzy").build())
.setMimeType(MIME_TYPE_VIDEO_MP4) .setMimeType(MIME_TYPE_VIDEO_MP4)
.build()); .build());
// DRM content. // DRM content.
samples.add( samples.add(
new MediaItem.Builder() new MediaItem.Builder()
.setUri(Uri.parse("https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd")) .setSourceUri(
.setTitle("Widevine DASH cenc: Tears") Uri.parse("https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd"))
.setMediaMetadata(
new MediaMetadata.Builder().setTitle("Widevine DASH cenc: Tears").build())
.setMimeType(MIME_TYPE_DASH) .setMimeType(MIME_TYPE_DASH)
.setDrmConfiguration( .setDrmUuid(C.WIDEVINE_UUID)
new DrmConfiguration( .setDrmLicenseUri("https://proxy.uat.widevine.com/proxy?provider=widevine_test")
C.WIDEVINE_UUID,
Uri.parse("https://proxy.uat.widevine.com/proxy?provider=widevine_test"),
Collections.emptyMap()))
.build()); .build());
samples.add( samples.add(
new MediaItem.Builder() new MediaItem.Builder()
.setUri( .setSourceUri(
Uri.parse( "https://storage.googleapis.com/wvmedia/cbc1/h264/tears/tears_aes_cbc1.mpd")
"https://storage.googleapis.com/wvmedia/cbc1/h264/tears/tears_aes_cbc1.mpd")) .setMediaMetadata(
.setTitle("Widevine DASH cbc1: Tears") new MediaMetadata.Builder().setTitle("Widevine DASH cbc1: Tears").build())
.setMimeType(MIME_TYPE_DASH) .setMimeType(MIME_TYPE_DASH)
.setDrmConfiguration( .setDrmUuid(C.WIDEVINE_UUID)
new DrmConfiguration( .setDrmLicenseUri("https://proxy.uat.widevine.com/proxy?provider=widevine_test")
C.WIDEVINE_UUID,
Uri.parse("https://proxy.uat.widevine.com/proxy?provider=widevine_test"),
Collections.emptyMap()))
.build()); .build());
samples.add( samples.add(
new MediaItem.Builder() new MediaItem.Builder()
.setUri( .setSourceUri(
Uri.parse( "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs.mpd")
"https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs.mpd")) .setMediaMetadata(
.setTitle("Widevine DASH cbcs: Tears") new MediaMetadata.Builder().setTitle("Widevine DASH cbcs: Tears").build())
.setMimeType(MIME_TYPE_DASH) .setMimeType(MIME_TYPE_DASH)
.setDrmConfiguration( .setDrmUuid(C.WIDEVINE_UUID)
new DrmConfiguration( .setDrmLicenseUri("https://proxy.uat.widevine.com/proxy?provider=widevine_test")
C.WIDEVINE_UUID,
Uri.parse("https://proxy.uat.widevine.com/proxy?provider=widevine_test"),
Collections.emptyMap()))
.build()); .build());
SAMPLES = Collections.unmodifiableList(samples); SAMPLES = Collections.unmodifiableList(samples);

View File

@ -37,10 +37,12 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder; import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
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.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.util.Assertions;
import com.google.android.exoplayer2.util.Util;
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;
@ -206,9 +208,10 @@ public class MainActivity extends AppCompatActivity
@Override @Override
public void onBindViewHolder(QueueItemViewHolder holder, int position) { public void onBindViewHolder(QueueItemViewHolder holder, int position) {
holder.item = playerManager.getItem(position); holder.item = Assertions.checkNotNull(playerManager.getItem(position));
TextView view = holder.textView; TextView view = holder.textView;
view.setText(holder.item.title); view.setText(holder.item.mediaMetadata.title);
// TODO: Solve coloring using the theme's ColorStateList. // TODO: Solve coloring using the theme's ColorStateList.
view.setTextColor( view.setTextColor(
ColorUtils.setAlphaComponent( ColorUtils.setAlphaComponent(
@ -305,7 +308,7 @@ public class MainActivity extends AppCompatActivity
@NonNull @NonNull
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View view = super.getView(position, convertView, parent); View view = super.getView(position, convertView, parent);
((TextView) view).setText(getItem(position).title); ((TextView) view).setText(Util.castNonNull(getItem(position)).mediaMetadata.title);
return view; return view;
} }
} }

View File

@ -16,11 +16,11 @@
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.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Player.DiscontinuityReason; import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.Player.EventListener; import com.google.android.exoplayer2.Player.EventListener;
@ -28,34 +28,22 @@ 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.ExoMediaCrypto;
import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
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.MediaItemConverter; import com.google.android.exoplayer2.ext.cast.MediaItemConverter;
import com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener; 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.DefaultMediaSourceFactory;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; 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.exoplayer2.util.Util;
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.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 {
@ -78,6 +66,7 @@ import java.util.Map;
private static final DefaultHttpDataSourceFactory DATA_SOURCE_FACTORY = private static final DefaultHttpDataSourceFactory DATA_SOURCE_FACTORY =
new DefaultHttpDataSourceFactory(USER_AGENT); new DefaultHttpDataSourceFactory(USER_AGENT);
private final DefaultMediaSourceFactory defaultMediaSourceFactory;
private final PlayerView localPlayerView; private final PlayerView localPlayerView;
private final PlayerControlView castControlView; private final PlayerControlView castControlView;
private final DefaultTrackSelector trackSelector; private final DefaultTrackSelector trackSelector;
@ -117,6 +106,7 @@ import java.util.Map;
trackSelector = new DefaultTrackSelector(context); trackSelector = new DefaultTrackSelector(context);
exoPlayer = new SimpleExoPlayer.Builder(context).setTrackSelector(trackSelector).build(); exoPlayer = new SimpleExoPlayer.Builder(context).setTrackSelector(trackSelector).build();
defaultMediaSourceFactory = DefaultMediaSourceFactory.newInstance(context, DATA_SOURCE_FACTORY);
exoPlayer.addListener(this); exoPlayer.addListener(this);
localPlayerView.setPlayer(exoPlayer); localPlayerView.setPlayer(exoPlayer);
@ -151,7 +141,7 @@ import java.util.Map;
*/ */
public void addItem(MediaItem item) { public void addItem(MediaItem item) {
mediaQueue.add(item); mediaQueue.add(item);
concatenatingMediaSource.addMediaSource(buildMediaSource(item)); concatenatingMediaSource.addMediaSource(defaultMediaSourceFactory.createMediaSource(item));
if (currentPlayer == castPlayer) { if (currentPlayer == castPlayer) {
castPlayer.addItems(mediaItemConverter.toMediaQueueItem(item)); castPlayer.addItems(mediaItemConverter.toMediaQueueItem(item));
} }
@ -400,62 +390,4 @@ import java.util.Map;
listener.onQueuePositionChanged(oldIndex, currentItemIndex); listener.onQueuePositionChanged(oldIndex, currentItemIndex);
} }
} }
private MediaSource buildMediaSource(MediaItem item) {
Uri uri = item.uri;
String mimeType = item.mimeType;
if (mimeType == null) {
throw new IllegalArgumentException("mimeType is required");
}
DrmSessionManager<ExoMediaCrypto> drmSessionManager =
DrmSessionManager.getDummyDrmSessionManager();
MediaItem.DrmConfiguration drmConfiguration = item.drmConfiguration;
if (drmConfiguration != null && Util.SDK_INT >= 18) {
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());
}
drmSessionManager =
new DefaultDrmSessionManager.Builder()
.setMultiSession(/* multiSession= */ true)
.setUuidAndExoMediaDrmProvider(
drmConfiguration.uuid, FrameworkMediaDrm.DEFAULT_PROVIDER)
.build(drmCallback);
}
MediaSource createdMediaSource;
switch (mimeType) {
case DemoUtil.MIME_TYPE_SS:
createdMediaSource =
new SsMediaSource.Factory(DATA_SOURCE_FACTORY)
.setDrmSessionManager(drmSessionManager)
.createMediaSource(uri);
break;
case DemoUtil.MIME_TYPE_DASH:
createdMediaSource =
new DashMediaSource.Factory(DATA_SOURCE_FACTORY)
.setDrmSessionManager(drmSessionManager)
.createMediaSource(uri);
break;
case DemoUtil.MIME_TYPE_HLS:
createdMediaSource =
new HlsMediaSource.Factory(DATA_SOURCE_FACTORY)
.setDrmSessionManager(drmSessionManager)
.createMediaSource(uri);
break;
case DemoUtil.MIME_TYPE_VIDEO_MP4:
createdMediaSource =
new ProgressiveMediaSource.Factory(DATA_SOURCE_FACTORY)
.setDrmSessionManager(drmSessionManager)
.createMediaSource(uri);
break;
default:
throw new IllegalArgumentException("mimeType is unsupported: " + mimeType);
}
return createdMediaSource;
}
} }

View File

@ -18,7 +18,8 @@ package com.google.android.exoplayer2.ext.cast;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ext.cast.MediaItem.DrmConfiguration; import com.google.android.exoplayer2.MediaItem;
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;
@ -48,17 +49,18 @@ public final class DefaultMediaItemConverter implements MediaItemConverter {
@Override @Override
public MediaQueueItem toMediaQueueItem(MediaItem item) { public MediaQueueItem toMediaQueueItem(MediaItem item) {
if (item.mimeType == null) { Assertions.checkNotNull(item.playbackProperties);
if (item.playbackProperties.mimeType == null) {
throw new IllegalArgumentException("The item must specify its mimeType"); throw new IllegalArgumentException("The item must specify its mimeType");
} }
MediaMetadata metadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); MediaMetadata metadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
if (item.title != null) { if (item.mediaMetadata.title != null) {
metadata.putString(MediaMetadata.KEY_TITLE, item.title); metadata.putString(MediaMetadata.KEY_TITLE, item.mediaMetadata.title);
} }
MediaInfo mediaInfo = MediaInfo mediaInfo =
new MediaInfo.Builder(item.uri.toString()) new MediaInfo.Builder(item.playbackProperties.sourceUri.toString())
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType(item.mimeType) .setContentType(item.playbackProperties.mimeType)
.setMetadata(metadata) .setMetadata(metadata)
.setCustomData(getCustomData(item)) .setCustomData(getCustomData(item))
.build(); .build();
@ -71,16 +73,19 @@ public final class DefaultMediaItemConverter implements MediaItemConverter {
try { try {
JSONObject mediaItemJson = customData.getJSONObject(KEY_MEDIA_ITEM); JSONObject mediaItemJson = customData.getJSONObject(KEY_MEDIA_ITEM);
MediaItem.Builder builder = new MediaItem.Builder(); MediaItem.Builder builder = new MediaItem.Builder();
builder.setUri(Uri.parse(mediaItemJson.getString(KEY_URI))); builder.setSourceUri(Uri.parse(mediaItemJson.getString(KEY_URI)));
if (mediaItemJson.has(KEY_TITLE)) { if (mediaItemJson.has(KEY_TITLE)) {
builder.setTitle(mediaItemJson.getString(KEY_TITLE)); com.google.android.exoplayer2.MediaMetadata mediaMetadata =
new com.google.android.exoplayer2.MediaMetadata.Builder()
.setTitle(mediaItemJson.getString(KEY_TITLE))
.build();
builder.setMediaMetadata(mediaMetadata);
} }
if (mediaItemJson.has(KEY_MIME_TYPE)) { if (mediaItemJson.has(KEY_MIME_TYPE)) {
builder.setMimeType(mediaItemJson.getString(KEY_MIME_TYPE)); builder.setMimeType(mediaItemJson.getString(KEY_MIME_TYPE));
} }
if (mediaItemJson.has(KEY_DRM_CONFIGURATION)) { if (mediaItemJson.has(KEY_DRM_CONFIGURATION)) {
builder.setDrmConfiguration( populateDrmConfiguration(mediaItemJson.getJSONObject(KEY_DRM_CONFIGURATION), builder);
getDrmConfiguration(mediaItemJson.getJSONObject(KEY_DRM_CONFIGURATION)));
} }
return builder.build(); return builder.build();
} catch (JSONException e) { } catch (JSONException e) {
@ -88,25 +93,26 @@ public final class DefaultMediaItemConverter implements MediaItemConverter {
} }
} }
private static DrmConfiguration getDrmConfiguration(JSONObject json) throws JSONException { private static void populateDrmConfiguration(JSONObject json, MediaItem.Builder builder)
UUID uuid = UUID.fromString(json.getString(KEY_UUID)); throws JSONException {
Uri licenseUri = Uri.parse(json.getString(KEY_LICENSE_URI)); builder.setDrmUuid(UUID.fromString(json.getString(KEY_UUID)));
builder.setDrmLicenseUri(json.getString(KEY_LICENSE_URI));
JSONObject requestHeadersJson = json.getJSONObject(KEY_REQUEST_HEADERS); JSONObject requestHeadersJson = json.getJSONObject(KEY_REQUEST_HEADERS);
HashMap<String, String> requestHeaders = new HashMap<>(); HashMap<String, String> requestHeaders = new HashMap<>();
for (Iterator<String> iterator = requestHeadersJson.keys(); iterator.hasNext(); ) { for (Iterator<String> iterator = requestHeadersJson.keys(); iterator.hasNext(); ) {
String key = iterator.next(); String key = iterator.next();
requestHeaders.put(key, requestHeadersJson.getString(key)); requestHeaders.put(key, requestHeadersJson.getString(key));
} }
return new DrmConfiguration(uuid, licenseUri, requestHeaders); builder.setDrmLicenseRequestHeaders(requestHeaders);
} }
// Serialization. // Serialization.
private static JSONObject getCustomData(MediaItem item) { private static JSONObject getCustomData(MediaItem mediaItem) {
JSONObject json = new JSONObject(); JSONObject json = new JSONObject();
try { try {
json.put(KEY_MEDIA_ITEM, getMediaItemJson(item)); json.put(KEY_MEDIA_ITEM, getMediaItemJson(mediaItem));
JSONObject playerConfigJson = getPlayerConfigJson(item); @Nullable JSONObject playerConfigJson = getPlayerConfigJson(mediaItem);
if (playerConfigJson != null) { if (playerConfigJson != null) {
json.put(KEY_PLAYER_CONFIG, playerConfigJson); json.put(KEY_PLAYER_CONFIG, playerConfigJson);
} }
@ -116,18 +122,21 @@ public final class DefaultMediaItemConverter implements MediaItemConverter {
return json; return json;
} }
private static JSONObject getMediaItemJson(MediaItem item) throws JSONException { private static JSONObject getMediaItemJson(MediaItem mediaItem) throws JSONException {
Assertions.checkNotNull(mediaItem.playbackProperties);
JSONObject json = new JSONObject(); JSONObject json = new JSONObject();
json.put(KEY_URI, item.uri.toString()); json.put(KEY_TITLE, mediaItem.mediaMetadata.title);
json.put(KEY_TITLE, item.title); json.put(KEY_URI, mediaItem.playbackProperties.sourceUri.toString());
json.put(KEY_MIME_TYPE, item.mimeType); json.put(KEY_MIME_TYPE, mediaItem.playbackProperties.mimeType);
if (item.drmConfiguration != null) { if (mediaItem.playbackProperties.drmConfiguration != null) {
json.put(KEY_DRM_CONFIGURATION, getDrmConfigurationJson(item.drmConfiguration)); json.put(
KEY_DRM_CONFIGURATION,
getDrmConfigurationJson(mediaItem.playbackProperties.drmConfiguration));
} }
return json; return json;
} }
private static JSONObject getDrmConfigurationJson(DrmConfiguration drmConfiguration) private static JSONObject getDrmConfigurationJson(MediaItem.DrmConfiguration drmConfiguration)
throws JSONException { throws JSONException {
JSONObject json = new JSONObject(); JSONObject json = new JSONObject();
json.put(KEY_UUID, drmConfiguration.uuid); json.put(KEY_UUID, drmConfiguration.uuid);
@ -137,11 +146,12 @@ public final class DefaultMediaItemConverter implements MediaItemConverter {
} }
@Nullable @Nullable
private static JSONObject getPlayerConfigJson(MediaItem item) throws JSONException { private static JSONObject getPlayerConfigJson(MediaItem mediaItem) throws JSONException {
DrmConfiguration drmConfiguration = item.drmConfiguration; if (mediaItem.playbackProperties == null
if (drmConfiguration == null) { || mediaItem.playbackProperties.drmConfiguration == null) {
return null; return null;
} }
MediaItem.DrmConfiguration drmConfiguration = mediaItem.playbackProperties.drmConfiguration;
String drmScheme; String drmScheme;
if (C.WIDEVINE_UUID.equals(drmConfiguration.uuid)) { if (C.WIDEVINE_UUID.equals(drmConfiguration.uuid)) {

View File

@ -1,175 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.ext.cast;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
/** Representation of a media item. */
public final class MediaItem {
/** A builder for {@link MediaItem} instances. */
public static final class Builder {
@Nullable private Uri uri;
@Nullable private String title;
@Nullable private String mimeType;
@Nullable private DrmConfiguration drmConfiguration;
/** See {@link MediaItem#uri}. */
public Builder setUri(String uri) {
return setUri(Uri.parse(uri));
}
/** See {@link MediaItem#uri}. */
public Builder setUri(Uri uri) {
this.uri = uri;
return this;
}
/** See {@link MediaItem#title}. */
public Builder setTitle(String title) {
this.title = title;
return this;
}
/** See {@link MediaItem#mimeType}. */
public Builder setMimeType(String mimeType) {
this.mimeType = mimeType;
return this;
}
/** See {@link MediaItem#drmConfiguration}. */
public Builder setDrmConfiguration(DrmConfiguration drmConfiguration) {
this.drmConfiguration = drmConfiguration;
return this;
}
/** Returns a new {@link MediaItem} instance with the current builder values. */
public MediaItem build() {
Assertions.checkNotNull(uri);
return new MediaItem(uri, title, mimeType, drmConfiguration);
}
}
/** DRM configuration for a media item. */
public static final class DrmConfiguration {
/** The UUID of the protection scheme. */
public final UUID uuid;
/**
* Optional license server {@link Uri}. If {@code null} then the license server must be
* specified by the media.
*/
@Nullable public final Uri licenseUri;
/** Headers that should be attached to any license requests. */
public final Map<String, String> requestHeaders;
/**
* Creates an instance.
*
* @param uuid See {@link #uuid}.
* @param licenseUri See {@link #licenseUri}.
* @param requestHeaders See {@link #requestHeaders}.
*/
public DrmConfiguration(
UUID uuid, @Nullable Uri licenseUri, @Nullable Map<String, String> requestHeaders) {
this.uuid = uuid;
this.licenseUri = licenseUri;
this.requestHeaders =
requestHeaders == null
? Collections.emptyMap()
: Collections.unmodifiableMap(requestHeaders);
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
DrmConfiguration other = (DrmConfiguration) obj;
return uuid.equals(other.uuid)
&& Util.areEqual(licenseUri, other.licenseUri)
&& requestHeaders.equals(other.requestHeaders);
}
@Override
public int hashCode() {
int result = uuid.hashCode();
result = 31 * result + (licenseUri != null ? licenseUri.hashCode() : 0);
result = 31 * result + requestHeaders.hashCode();
return result;
}
}
/** The media {@link Uri}. */
public final Uri uri;
/** The title of the item, or {@code null} if unspecified. */
@Nullable public final String title;
/** The mime type for the media, or {@code null} if unspecified. */
@Nullable public final String mimeType;
/** Optional {@link DrmConfiguration} for the media. */
@Nullable public final DrmConfiguration drmConfiguration;
private MediaItem(
Uri uri,
@Nullable String title,
@Nullable String mimeType,
@Nullable DrmConfiguration drmConfiguration) {
this.uri = uri;
this.title = title;
this.mimeType = mimeType;
this.drmConfiguration = drmConfiguration;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
MediaItem other = (MediaItem) obj;
return uri.equals(other.uri)
&& Util.areEqual(title, other.title)
&& Util.areEqual(mimeType, other.mimeType)
&& Util.areEqual(drmConfiguration, other.drmConfiguration);
}
@Override
public int hashCode() {
int result = uri.hashCode();
result = 31 * result + (title == null ? 0 : title.hashCode());
result = 31 * result + (drmConfiguration == null ? 0 : drmConfiguration.hashCode());
result = 31 * result + (mimeType == null ? 0 : mimeType.hashCode());
return result;
}
}

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.ext.cast; package com.google.android.exoplayer2.ext.cast;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.gms.cast.MediaQueueItem; import com.google.android.gms.cast.MediaQueueItem;
/** Converts between {@link MediaItem} and the Cast SDK's {@link MediaQueueItem}. */ /** Converts between {@link MediaItem} and the Cast SDK's {@link MediaQueueItem}. */

View File

@ -20,7 +20,9 @@ import static com.google.common.truth.Truth.assertThat;
import android.net.Uri; 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.ext.cast.MediaItem.DrmConfiguration; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.MediaMetadata;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.gms.cast.MediaQueueItem; import com.google.android.gms.cast.MediaQueueItem;
import java.util.Collections; import java.util.Collections;
import org.junit.Test; import org.junit.Test;
@ -33,7 +35,8 @@ public class DefaultMediaItemConverterTest {
@Test @Test
public void serialize_deserialize_minimal() { public void serialize_deserialize_minimal() {
MediaItem.Builder builder = new MediaItem.Builder(); MediaItem.Builder builder = new MediaItem.Builder();
MediaItem item = builder.setUri(Uri.parse("http://example.com")).setMimeType("mime").build(); MediaItem item =
builder.setSourceUri("http://example.com").setMimeType(MimeTypes.APPLICATION_MPD).build();
DefaultMediaItemConverter converter = new DefaultMediaItemConverter(); DefaultMediaItemConverter converter = new DefaultMediaItemConverter();
MediaQueueItem queueItem = converter.toMediaQueueItem(item); MediaQueueItem queueItem = converter.toMediaQueueItem(item);
@ -47,14 +50,12 @@ public class DefaultMediaItemConverterTest {
MediaItem.Builder builder = new MediaItem.Builder(); MediaItem.Builder builder = new MediaItem.Builder();
MediaItem item = MediaItem item =
builder builder
.setUri(Uri.parse("http://example.com")) .setSourceUri(Uri.parse("http://example.com"))
.setTitle("title") .setMediaMetadata(new MediaMetadata.Builder().build())
.setMimeType("mime") .setMimeType(MimeTypes.APPLICATION_MPD)
.setDrmConfiguration( .setDrmUuid(C.WIDEVINE_UUID)
new DrmConfiguration( .setDrmLicenseUri("http://license.com")
C.WIDEVINE_UUID, .setDrmLicenseRequestHeaders(Collections.singletonMap("key", "value"))
Uri.parse("http://license.com"),
Collections.singletonMap("key", "value")))
.build(); .build();
DefaultMediaItemConverter converter = new DefaultMediaItemConverter(); DefaultMediaItemConverter converter = new DefaultMediaItemConverter();

View File

@ -1,86 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.ext.cast;
import static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.MimeTypes;
import java.util.HashMap;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Test for {@link MediaItem}. */
@RunWith(AndroidJUnit4.class)
public class MediaItemTest {
@Test
public void buildMediaItem_doesNotChangeState() {
MediaItem.Builder builder = new MediaItem.Builder();
MediaItem item1 =
builder
.setUri(Uri.parse("http://example.com"))
.setTitle("title")
.setMimeType(MimeTypes.AUDIO_MP4)
.build();
MediaItem item2 = builder.build();
assertThat(item1).isEqualTo(item2);
}
@Test
public void equals_withEqualDrmSchemes_returnsTrue() {
MediaItem.Builder builder1 = new MediaItem.Builder();
MediaItem mediaItem1 =
builder1
.setUri(Uri.parse("www.google.com"))
.setDrmConfiguration(buildDrmConfiguration(1))
.build();
MediaItem.Builder builder2 = new MediaItem.Builder();
MediaItem mediaItem2 =
builder2
.setUri(Uri.parse("www.google.com"))
.setDrmConfiguration(buildDrmConfiguration(1))
.build();
assertThat(mediaItem1).isEqualTo(mediaItem2);
}
@Test
public void equals_withDifferentDrmRequestHeaders_returnsFalse() {
MediaItem.Builder builder1 = new MediaItem.Builder();
MediaItem mediaItem1 =
builder1
.setUri(Uri.parse("www.google.com"))
.setDrmConfiguration(buildDrmConfiguration(1))
.build();
MediaItem.Builder builder2 = new MediaItem.Builder();
MediaItem mediaItem2 =
builder2
.setUri(Uri.parse("www.google.com"))
.setDrmConfiguration(buildDrmConfiguration(2))
.build();
assertThat(mediaItem1).isNotEqualTo(mediaItem2);
}
private static MediaItem.DrmConfiguration buildDrmConfiguration(int seed) {
HashMap<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("key1", "value1");
requestHeaders.put("key2", "value2" + seed);
return new MediaItem.DrmConfiguration(
C.WIDEVINE_UUID, Uri.parse("www.uri1.com"), requestHeaders);
}
}

View File

@ -62,6 +62,7 @@ public final class MediaItem {
private boolean drmMultiSession; private boolean drmMultiSession;
private List<StreamKey> streamKeys; private List<StreamKey> streamKeys;
@Nullable private Object tag; @Nullable private Object tag;
@Nullable private MediaMetadata mediaMetadata;
/** Creates a builder. */ /** Creates a builder. */
public Builder() { public Builder() {
@ -199,6 +200,12 @@ public final class MediaItem {
return this; return this;
} }
/** Sets the media metadata. */
public Builder setMediaMetadata(MediaMetadata mediaMetadata) {
this.mediaMetadata = mediaMetadata;
return this;
}
/** /**
* Returns a new {@link MediaItem} instance with the current builder values. * Returns a new {@link MediaItem} instance with the current builder values.
*/ */
@ -218,7 +225,10 @@ public final class MediaItem {
tag); tag);
mediaId = mediaId != null ? mediaId : sourceUri.toString(); mediaId = mediaId != null ? mediaId : sourceUri.toString();
} }
return new MediaItem(Assertions.checkNotNull(mediaId), playbackProperties); return new MediaItem(
Assertions.checkNotNull(mediaId),
playbackProperties,
mediaMetadata != null ? mediaMetadata : new MediaMetadata.Builder().build());
} }
} }
@ -240,15 +250,7 @@ public final class MediaItem {
/** Whether the drm configuration is multi session enabled. */ /** Whether the drm configuration is multi session enabled. */
public final boolean multiSession; public final boolean multiSession;
/** private DrmConfiguration(
* Creates an instance.
*
* @param uuid See {@link #uuid}.
* @param licenseUri See {@link #licenseUri}.
* @param requestHeaders See {@link #requestHeaders}.
* @param multiSession See {@link #multiSession}.
*/
public DrmConfiguration(
UUID uuid, UUID uuid,
@Nullable Uri licenseUri, @Nullable Uri licenseUri,
Map<String, String> requestHeaders, Map<String, String> requestHeaders,
@ -312,7 +314,7 @@ public final class MediaItem {
*/ */
@Nullable public final Object tag; @Nullable public final Object tag;
public PlaybackProperties( private PlaybackProperties(
Uri sourceUri, Uri sourceUri,
@Nullable String mimeType, @Nullable String mimeType,
@Nullable DrmConfiguration drmConfiguration, @Nullable DrmConfiguration drmConfiguration,
@ -356,12 +358,19 @@ public final class MediaItem {
/** Identifies the media item. */ /** Identifies the media item. */
public final String mediaId; public final String mediaId;
/** Optional playback properties. */ /** Optional playback properties. Maybe be {@code null} if shared over process boundaries. */
@Nullable public final PlaybackProperties playbackProperties; @Nullable public final PlaybackProperties playbackProperties;
private MediaItem(String mediaId, @Nullable PlaybackProperties playbackProperties) { /** The media metadata. */
public final MediaMetadata mediaMetadata;
private MediaItem(
String mediaId,
@Nullable PlaybackProperties playbackProperties,
MediaMetadata mediaMetadata) {
this.mediaId = mediaId; this.mediaId = mediaId;
this.playbackProperties = playbackProperties; this.playbackProperties = playbackProperties;
this.mediaMetadata = mediaMetadata;
} }
@Override @Override
@ -376,13 +385,15 @@ public final class MediaItem {
MediaItem other = (MediaItem) o; MediaItem other = (MediaItem) o;
return Util.areEqual(mediaId, other.mediaId) return Util.areEqual(mediaId, other.mediaId)
&& Util.areEqual(playbackProperties, other.playbackProperties); && Util.areEqual(playbackProperties, other.playbackProperties)
&& Util.areEqual(mediaMetadata, other.mediaMetadata);
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result = mediaId.hashCode(); int result = mediaId.hashCode();
result = 31 * result + (playbackProperties != null ? playbackProperties.hashCode() : 0); result = 31 * result + (playbackProperties != null ? playbackProperties.hashCode() : 0);
result = 31 * result + mediaMetadata.hashCode();
return result; return result;
} }
} }

View File

@ -0,0 +1,65 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.Util;
/** Metadata of the {@link MediaItem}. */
public final class MediaMetadata {
/** A builder for {@link MediaMetadata} instances. */
public static final class Builder {
@Nullable private String title;
/** Sets the optional title. */
public Builder setTitle(@Nullable String title) {
this.title = title;
return this;
}
/** Returns a new {@link MediaMetadata} instance with the current builder values. */
public MediaMetadata build() {
return new MediaMetadata(title);
}
}
/** Optional title. */
@Nullable public final String title;
private MediaMetadata(@Nullable String title) {
this.title = title;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
MediaMetadata other = (MediaMetadata) obj;
return Util.areEqual(title, other.title);
}
@Override
public int hashCode() {
return title == null ? 0 : title.hashCode();
}
}

View File

@ -48,6 +48,7 @@ public class MediaItemTest {
assertThat(mediaItem.playbackProperties.sourceUri.toString()).isEqualTo(URI_STRING); assertThat(mediaItem.playbackProperties.sourceUri.toString()).isEqualTo(URI_STRING);
assertThat(mediaItem.mediaId).isEqualTo(URI_STRING); assertThat(mediaItem.mediaId).isEqualTo(URI_STRING);
assertThat(mediaItem.mediaMetadata).isNotNull();
} }
@Test @Test
@ -144,4 +145,14 @@ public class MediaItemTest {
assertThat(mediaItem.playbackProperties.tag).isEqualTo(tag); assertThat(mediaItem.playbackProperties.tag).isEqualTo(tag);
} }
@Test
public void builderSetMediaMetadata_setsMetadata() {
MediaMetadata mediaMetadata = new MediaMetadata.Builder().setTitle("title").build();
MediaItem mediaItem =
new MediaItem.Builder().setSourceUri(URI_STRING).setMediaMetadata(mediaMetadata).build();
assertThat(mediaItem.mediaMetadata).isEqualTo(mediaMetadata);
}
} }

View File

@ -0,0 +1,43 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Unit test for {@link MediaMetadata}. */
@RunWith(AndroidJUnit4.class)
public class MediaMetadataTest {
@Test
public void builder_minimal_correctDefaults() {
MediaMetadata mediaMetadata = new MediaMetadata.Builder().build();
assertThat(mediaMetadata.title).isNull();
}
@Test
public void builderSetTitle_setsTitle() {
String title = "title";
MediaMetadata mediaMetadata = new MediaMetadata.Builder().setTitle(title).build();
assertThat(mediaMetadata.title).isEqualTo(title);
}
}