Commit 961adb7e by olly Committed by Oliver Woodman

Cast: Add MediaItemConverter

For now this just moves some code from the demo app to the extension.
Eventually the goal would be to have CastPlayer playlist methods take
MediaItem, have CastPlayer convert them internally to MediaQueueItem
for sending to the Cast SDK, and also allow reverse conversion so we
can reconstruct MediaItems from the Cast SDK's queue.

PiperOrigin-RevId: 260548020
parent 06f94815
...@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.castdemo; ...@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.castdemo;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.Nullable;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
...@@ -30,7 +29,9 @@ import com.google.android.exoplayer2.SimpleExoPlayer; ...@@ -30,7 +29,9 @@ 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.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.MediaItem; import com.google.android.exoplayer2.ext.cast.MediaItem;
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.MediaSource;
...@@ -41,13 +42,9 @@ import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; ...@@ -41,13 +42,9 @@ import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
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.MediaInfo;
import com.google.android.gms.cast.MediaMetadata;
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 org.json.JSONException;
import org.json.JSONObject;
/** 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 {
...@@ -70,6 +67,7 @@ import org.json.JSONObject; ...@@ -70,6 +67,7 @@ import org.json.JSONObject;
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 int currentItemIndex; private int currentItemIndex;
private Player currentPlayer; private Player currentPlayer;
...@@ -95,6 +93,7 @@ import org.json.JSONObject; ...@@ -95,6 +93,7 @@ import org.json.JSONObject;
mediaQueue = new ArrayList<>(); mediaQueue = new ArrayList<>();
currentItemIndex = C.INDEX_UNSET; currentItemIndex = C.INDEX_UNSET;
concatenatingMediaSource = new ConcatenatingMediaSource(); concatenatingMediaSource = new ConcatenatingMediaSource();
mediaItemConverter = new DefaultMediaItemConverter();
exoPlayer = ExoPlayerFactory.newSimpleInstance(context); exoPlayer = ExoPlayerFactory.newSimpleInstance(context);
exoPlayer.addListener(this); exoPlayer.addListener(this);
...@@ -133,7 +132,7 @@ import org.json.JSONObject; ...@@ -133,7 +132,7 @@ import org.json.JSONObject;
mediaQueue.add(item); mediaQueue.add(item);
concatenatingMediaSource.addMediaSource(buildMediaSource(item)); concatenatingMediaSource.addMediaSource(buildMediaSource(item));
if (currentPlayer == castPlayer) { if (currentPlayer == castPlayer) {
castPlayer.addItems(buildMediaQueueItem(item)); castPlayer.addItems(mediaItemConverter.toMediaQueueItem(item));
} }
} }
...@@ -344,7 +343,7 @@ import org.json.JSONObject; ...@@ -344,7 +343,7 @@ import org.json.JSONObject;
if (currentPlayer == castPlayer && castPlayer.getCurrentTimeline().isEmpty()) { if (currentPlayer == castPlayer && castPlayer.getCurrentTimeline().isEmpty()) {
MediaQueueItem[] items = new MediaQueueItem[mediaQueue.size()]; MediaQueueItem[] items = new MediaQueueItem[mediaQueue.size()];
for (int i = 0; i < items.length; i++) { for (int i = 0; i < items.length; i++) {
items[i] = buildMediaQueueItem(mediaQueue.get(i)); items[i] = mediaItemConverter.toMediaQueueItem(mediaQueue.get(i));
} }
castPlayer.loadItems(items, itemIndex, positionMs, Player.REPEAT_MODE_OFF); castPlayer.loadItems(items, itemIndex, positionMs, Player.REPEAT_MODE_OFF);
} else { } else {
...@@ -380,50 +379,4 @@ import org.json.JSONObject; ...@@ -380,50 +379,4 @@ import org.json.JSONObject;
throw new IllegalArgumentException("mimeType is unsupported: " + mimeType); throw new IllegalArgumentException("mimeType is unsupported: " + mimeType);
} }
} }
private static MediaQueueItem buildMediaQueueItem(MediaItem item) {
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
movieMetadata.putString(MediaMetadata.KEY_TITLE, item.title);
MediaInfo.Builder mediaInfoBuilder =
new MediaInfo.Builder(item.uri.toString())
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType(item.mimeType)
.setMetadata(movieMetadata);
MediaItem.DrmConfiguration drmConfiguration = item.drmConfiguration;
if (drmConfiguration != null) {
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 drmConfigurationJson = getDrmConfigurationJson(drmConfiguration);
if (drmConfigurationJson != null) {
mediaInfoBuilder.setCustomData(drmConfigurationJson);
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
return new MediaQueueItem.Builder(mediaInfoBuilder.build()).build();
}
@Nullable
private static JSONObject getDrmConfigurationJson(MediaItem.DrmConfiguration drmConfiguration)
throws JSONException {
String drmScheme;
if (C.WIDEVINE_UUID.equals(drmConfiguration.uuid)) {
drmScheme = "widevine";
} else if (C.PLAYREADY_UUID.equals(drmConfiguration.uuid)) {
drmScheme = "playready";
} else {
return null;
}
JSONObject exoplayerConfig =
new JSONObject().put("withCredentials", false).put("protectionSystem", drmScheme);
if (drmConfiguration.licenseUri != null) {
exoplayerConfig.put("licenseUrl", drmConfiguration.licenseUri);
}
if (!drmConfiguration.requestHeaders.isEmpty()) {
exoplayerConfig.put("headers", new JSONObject(drmConfiguration.requestHeaders));
}
return new JSONObject().put("exoPlayerConfig", exoplayerConfig);
}
} }
/*
* Copyright (C) 2019 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 com.google.android.exoplayer2.C;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.cast.MediaQueueItem;
import org.json.JSONException;
import org.json.JSONObject;
/** Default {@link MediaItemConverter} implementation. */
public final class DefaultMediaItemConverter implements MediaItemConverter {
@Override
public MediaQueueItem toMediaQueueItem(MediaItem item) {
if (item.mimeType == null) {
throw new IllegalArgumentException("The item must specify its mimeType");
}
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
if (item.title != null) {
movieMetadata.putString(MediaMetadata.KEY_TITLE, item.title);
}
MediaInfo mediaInfo =
new MediaInfo.Builder(item.uri.toString())
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType(item.mimeType)
.setMetadata(movieMetadata)
.setCustomData(getCustomData(item))
.build();
return new MediaQueueItem.Builder(mediaInfo).build();
}
private static JSONObject getCustomData(MediaItem item) {
JSONObject customData = new JSONObject();
MediaItem.DrmConfiguration drmConfiguration = item.drmConfiguration;
if (drmConfiguration == null) {
return customData;
}
String drmScheme;
if (C.WIDEVINE_UUID.equals(drmConfiguration.uuid)) {
drmScheme = "widevine";
} else if (C.PLAYREADY_UUID.equals(drmConfiguration.uuid)) {
drmScheme = "playready";
} else {
return customData;
}
JSONObject exoPlayerConfig = new JSONObject();
try {
exoPlayerConfig.put("withCredentials", false);
exoPlayerConfig.put("protectionSystem", drmScheme);
if (drmConfiguration.licenseUri != null) {
exoPlayerConfig.put("licenseUrl", drmConfiguration.licenseUri);
}
if (!drmConfiguration.requestHeaders.isEmpty()) {
exoPlayerConfig.put("headers", new JSONObject(drmConfiguration.requestHeaders));
}
customData.put("exoPlayerConfig", exoPlayerConfig);
} catch (JSONException e) {
throw new RuntimeException(e);
}
return customData;
}
}
/*
* Copyright (C) 2019 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 com.google.android.gms.cast.MediaQueueItem;
/** Converts between {@link MediaItem} and the Cast SDK's {@link MediaQueueItem}. */
public interface MediaItemConverter {
/**
* Converts a {@link MediaItem} to a {@link MediaQueueItem}.
*
* @param mediaItem The {@link MediaItem}.
* @return An equivalent {@link MediaQueueItem}.
*/
MediaQueueItem toMediaQueueItem(MediaItem mediaItem);
// TODO: Add toMediaItem to convert in the opposite direction.
}
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