Automated g4 rollback of changelist 233073011.
*** Reason for rollback *** Pending discussions about cast *** Original change description *** Remove default-receiver-related classes from Cast demo app *** PiperOrigin-RevId: 233790699
This commit is contained in:
parent
04e6061498
commit
5c2aa4bd9e
@ -47,6 +47,22 @@ android {
|
|||||||
// The demo app isn't indexed and doesn't have translations.
|
// The demo app isn't indexed and doesn't have translations.
|
||||||
disable 'GoogleAppIndexingWarning','MissingTranslation'
|
disable 'GoogleAppIndexingWarning','MissingTranslation'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flavorDimensions "receiver"
|
||||||
|
|
||||||
|
productFlavors {
|
||||||
|
exoCast {
|
||||||
|
dimension "receiver"
|
||||||
|
manifestPlaceholders =
|
||||||
|
[castOptionsProvider: "com.google.android.exoplayer2.ext.cast.ExoCastOptionsProvider"]
|
||||||
|
}
|
||||||
|
defaultCast {
|
||||||
|
dimension "receiver"
|
||||||
|
manifestPlaceholders =
|
||||||
|
[castOptionsProvider: "com.google.android.exoplayer2.ext.cast.DefaultCastOptionsProvider"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
android:largeHeap="true" android:allowBackup="false">
|
android:largeHeap="true" android:allowBackup="false">
|
||||||
|
|
||||||
<meta-data android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
<meta-data android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||||
android:value="com.google.android.exoplayer2.ext.cast.ExoCastOptionsProvider" />
|
android:value="${castOptionsProvider}" />
|
||||||
|
|
||||||
<activity android:name="com.google.android.exoplayer2.castdemo.MainActivity"
|
<activity android:name="com.google.android.exoplayer2.castdemo.MainActivity"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
|
||||||
|
@ -0,0 +1,405 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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.castdemo;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.DefaultRenderersFactory;
|
||||||
|
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
|
import com.google.android.exoplayer2.Player.DiscontinuityReason;
|
||||||
|
import com.google.android.exoplayer2.Player.EventListener;
|
||||||
|
import com.google.android.exoplayer2.Player.TimelineChangeReason;
|
||||||
|
import com.google.android.exoplayer2.RenderersFactory;
|
||||||
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
|
import com.google.android.exoplayer2.Timeline;
|
||||||
|
import com.google.android.exoplayer2.Timeline.Period;
|
||||||
|
import com.google.android.exoplayer2.ext.cast.CastPlayer;
|
||||||
|
import com.google.android.exoplayer2.ext.cast.MediaItem;
|
||||||
|
import com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener;
|
||||||
|
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||||
|
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.ui.PlayerControlView;
|
||||||
|
import com.google.android.exoplayer2.ui.PlayerView;
|
||||||
|
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.framework.CastContext;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/** Manages players and an internal media queue for the ExoPlayer/Cast demo app. */
|
||||||
|
/* package */ class DefaultReceiverPlayerManager
|
||||||
|
implements PlayerManager, EventListener, SessionAvailabilityListener {
|
||||||
|
|
||||||
|
private static final String USER_AGENT = "ExoCastDemoPlayer";
|
||||||
|
private static final DefaultHttpDataSourceFactory DATA_SOURCE_FACTORY =
|
||||||
|
new DefaultHttpDataSourceFactory(USER_AGENT);
|
||||||
|
|
||||||
|
private final PlayerView localPlayerView;
|
||||||
|
private final PlayerControlView castControlView;
|
||||||
|
private final SimpleExoPlayer exoPlayer;
|
||||||
|
private final CastPlayer castPlayer;
|
||||||
|
private final ArrayList<MediaItem> mediaQueue;
|
||||||
|
private final Listener listener;
|
||||||
|
private final ConcatenatingMediaSource concatenatingMediaSource;
|
||||||
|
|
||||||
|
private boolean castMediaQueueCreationPending;
|
||||||
|
private int currentItemIndex;
|
||||||
|
private Player currentPlayer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new manager for {@link SimpleExoPlayer} and {@link CastPlayer}.
|
||||||
|
*
|
||||||
|
* @param listener A {@link Listener} for queue position changes.
|
||||||
|
* @param localPlayerView The {@link PlayerView} for local playback.
|
||||||
|
* @param castControlView The {@link PlayerControlView} to control remote playback.
|
||||||
|
* @param context A {@link Context}.
|
||||||
|
* @param castContext The {@link CastContext}.
|
||||||
|
*/
|
||||||
|
public DefaultReceiverPlayerManager(
|
||||||
|
Listener listener,
|
||||||
|
PlayerView localPlayerView,
|
||||||
|
PlayerControlView castControlView,
|
||||||
|
Context context,
|
||||||
|
CastContext castContext) {
|
||||||
|
this.listener = listener;
|
||||||
|
this.localPlayerView = localPlayerView;
|
||||||
|
this.castControlView = castControlView;
|
||||||
|
mediaQueue = new ArrayList<>();
|
||||||
|
currentItemIndex = C.INDEX_UNSET;
|
||||||
|
concatenatingMediaSource = new ConcatenatingMediaSource();
|
||||||
|
|
||||||
|
DefaultTrackSelector trackSelector = new DefaultTrackSelector();
|
||||||
|
RenderersFactory renderersFactory = new DefaultRenderersFactory(context);
|
||||||
|
exoPlayer = ExoPlayerFactory.newSimpleInstance(context, renderersFactory, trackSelector);
|
||||||
|
exoPlayer.addListener(this);
|
||||||
|
localPlayerView.setPlayer(exoPlayer);
|
||||||
|
|
||||||
|
castPlayer = new CastPlayer(castContext);
|
||||||
|
castPlayer.addListener(this);
|
||||||
|
castPlayer.setSessionAvailabilityListener(this);
|
||||||
|
castControlView.setPlayer(castPlayer);
|
||||||
|
|
||||||
|
setCurrentPlayer(castPlayer.isCastSessionAvailable() ? castPlayer : exoPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue manipulation methods.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays a specified queue item in the current player.
|
||||||
|
*
|
||||||
|
* @param itemIndex The index of the item to play.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void selectQueueItem(int itemIndex) {
|
||||||
|
setCurrentItem(itemIndex, C.TIME_UNSET, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the index of the currently played item. */
|
||||||
|
@Override
|
||||||
|
public int getCurrentItemIndex() {
|
||||||
|
return currentItemIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends {@code item} to the media queue.
|
||||||
|
*
|
||||||
|
* @param item The {@link MediaItem} to append.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addItem(MediaItem item) {
|
||||||
|
mediaQueue.add(item);
|
||||||
|
concatenatingMediaSource.addMediaSource(buildMediaSource(item));
|
||||||
|
if (currentPlayer == castPlayer) {
|
||||||
|
castPlayer.addItems(buildMediaQueueItem(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the size of the media queue. */
|
||||||
|
@Override
|
||||||
|
public int getMediaQueueSize() {
|
||||||
|
return mediaQueue.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item at the given index in the media queue.
|
||||||
|
*
|
||||||
|
* @param position The index of the item.
|
||||||
|
* @return The item at the given index in the media queue.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public MediaItem getItem(int position) {
|
||||||
|
return mediaQueue.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the item at the given index from the media queue.
|
||||||
|
*
|
||||||
|
* @param item The item to remove.
|
||||||
|
* @return Whether the removal was successful.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean removeItem(MediaItem item) {
|
||||||
|
int itemIndex = mediaQueue.indexOf(item);
|
||||||
|
if (itemIndex == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
concatenatingMediaSource.removeMediaSource(itemIndex);
|
||||||
|
if (currentPlayer == castPlayer) {
|
||||||
|
if (castPlayer.getPlaybackState() != Player.STATE_IDLE) {
|
||||||
|
Timeline castTimeline = castPlayer.getCurrentTimeline();
|
||||||
|
if (castTimeline.getPeriodCount() <= itemIndex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
castPlayer.removeItem((int) castTimeline.getPeriod(itemIndex, new Period()).id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mediaQueue.remove(itemIndex);
|
||||||
|
if (itemIndex == currentItemIndex && itemIndex == mediaQueue.size()) {
|
||||||
|
maybeSetCurrentItemAndNotify(C.INDEX_UNSET);
|
||||||
|
} else if (itemIndex < currentItemIndex) {
|
||||||
|
maybeSetCurrentItemAndNotify(currentItemIndex - 1);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves an item within the queue.
|
||||||
|
*
|
||||||
|
* @param item The item to move.
|
||||||
|
* @param toIndex The target index of the item in the queue.
|
||||||
|
* @return Whether the item move was successful.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean moveItem(MediaItem item, int toIndex) {
|
||||||
|
int fromIndex = mediaQueue.indexOf(item);
|
||||||
|
if (fromIndex == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Player update.
|
||||||
|
concatenatingMediaSource.moveMediaSource(fromIndex, toIndex);
|
||||||
|
if (currentPlayer == castPlayer && castPlayer.getPlaybackState() != Player.STATE_IDLE) {
|
||||||
|
Timeline castTimeline = castPlayer.getCurrentTimeline();
|
||||||
|
int periodCount = castTimeline.getPeriodCount();
|
||||||
|
if (periodCount <= fromIndex || periodCount <= toIndex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int elementId = (int) castTimeline.getPeriod(fromIndex, new Period()).id;
|
||||||
|
castPlayer.moveItem(elementId, toIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaQueue.add(toIndex, mediaQueue.remove(fromIndex));
|
||||||
|
|
||||||
|
// Index update.
|
||||||
|
if (fromIndex == currentItemIndex) {
|
||||||
|
maybeSetCurrentItemAndNotify(toIndex);
|
||||||
|
} else if (fromIndex < currentItemIndex && toIndex >= currentItemIndex) {
|
||||||
|
maybeSetCurrentItemAndNotify(currentItemIndex - 1);
|
||||||
|
} else if (fromIndex > currentItemIndex && toIndex <= currentItemIndex) {
|
||||||
|
maybeSetCurrentItemAndNotify(currentItemIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches a given {@link KeyEvent} to the corresponding view of the current player.
|
||||||
|
*
|
||||||
|
* @param event The {@link KeyEvent}.
|
||||||
|
* @return Whether the event was handled by the target view.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||||
|
if (currentPlayer == exoPlayer) {
|
||||||
|
return localPlayerView.dispatchKeyEvent(event);
|
||||||
|
} else /* currentPlayer == castPlayer */ {
|
||||||
|
return castControlView.dispatchKeyEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Releases the manager and the players that it holds. */
|
||||||
|
@Override
|
||||||
|
public void release() {
|
||||||
|
currentItemIndex = C.INDEX_UNSET;
|
||||||
|
mediaQueue.clear();
|
||||||
|
concatenatingMediaSource.clear();
|
||||||
|
castPlayer.setSessionAvailabilityListener(null);
|
||||||
|
castPlayer.release();
|
||||||
|
localPlayerView.setPlayer(null);
|
||||||
|
exoPlayer.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player.EventListener implementation.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||||
|
updateCurrentItemIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPositionDiscontinuity(@DiscontinuityReason int reason) {
|
||||||
|
updateCurrentItemIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTimelineChanged(
|
||||||
|
Timeline timeline, @Nullable Object manifest, @TimelineChangeReason int reason) {
|
||||||
|
updateCurrentItemIndex();
|
||||||
|
if (currentPlayer == castPlayer && timeline.isEmpty()) {
|
||||||
|
castMediaQueueCreationPending = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CastPlayer.SessionAvailabilityListener implementation.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCastSessionAvailable() {
|
||||||
|
setCurrentPlayer(castPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCastSessionUnavailable() {
|
||||||
|
setCurrentPlayer(exoPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal methods.
|
||||||
|
|
||||||
|
private void updateCurrentItemIndex() {
|
||||||
|
int playbackState = currentPlayer.getPlaybackState();
|
||||||
|
maybeSetCurrentItemAndNotify(
|
||||||
|
playbackState != Player.STATE_IDLE && playbackState != Player.STATE_ENDED
|
||||||
|
? currentPlayer.getCurrentWindowIndex()
|
||||||
|
: C.INDEX_UNSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCurrentPlayer(Player currentPlayer) {
|
||||||
|
if (this.currentPlayer == currentPlayer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// View management.
|
||||||
|
if (currentPlayer == exoPlayer) {
|
||||||
|
localPlayerView.setVisibility(View.VISIBLE);
|
||||||
|
castControlView.hide();
|
||||||
|
} else /* currentPlayer == castPlayer */ {
|
||||||
|
localPlayerView.setVisibility(View.GONE);
|
||||||
|
castControlView.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player state management.
|
||||||
|
long playbackPositionMs = C.TIME_UNSET;
|
||||||
|
int windowIndex = C.INDEX_UNSET;
|
||||||
|
boolean playWhenReady = false;
|
||||||
|
if (this.currentPlayer != null) {
|
||||||
|
int playbackState = this.currentPlayer.getPlaybackState();
|
||||||
|
if (playbackState != Player.STATE_ENDED) {
|
||||||
|
playbackPositionMs = this.currentPlayer.getCurrentPosition();
|
||||||
|
playWhenReady = this.currentPlayer.getPlayWhenReady();
|
||||||
|
windowIndex = this.currentPlayer.getCurrentWindowIndex();
|
||||||
|
if (windowIndex != currentItemIndex) {
|
||||||
|
playbackPositionMs = C.TIME_UNSET;
|
||||||
|
windowIndex = currentItemIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.currentPlayer.stop(true);
|
||||||
|
} else {
|
||||||
|
// This is the initial setup. No need to save any state.
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentPlayer = currentPlayer;
|
||||||
|
|
||||||
|
// Media queue management.
|
||||||
|
castMediaQueueCreationPending = currentPlayer == castPlayer;
|
||||||
|
if (currentPlayer == exoPlayer) {
|
||||||
|
exoPlayer.prepare(concatenatingMediaSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Playback transition.
|
||||||
|
if (windowIndex != C.INDEX_UNSET) {
|
||||||
|
setCurrentItem(windowIndex, playbackPositionMs, playWhenReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts playback of the item at the given position.
|
||||||
|
*
|
||||||
|
* @param itemIndex The index of the item to play.
|
||||||
|
* @param positionMs The position at which playback should start.
|
||||||
|
* @param playWhenReady Whether the player should proceed when ready to do so.
|
||||||
|
*/
|
||||||
|
private void setCurrentItem(int itemIndex, long positionMs, boolean playWhenReady) {
|
||||||
|
maybeSetCurrentItemAndNotify(itemIndex);
|
||||||
|
if (castMediaQueueCreationPending) {
|
||||||
|
MediaQueueItem[] items = new MediaQueueItem[mediaQueue.size()];
|
||||||
|
for (int i = 0; i < items.length; i++) {
|
||||||
|
items[i] = buildMediaQueueItem(mediaQueue.get(i));
|
||||||
|
}
|
||||||
|
castMediaQueueCreationPending = false;
|
||||||
|
castPlayer.loadItems(items, itemIndex, positionMs, Player.REPEAT_MODE_OFF);
|
||||||
|
} else {
|
||||||
|
currentPlayer.seekTo(itemIndex, positionMs);
|
||||||
|
currentPlayer.setPlayWhenReady(playWhenReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeSetCurrentItemAndNotify(int currentItemIndex) {
|
||||||
|
if (this.currentItemIndex != currentItemIndex) {
|
||||||
|
int oldIndex = this.currentItemIndex;
|
||||||
|
this.currentItemIndex = currentItemIndex;
|
||||||
|
listener.onQueuePositionChanged(oldIndex, currentItemIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MediaSource buildMediaSource(MediaItem item) {
|
||||||
|
Uri uri = item.media.uri;
|
||||||
|
switch (item.mimeType) {
|
||||||
|
case DemoUtil.MIME_TYPE_SS:
|
||||||
|
return new SsMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri);
|
||||||
|
case DemoUtil.MIME_TYPE_DASH:
|
||||||
|
return new DashMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri);
|
||||||
|
case DemoUtil.MIME_TYPE_HLS:
|
||||||
|
return new HlsMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri);
|
||||||
|
case DemoUtil.MIME_TYPE_VIDEO_MP4:
|
||||||
|
return new ProgressiveMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri);
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Unsupported type: " + item.mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MediaQueueItem buildMediaQueueItem(MediaItem item) {
|
||||||
|
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
|
||||||
|
movieMetadata.putString(MediaMetadata.KEY_TITLE, item.title);
|
||||||
|
MediaInfo mediaInfo =
|
||||||
|
new MediaInfo.Builder(item.media.uri.toString())
|
||||||
|
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
||||||
|
.setContentType(item.mimeType)
|
||||||
|
.setMetadata(movieMetadata)
|
||||||
|
.build();
|
||||||
|
return new MediaQueueItem.Builder(mediaInfo).build();
|
||||||
|
}
|
||||||
|
}
|
@ -39,6 +39,7 @@ import com.google.android.exoplayer2.SimpleExoPlayer;
|
|||||||
import com.google.android.exoplayer2.ext.cast.MediaItem;
|
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.gms.cast.CastMediaControlIntent;
|
||||||
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;
|
||||||
@ -117,13 +118,29 @@ public class MainActivity extends AppCompatActivity
|
|||||||
// There is no Cast context to work with. Do nothing.
|
// There is no Cast context to work with. Do nothing.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
String applicationId = castContext.getCastOptions().getReceiverApplicationId();
|
||||||
|
switch (applicationId) {
|
||||||
|
case CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID:
|
||||||
playerManager =
|
playerManager =
|
||||||
new PlayerManager(
|
new DefaultReceiverPlayerManager(
|
||||||
/* listener= */ this,
|
/* listener= */ this,
|
||||||
localPlayerView,
|
localPlayerView,
|
||||||
castControlView,
|
castControlView,
|
||||||
/* context= */ this,
|
/* context= */ this,
|
||||||
castContext);
|
castContext);
|
||||||
|
break;
|
||||||
|
case ExoCastOptionsProvider.RECEIVER_ID:
|
||||||
|
playerManager =
|
||||||
|
new ExoCastPlayerManager(
|
||||||
|
/* listener= */ this,
|
||||||
|
localPlayerView,
|
||||||
|
castControlView,
|
||||||
|
/* context= */ this,
|
||||||
|
castContext);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Illegal receiver app id: " + applicationId);
|
||||||
|
}
|
||||||
mediaQueueList.setAdapter(mediaQueueListAdapter);
|
mediaQueueList.setAdapter(mediaQueueListAdapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,46 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.castdemo;
|
package com.google.android.exoplayer2.castdemo;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.DefaultRenderersFactory;
|
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
|
||||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
|
||||||
import com.google.android.exoplayer2.Player;
|
|
||||||
import com.google.android.exoplayer2.Player.DiscontinuityReason;
|
|
||||||
import com.google.android.exoplayer2.Player.EventListener;
|
|
||||||
import com.google.android.exoplayer2.Player.TimelineChangeReason;
|
|
||||||
import com.google.android.exoplayer2.RenderersFactory;
|
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
|
||||||
import com.google.android.exoplayer2.Timeline;
|
|
||||||
import com.google.android.exoplayer2.ext.cast.DefaultCastSessionManager;
|
|
||||||
import com.google.android.exoplayer2.ext.cast.ExoCastPlayer;
|
|
||||||
import com.google.android.exoplayer2.ext.cast.MediaItem;
|
import com.google.android.exoplayer2.ext.cast.MediaItem;
|
||||||
import com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener;
|
|
||||||
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
|
||||||
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.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.exoplayer2.util.Log;
|
|
||||||
import com.google.android.gms.cast.framework.CastContext;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/** Manages players and an internal media queue for the Cast demo app. */
|
/** Manages the players in the Cast demo app. */
|
||||||
/* package */ class PlayerManager implements EventListener, SessionAvailabilityListener {
|
/* package */ interface PlayerManager {
|
||||||
|
|
||||||
/** Listener for events. */
|
/** Listener for events. */
|
||||||
public interface Listener {
|
interface Listener {
|
||||||
|
|
||||||
/** 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);
|
||||||
@ -66,361 +35,33 @@ import java.util.ArrayList;
|
|||||||
void onPlayerError();
|
void onPlayerError();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String TAG = "PlayerManager";
|
/** Redirects the given {@code keyEvent} to the active player. */
|
||||||
private static final String USER_AGENT = "ExoCastDemoPlayer";
|
boolean dispatchKeyEvent(KeyEvent keyEvent);
|
||||||
private static final DefaultHttpDataSourceFactory DATA_SOURCE_FACTORY =
|
|
||||||
new DefaultHttpDataSourceFactory(USER_AGENT);
|
|
||||||
|
|
||||||
private final PlayerView localPlayerView;
|
/** Appends the given {@link MediaItem} to the media queue. */
|
||||||
private final PlayerControlView castControlView;
|
void addItem(MediaItem mediaItem);
|
||||||
private final SimpleExoPlayer exoPlayer;
|
|
||||||
private final ExoCastPlayer exoCastPlayer;
|
|
||||||
private final ArrayList<MediaItem> mediaQueue;
|
|
||||||
private final Listener listener;
|
|
||||||
private final ConcatenatingMediaSource concatenatingMediaSource;
|
|
||||||
|
|
||||||
private int currentItemIndex;
|
/** Returns the number of items in the media queue. */
|
||||||
private Player currentPlayer;
|
int getMediaQueueSize();
|
||||||
|
|
||||||
|
/** Selects the item at the given position for playback. */
|
||||||
|
void selectQueueItem(int position);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new manager for {@link SimpleExoPlayer} and {@link ExoCastPlayer}.
|
* Returns the position of the item currently being played, or {@link C#INDEX_UNSET} if no item is
|
||||||
*
|
* being played.
|
||||||
* @param listener A {@link Listener}.
|
|
||||||
* @param localPlayerView The {@link PlayerView} for local playback.
|
|
||||||
* @param castControlView The {@link PlayerControlView} to control remote playback.
|
|
||||||
* @param context A {@link Context}.
|
|
||||||
* @param castContext The {@link CastContext}.
|
|
||||||
*/
|
*/
|
||||||
public PlayerManager(
|
int getCurrentItemIndex();
|
||||||
Listener listener,
|
|
||||||
PlayerView localPlayerView,
|
|
||||||
PlayerControlView castControlView,
|
|
||||||
Context context,
|
|
||||||
CastContext castContext) {
|
|
||||||
this.listener = listener;
|
|
||||||
this.localPlayerView = localPlayerView;
|
|
||||||
this.castControlView = castControlView;
|
|
||||||
mediaQueue = new ArrayList<>();
|
|
||||||
currentItemIndex = C.INDEX_UNSET;
|
|
||||||
concatenatingMediaSource = new ConcatenatingMediaSource();
|
|
||||||
|
|
||||||
DefaultTrackSelector trackSelector = new DefaultTrackSelector();
|
/** Returns the {@link MediaItem} at the given {@code position}. */
|
||||||
RenderersFactory renderersFactory = new DefaultRenderersFactory(context);
|
MediaItem getItem(int position);
|
||||||
exoPlayer = ExoPlayerFactory.newSimpleInstance(context, renderersFactory, trackSelector);
|
|
||||||
exoPlayer.addListener(this);
|
|
||||||
localPlayerView.setPlayer(exoPlayer);
|
|
||||||
|
|
||||||
exoCastPlayer =
|
/** Moves the item at position {@code from} to position {@code to}. */
|
||||||
new ExoCastPlayer(
|
boolean moveItem(MediaItem item, int to);
|
||||||
sessionManagerListener ->
|
|
||||||
new DefaultCastSessionManager(castContext, sessionManagerListener));
|
|
||||||
exoCastPlayer.addListener(this);
|
|
||||||
exoCastPlayer.setSessionAvailabilityListener(this);
|
|
||||||
castControlView.setPlayer(exoCastPlayer);
|
|
||||||
|
|
||||||
setCurrentPlayer(exoCastPlayer.isCastSessionAvailable() ? exoCastPlayer : exoPlayer);
|
/** Removes the item at position {@code index}. */
|
||||||
}
|
boolean removeItem(MediaItem item);
|
||||||
|
|
||||||
// Queue manipulation methods.
|
/** Releases any acquired resources. */
|
||||||
|
void release();
|
||||||
/**
|
|
||||||
* Plays a specified queue item in the current player.
|
|
||||||
*
|
|
||||||
* @param itemIndex The index of the item to play.
|
|
||||||
*/
|
|
||||||
public void selectQueueItem(int itemIndex) {
|
|
||||||
setCurrentItem(itemIndex, C.TIME_UNSET, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the index of the currently played item. */
|
|
||||||
public int getCurrentItemIndex() {
|
|
||||||
return currentItemIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends {@code item} to the media queue.
|
|
||||||
*
|
|
||||||
* @param item The {@link MediaItem} to append.
|
|
||||||
*/
|
|
||||||
public void addItem(MediaItem item) {
|
|
||||||
mediaQueue.add(item);
|
|
||||||
concatenatingMediaSource.addMediaSource(buildMediaSource(item));
|
|
||||||
if (currentPlayer == exoCastPlayer) {
|
|
||||||
exoCastPlayer.addItemsToQueue(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the size of the media queue. */
|
|
||||||
public int getMediaQueueSize() {
|
|
||||||
return mediaQueue.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the item at the given index in the media queue.
|
|
||||||
*
|
|
||||||
* @param position The index of the item.
|
|
||||||
* @return The item at the given index in the media queue.
|
|
||||||
*/
|
|
||||||
public MediaItem getItem(int position) {
|
|
||||||
return mediaQueue.get(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the item at the given index from the media queue.
|
|
||||||
*
|
|
||||||
* @param item The item to remove.
|
|
||||||
* @return Whether the removal was successful.
|
|
||||||
*/
|
|
||||||
public boolean removeItem(MediaItem item) {
|
|
||||||
int itemIndex = mediaQueue.indexOf(item);
|
|
||||||
if (itemIndex == -1) {
|
|
||||||
// This may happen if another sender app removes items while this sender app is in "swiping
|
|
||||||
// an item" state.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
concatenatingMediaSource.removeMediaSource(itemIndex);
|
|
||||||
mediaQueue.remove(itemIndex);
|
|
||||||
if (currentPlayer == exoCastPlayer) {
|
|
||||||
exoCastPlayer.removeItemFromQueue(itemIndex);
|
|
||||||
}
|
|
||||||
if (itemIndex == currentItemIndex && itemIndex == mediaQueue.size()) {
|
|
||||||
maybeSetCurrentItemAndNotify(C.INDEX_UNSET);
|
|
||||||
} else if (itemIndex < currentItemIndex) {
|
|
||||||
maybeSetCurrentItemAndNotify(currentItemIndex - 1);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves an item within the queue.
|
|
||||||
*
|
|
||||||
* @param item The item to move. This method does nothing if {@code item} is not contained in the
|
|
||||||
* queue.
|
|
||||||
* @param toIndex The target index of the item in the queue. If {@code toIndex} exceeds the last
|
|
||||||
* position in the queue, {@code toIndex} is clamped to match the largest possible value.
|
|
||||||
* @return True if {@code item} was contained in the queue, and {@code toIndex} was a valid
|
|
||||||
* position. False otherwise.
|
|
||||||
*/
|
|
||||||
public boolean moveItem(MediaItem item, int toIndex) {
|
|
||||||
int indexOfItem = mediaQueue.indexOf(item);
|
|
||||||
if (indexOfItem == -1) {
|
|
||||||
// This may happen if another sender app removes items while this sender app is in "dragging
|
|
||||||
// an item" state.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int clampedToIndex = Math.min(toIndex, mediaQueue.size() - 1);
|
|
||||||
mediaQueue.add(clampedToIndex, mediaQueue.remove(indexOfItem));
|
|
||||||
concatenatingMediaSource.moveMediaSource(indexOfItem, clampedToIndex);
|
|
||||||
if (currentPlayer == exoCastPlayer) {
|
|
||||||
exoCastPlayer.moveItemInQueue(indexOfItem, clampedToIndex);
|
|
||||||
}
|
|
||||||
// Index update.
|
|
||||||
maybeSetCurrentItemAndNotify(currentPlayer.getCurrentWindowIndex());
|
|
||||||
return clampedToIndex == toIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Miscellaneous methods.
|
|
||||||
|
|
||||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
|
||||||
if (currentPlayer == exoPlayer) {
|
|
||||||
return localPlayerView.dispatchKeyEvent(event);
|
|
||||||
} else /* currentPlayer == exoCastPlayer */ {
|
|
||||||
return castControlView.dispatchKeyEvent(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Releases the manager and the players that it holds. */
|
|
||||||
public void release() {
|
|
||||||
currentItemIndex = C.INDEX_UNSET;
|
|
||||||
mediaQueue.clear();
|
|
||||||
concatenatingMediaSource.clear();
|
|
||||||
exoCastPlayer.setSessionAvailabilityListener(null);
|
|
||||||
exoCastPlayer.release();
|
|
||||||
localPlayerView.setPlayer(null);
|
|
||||||
exoPlayer.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Player.EventListener implementation.
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
|
||||||
updateCurrentItemIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPositionDiscontinuity(@DiscontinuityReason int reason) {
|
|
||||||
updateCurrentItemIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTimelineChanged(
|
|
||||||
Timeline timeline, @Nullable Object manifest, @TimelineChangeReason int reason) {
|
|
||||||
if (currentPlayer == exoCastPlayer && reason != Player.TIMELINE_CHANGE_REASON_RESET) {
|
|
||||||
maybeUpdateLocalQueueWithRemoteQueueAndNotify();
|
|
||||||
}
|
|
||||||
updateCurrentItemIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPlayerError(ExoPlaybackException error) {
|
|
||||||
Log.e(TAG, "The player encountered an error.", error);
|
|
||||||
listener.onPlayerError();
|
|
||||||
}
|
|
||||||
|
|
||||||
// CastPlayer.SessionAvailabilityListener implementation.
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCastSessionAvailable() {
|
|
||||||
setCurrentPlayer(exoCastPlayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCastSessionUnavailable() {
|
|
||||||
setCurrentPlayer(exoPlayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal methods.
|
|
||||||
|
|
||||||
private void maybeUpdateLocalQueueWithRemoteQueueAndNotify() {
|
|
||||||
Assertions.checkState(currentPlayer == exoCastPlayer);
|
|
||||||
boolean mediaQueuesMatch = mediaQueue.size() == exoCastPlayer.getQueueSize();
|
|
||||||
for (int i = 0; mediaQueuesMatch && i < mediaQueue.size(); i++) {
|
|
||||||
mediaQueuesMatch = mediaQueue.get(i).uuid.equals(exoCastPlayer.getQueueItem(i).uuid);
|
|
||||||
}
|
|
||||||
if (mediaQueuesMatch) {
|
|
||||||
// The media queues match. Do nothing.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mediaQueue.clear();
|
|
||||||
concatenatingMediaSource.clear();
|
|
||||||
for (int i = 0; i < exoCastPlayer.getQueueSize(); i++) {
|
|
||||||
MediaItem item = exoCastPlayer.getQueueItem(i);
|
|
||||||
mediaQueue.add(item);
|
|
||||||
concatenatingMediaSource.addMediaSource(buildMediaSource(item));
|
|
||||||
}
|
|
||||||
listener.onQueueContentsExternallyChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateCurrentItemIndex() {
|
|
||||||
int playbackState = currentPlayer.getPlaybackState();
|
|
||||||
maybeSetCurrentItemAndNotify(
|
|
||||||
playbackState != Player.STATE_IDLE && playbackState != Player.STATE_ENDED
|
|
||||||
? currentPlayer.getCurrentWindowIndex()
|
|
||||||
: C.INDEX_UNSET);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCurrentPlayer(Player currentPlayer) {
|
|
||||||
if (this.currentPlayer == currentPlayer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// View management.
|
|
||||||
if (currentPlayer == exoPlayer) {
|
|
||||||
localPlayerView.setVisibility(View.VISIBLE);
|
|
||||||
castControlView.hide();
|
|
||||||
} else /* currentPlayer == exoCastPlayer */ {
|
|
||||||
localPlayerView.setVisibility(View.GONE);
|
|
||||||
castControlView.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Player state management.
|
|
||||||
long playbackPositionMs = C.TIME_UNSET;
|
|
||||||
int windowIndex = C.INDEX_UNSET;
|
|
||||||
boolean playWhenReady = false;
|
|
||||||
if (this.currentPlayer != null) {
|
|
||||||
int playbackState = this.currentPlayer.getPlaybackState();
|
|
||||||
if (playbackState != Player.STATE_ENDED) {
|
|
||||||
playbackPositionMs = this.currentPlayer.getCurrentPosition();
|
|
||||||
playWhenReady = this.currentPlayer.getPlayWhenReady();
|
|
||||||
windowIndex = this.currentPlayer.getCurrentWindowIndex();
|
|
||||||
if (windowIndex != currentItemIndex) {
|
|
||||||
playbackPositionMs = C.TIME_UNSET;
|
|
||||||
windowIndex = currentItemIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.currentPlayer.stop(true);
|
|
||||||
} else {
|
|
||||||
// This is the initial setup. No need to save any state.
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentPlayer = currentPlayer;
|
|
||||||
|
|
||||||
// Media queue management.
|
|
||||||
boolean shouldSeekInNewCurrentPlayer;
|
|
||||||
if (currentPlayer == exoPlayer) {
|
|
||||||
exoPlayer.prepare(concatenatingMediaSource);
|
|
||||||
shouldSeekInNewCurrentPlayer = true;
|
|
||||||
} else /* currentPlayer == exoCastPlayer */ {
|
|
||||||
if (exoCastPlayer.getPlaybackState() == Player.STATE_IDLE) {
|
|
||||||
exoCastPlayer.prepare();
|
|
||||||
}
|
|
||||||
if (mediaQueue.isEmpty()) {
|
|
||||||
// Casting started with no local queue. We take the receiver app's queue as our own.
|
|
||||||
maybeUpdateLocalQueueWithRemoteQueueAndNotify();
|
|
||||||
shouldSeekInNewCurrentPlayer = false;
|
|
||||||
} else {
|
|
||||||
// Casting started when the sender app had no queue. We just load our items into the
|
|
||||||
// receiver app's queue. If the receiver had no items in its queue, we also seek to wherever
|
|
||||||
// the sender app was playing.
|
|
||||||
int currentExoCastPlayerState = exoCastPlayer.getPlaybackState();
|
|
||||||
shouldSeekInNewCurrentPlayer =
|
|
||||||
currentExoCastPlayerState == Player.STATE_IDLE
|
|
||||||
|| currentExoCastPlayerState == Player.STATE_ENDED;
|
|
||||||
exoCastPlayer.addItemsToQueue(mediaQueue.toArray(new MediaItem[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Playback transition.
|
|
||||||
if (shouldSeekInNewCurrentPlayer && windowIndex != C.INDEX_UNSET) {
|
|
||||||
setCurrentItem(windowIndex, playbackPositionMs, playWhenReady);
|
|
||||||
} else if (getMediaQueueSize() > 0) {
|
|
||||||
maybeSetCurrentItemAndNotify(currentPlayer.getCurrentWindowIndex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts playback of the item at the given position.
|
|
||||||
*
|
|
||||||
* @param itemIndex The index of the item to play.
|
|
||||||
* @param positionMs The position at which playback should start.
|
|
||||||
* @param playWhenReady Whether the player should proceed when ready to do so.
|
|
||||||
*/
|
|
||||||
private void setCurrentItem(int itemIndex, long positionMs, boolean playWhenReady) {
|
|
||||||
maybeSetCurrentItemAndNotify(itemIndex);
|
|
||||||
currentPlayer.seekTo(itemIndex, positionMs);
|
|
||||||
if (currentPlayer.getPlaybackState() == Player.STATE_IDLE) {
|
|
||||||
if (currentPlayer == exoCastPlayer) {
|
|
||||||
exoCastPlayer.prepare();
|
|
||||||
} else {
|
|
||||||
exoPlayer.prepare(concatenatingMediaSource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentPlayer.setPlayWhenReady(playWhenReady);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void maybeSetCurrentItemAndNotify(int currentItemIndex) {
|
|
||||||
if (this.currentItemIndex != currentItemIndex) {
|
|
||||||
int oldIndex = this.currentItemIndex;
|
|
||||||
this.currentItemIndex = currentItemIndex;
|
|
||||||
listener.onQueuePositionChanged(oldIndex, currentItemIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MediaSource buildMediaSource(MediaItem item) {
|
|
||||||
Uri uri = item.media.uri;
|
|
||||||
switch (item.mimeType) {
|
|
||||||
case DemoUtil.MIME_TYPE_SS:
|
|
||||||
return new SsMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri);
|
|
||||||
case DemoUtil.MIME_TYPE_DASH:
|
|
||||||
return new DashMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri);
|
|
||||||
case DemoUtil.MIME_TYPE_HLS:
|
|
||||||
return new HlsMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri);
|
|
||||||
case DemoUtil.MIME_TYPE_VIDEO_MP4:
|
|
||||||
return new ProgressiveMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri);
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Unsupported type: " + item.mimeType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user