summaryrefslogtreecommitdiffstats
path: root/chrome/test/android/cast_emulator/src/org/chromium/chrome/browser/media/remote/RemoteSessionManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/test/android/cast_emulator/src/org/chromium/chrome/browser/media/remote/RemoteSessionManager.java')
-rw-r--r--chrome/test/android/cast_emulator/src/org/chromium/chrome/browser/media/remote/RemoteSessionManager.java360
1 files changed, 360 insertions, 0 deletions
diff --git a/chrome/test/android/cast_emulator/src/org/chromium/chrome/browser/media/remote/RemoteSessionManager.java b/chrome/test/android/cast_emulator/src/org/chromium/chrome/browser/media/remote/RemoteSessionManager.java
new file mode 100644
index 0000000..a0835fb
--- /dev/null
+++ b/chrome/test/android/cast_emulator/src/org/chromium/chrome/browser/media/remote/RemoteSessionManager.java
@@ -0,0 +1,360 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.media.remote;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.net.Uri;
+import android.support.v7.media.MediaItemStatus;
+import android.support.v7.media.MediaSessionStatus;
+import android.util.Log;
+
+/**
+ * RemoteSessionManager emulates the session management of the playback of media items on
+ * Chromecast. This can be seen as emulating the cast receiver application. It only handles one item
+ * at a time, and does not support queuing.
+ *
+ * Actual playback of a single media item is abstracted into the DummyPlayer class, and is handled
+ * outside this class.
+ */
+public class RemoteSessionManager implements DummyPlayer.Callback {
+ private static final String TAG = "RemoteSessionManager";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * Connect a local session manager to the unique remote session manager, creating it if needed.
+ * @param localSessionManager the local session manager being connected
+ * @param player the player to use
+ * @return the remote session manager
+ */
+ public static RemoteSessionManager connect(LocalSessionManager localSessionManager,
+ Context context) {
+ if (sInstance == null) {
+ sInstance = new RemoteSessionManager("remote", context);
+ }
+ sInstance.mLocalSessionManager = localSessionManager;
+ return sInstance;
+ }
+
+ private String mName;
+ private int mSessionId;
+ private int mItemId;
+ private boolean mPaused;
+ private boolean mSessionValid;
+ private DummyPlayer mPlayer;
+ private MediaItem mCurrentItem;
+ private static RemoteSessionManager sInstance;
+
+ private LocalSessionManager mLocalSessionManager;
+ private Context mContext;
+
+ private RemoteSessionManager(String name, Context context) {
+ mName = name;
+ mContext = context;
+ }
+
+ /**
+ * Add a video we want to play
+ *
+ * @param uri the URI of the video
+ * @param mime the mime type
+ * @param receiver the pending intent to use to send state changes
+ * @return the new media item
+ */
+ public MediaItem add(Uri uri, String mime, PendingIntent receiver) {
+ if (DEBUG) log("add: uri=" + uri + ", receiver=" + receiver);
+ // create new session if needed
+ startSession(false);
+ checkPlayerAndSession();
+
+ // create new item with initial status PLAYBACK_STATE_PENDING
+ mItemId++;
+ mCurrentItem = new MediaItem(Integer.toString(mSessionId), Integer.toString(mItemId), uri,
+ mime, receiver);
+ if (DEBUG) log("add: new item id = " + mCurrentItem);
+ return mCurrentItem;
+ }
+
+ /**
+ * Disconnect from the local session
+ */
+ public void disconnect() {
+ mLocalSessionManager = null;
+ }
+
+ /**
+ * Get the currently playing item
+ *
+ * @return the currently playing item, or null if none.
+ */
+ public MediaItem getCurrentItem() {
+ return mCurrentItem;
+ }
+
+ /**
+ * Get the session id of the current session
+ *
+ * @return the session id, or null if none.
+ */
+ public String getSessionId() {
+ return mSessionValid ? Integer.toString(mSessionId) : null;
+ }
+
+ /**
+ * Get the status of a session
+ *
+ * @param sid the session id of session being asked about
+ * @return the status
+ */
+ public MediaSessionStatus getSessionStatus(String sid) {
+ Log.d(TAG, "Getting session status for session " + sid);
+ int sessionState =
+ (sid != null && sid.equals(Integer.toString(mSessionId)))
+ ? MediaSessionStatus.SESSION_STATE_ACTIVE
+ : MediaSessionStatus.SESSION_STATE_INVALIDATED;
+
+ Log.d(TAG, "Session state is " + sessionState);
+
+ return new MediaSessionStatus.Builder(sessionState).setQueuePaused(mPaused).build();
+ }
+
+ /**
+ * Get a printable string describing the status of the session
+ * @return the string
+ */
+ public String getSessionStatusString() {
+ if (mCurrentItem != null) {
+ return "Current media item: " + mCurrentItem.toString();
+ } else {
+ return "No current media item";
+ }
+ }
+
+ /**
+ * Get the status of a media item
+ *
+ * @param iid - the id of the item
+ * @return the MediaItem, from which its status can be read.
+ */
+ public MediaItem getStatus(String iid) {
+ checkPlayerAndSession();
+ checkItemCurrent(iid);
+
+ mPlayer.getStatus(mCurrentItem, false);
+ return mCurrentItem;
+ }
+
+ /**
+ * @return whether the current video is paused
+ */
+ public boolean isPaused() {
+ return mSessionValid && mPaused;
+ }
+
+ @Override
+ public void onCompletion() {
+ finishItem(false);
+ }
+
+ // Player.Callback
+ @Override
+ public void onError() {
+ finishItem(true);
+ }
+
+ @Override
+ public void onSeekComplete() {
+ // Playlist has changed, update the cached playlist
+ updateStatus();
+ }
+
+ @Override
+ public void onPrepared() {
+ // Item is ready to play, update the status.
+ updateStatus();
+ // Send the new status to the local session manager.
+ onItemChanged();
+ }
+
+ /**
+ * Pause the current video
+ */
+ public void pause() {
+ if (DEBUG) log("pause");
+ if (!mSessionValid) {
+ return;
+ }
+ checkPlayer();
+ mPaused = true;
+ updatePlaybackState();
+ }
+
+ /**
+ * Resume the current video
+ */
+ public void resume() {
+ if (DEBUG) log("resume");
+ if (!mSessionValid) {
+ return;
+ }
+ checkPlayer();
+ mPaused = false;
+ updatePlaybackState();
+ }
+
+ /**
+ * Seek to a position in a video
+ *
+ * @param iid the id of the video
+ * @param pos the position in ms
+ * @return the Media item.
+ */
+ public MediaItem seek(String iid, long pos) {
+ if (DEBUG) log("seek: iid=" + iid + ", pos=" + pos);
+ checkPlayerAndSession();
+ checkItemCurrent(iid);
+
+ if (pos != mCurrentItem.getPosition()) {
+ mCurrentItem.setPosition(pos);
+ if (mCurrentItem.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING
+ || mCurrentItem.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
+ mPlayer.seek(mCurrentItem);
+ }
+ }
+ return mCurrentItem;
+ }
+
+ /**
+ * Start a new emulated Chromecast session if needed.
+ *
+ * @param relaunch relaunch the remote session (the emulation of the Chromecast app) even if it
+ * is already running.
+ * @return The new session id
+ */
+ public String startSession(boolean relaunch) {
+ if (!mSessionValid || relaunch) {
+ if (mPlayer != null) mPlayer.setCallback(null);
+ finishItem(false);
+ if (mPlayer != null) mPlayer.release();
+ mSessionId++;
+ mItemId = 0;
+ mPaused = false;
+ mSessionValid = true;
+ mPlayer = DummyPlayer.create(mContext, null);
+ mPlayer.setCallback(this);
+ mCurrentItem = null;
+ Log.d(TAG, "Starting session " + mSessionId);
+ }
+ return Integer.toString(mSessionId);
+ }
+
+ /**
+ * Stop the current video
+ */
+ public void stop() {
+ if (DEBUG) log("stop");
+ if (!mSessionValid) {
+ return;
+ }
+ checkPlayer();
+ mPlayer.stop();
+ mCurrentItem = null;
+ mPaused = false;
+ updateStatus();
+ }
+
+ // Updates the playlist.
+ public void updateStatus() {
+ if (DEBUG) log("updateStatus");
+ checkPlayer();
+
+ if (mCurrentItem != null) {
+ mPlayer.getStatus(mCurrentItem, true /* update */);
+ }
+ }
+
+ private void checkItemCurrent(String iid) {
+ if (mCurrentItem == null || !mCurrentItem.getItemId().equals(iid)) {
+ throw new IllegalArgumentException("Item is not current!");
+ }
+ }
+
+ private void checkPlayer() {
+ if (mPlayer == null) {
+ throw new IllegalStateException("Player not set!");
+ }
+ }
+
+ private void checkPlayerAndSession() {
+ checkPlayer();
+ checkSession();
+ }
+
+ private void checkSession() {
+ if (!mSessionValid) {
+ throw new IllegalStateException("Session not set!");
+ }
+ }
+
+ private void finishItem(boolean error) {
+ if (mCurrentItem != null) {
+ removeItem(mCurrentItem.getItemId(), error ? MediaItemStatus.PLAYBACK_STATE_ERROR
+ : MediaItemStatus.PLAYBACK_STATE_FINISHED);
+ updateStatus();
+ }
+ }
+
+ private void log(String message) {
+ Log.d(TAG, mName + ": " + message);
+ }
+
+ private MediaItem removeItem(String iid, int state) {
+ checkPlayerAndSession();
+
+ checkItemCurrent(iid);
+
+ if (mCurrentItem.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING
+ || mCurrentItem.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
+ mPlayer.stop();
+ }
+ mCurrentItem.setState(state);
+ onItemChanged();
+ updatePlaybackState();
+
+ MediaItem item = mCurrentItem;
+
+ mCurrentItem = null;
+
+ return item;
+ }
+
+ private void updatePlaybackState() {
+ if (mCurrentItem != null) {
+ if (mCurrentItem.getState() == MediaItemStatus.PLAYBACK_STATE_PENDING) {
+ mCurrentItem.setState(mPaused ? MediaItemStatus.PLAYBACK_STATE_PAUSED
+ : MediaItemStatus.PLAYBACK_STATE_PLAYING);
+ mPlayer.play(mCurrentItem);
+ } else if (mPaused
+ && mCurrentItem.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING) {
+ mPlayer.pause();
+ mCurrentItem.setState(MediaItemStatus.PLAYBACK_STATE_PAUSED);
+ } else if (!mPaused
+ && mCurrentItem.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
+ mPlayer.resume();
+ mCurrentItem.setState(MediaItemStatus.PLAYBACK_STATE_PLAYING);
+ }
+ // notify client that item playback status has changed
+ onItemChanged();
+ }
+ updateStatus();
+ }
+
+ private void onItemChanged() {
+ if (mLocalSessionManager != null) {
+ mLocalSessionManager.onItemChanged(mCurrentItem);
+ }
+ }
+
+}