diff options
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.java | 360 |
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); + } + } + +} |