blob: 141a209d9936a960b51eb2a9c39212e85215aea7 [file] [log] [blame]
RoboErik8ae0f342014-02-24 18:02:08 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
RoboErikbfa153b2014-02-13 14:22:42 -080016package com.android.onemedia;
17
18import android.content.Context;
19import android.content.Intent;
Daniel Sandler45f7ee82014-06-05 02:54:13 -040020import android.graphics.Bitmap;
21import android.media.MediaMetadata;
Jeff Brownff0215d2014-07-14 04:05:08 -070022import android.media.routing.MediaRouteSelector;
23import android.media.routing.MediaRouter;
24import android.media.routing.MediaRouter.ConnectionRequest;
25import android.media.routing.MediaRouter.DestinationInfo;
26import android.media.routing.MediaRouter.RouteInfo;
RoboErik42ea7ee2014-05-16 16:27:35 -070027import android.media.session.MediaSession;
RoboErik03fce072014-09-24 09:46:57 -070028import android.media.session.MediaSession.QueueItem;
RoboErik42ea7ee2014-05-16 16:27:35 -070029import android.media.session.MediaSessionManager;
RoboErik8ae0f342014-02-24 18:02:08 -080030import android.media.session.PlaybackState;
RoboErikbfa153b2014-02-13 14:22:42 -080031import android.os.Bundle;
Jeff Brownff0215d2014-07-14 04:05:08 -070032import android.support.media.protocols.MediaPlayerProtocol;
33import android.support.media.protocols.MediaPlayerProtocol.MediaStatus;
RoboErik8b4bffc2014-07-10 13:48:01 -070034import android.os.RemoteException;
RoboErikc785a782014-07-14 13:40:43 -070035import android.os.SystemClock;
RoboErikbfa153b2014-02-13 14:22:42 -080036import android.util.Log;
37import android.view.KeyEvent;
38
39import com.android.onemedia.playback.LocalRenderer;
RoboErik07c70772014-03-20 13:33:52 -070040import com.android.onemedia.playback.OneMRPRenderer;
RoboErikbfa153b2014-02-13 14:22:42 -080041import com.android.onemedia.playback.Renderer;
RoboErik07c70772014-03-20 13:33:52 -070042import com.android.onemedia.playback.RequestUtils;
43
44import java.util.ArrayList;
Jeff Brownff0215d2014-07-14 04:05:08 -070045import java.util.List;
RoboErikbfa153b2014-02-13 14:22:42 -080046
47public class PlayerSession {
RoboErik8ae0f342014-02-24 18:02:08 -080048 private static final String TAG = "PlayerSession";
RoboErikbfa153b2014-02-13 14:22:42 -080049
RoboErik42ea7ee2014-05-16 16:27:35 -070050 protected MediaSession mSession;
Jeff Brownff0215d2014-07-14 04:05:08 -070051 protected MediaRouter mRouter;
RoboErikbfa153b2014-02-13 14:22:42 -080052 protected Context mContext;
RoboErik07c70772014-03-20 13:33:52 -070053 protected Renderer mRenderer;
RoboErik42ea7ee2014-05-16 16:27:35 -070054 protected MediaSession.Callback mCallback;
RoboErik8ae0f342014-02-24 18:02:08 -080055 protected Renderer.Listener mRenderListener;
Daniel Sandler45f7ee82014-06-05 02:54:13 -040056 protected MediaMetadata.Builder mMetadataBuilder;
RoboErik03fce072014-09-24 09:46:57 -070057 protected ArrayList<MediaSession.QueueItem> mQueue;
58 protected boolean mUseQueue;
RoboErik8ae0f342014-02-24 18:02:08 -080059
60 protected PlaybackState mPlaybackState;
61 protected Listener mListener;
RoboErik07c70772014-03-20 13:33:52 -070062
63 private String mContent;
RoboErikbfa153b2014-02-13 14:22:42 -080064
65 public PlayerSession(Context context) {
66 mContext = context;
RoboErikbfa153b2014-02-13 14:22:42 -080067 mRenderer = new LocalRenderer(context, null);
RoboErik07c70772014-03-20 13:33:52 -070068 mCallback = new SessionCb();
RoboErikbfa153b2014-02-13 14:22:42 -080069 mRenderListener = new RenderListener();
RoboErikc785a782014-07-14 13:40:43 -070070 PlaybackState.Builder psBob = new PlaybackState.Builder();
71 psBob.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY);
72 mPlaybackState = psBob.build();
RoboErik03fce072014-09-24 09:46:57 -070073 mQueue = new ArrayList<MediaSession.QueueItem>();
RoboErikbfa153b2014-02-13 14:22:42 -080074
75 mRenderer.registerListener(mRenderListener);
Daniel Sandler45f7ee82014-06-05 02:54:13 -040076
77 initMetadata();
RoboErikbfa153b2014-02-13 14:22:42 -080078 }
79
80 public void createSession() {
Jeff Brownff0215d2014-07-14 04:05:08 -070081 releaseSession();
82
RoboErik42ea7ee2014-05-16 16:27:35 -070083 MediaSessionManager man = (MediaSessionManager) mContext
RoboErikbfa153b2014-02-13 14:22:42 -080084 .getSystemService(Context.MEDIA_SESSION_SERVICE);
85 Log.d(TAG, "Creating session for package " + mContext.getBasePackageName());
Jeff Brownff0215d2014-07-14 04:05:08 -070086
87 mRouter = new MediaRouter(mContext);
88 mRouter.addSelector(new MediaRouteSelector.Builder()
89 .addRequiredProtocol(MediaPlayerProtocol.class)
90 .build());
91 mRouter.addSelector(new MediaRouteSelector.Builder()
92 .setRequiredFeatures(MediaRouter.ROUTE_FEATURE_LIVE_AUDIO)
93 .setOptionalFeatures(MediaRouter.ROUTE_FEATURE_LIVE_VIDEO)
94 .build());
95 mRouter.setRoutingCallback(new RoutingCallback(), null);
96
RoboErik8b4bffc2014-07-10 13:48:01 -070097 mSession = new MediaSession(mContext, "OneMedia");
RoboErik477d1192014-08-06 13:43:22 -070098 mSession.setCallback(mCallback);
RoboErikc47fa842014-05-28 17:36:42 -070099 mSession.setPlaybackState(mPlaybackState);
RoboErik477d1192014-08-06 13:43:22 -0700100 mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS
101 | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
Jeff Brownff0215d2014-07-14 04:05:08 -0700102 mSession.setMediaRouter(mRouter);
RoboErika8f95142014-05-05 14:23:49 -0700103 mSession.setActive(true);
Daniel Sandler45f7ee82014-06-05 02:54:13 -0400104 updateMetadata();
RoboErikbfa153b2014-02-13 14:22:42 -0800105 }
106
107 public void onDestroy() {
Jeff Brownff0215d2014-07-14 04:05:08 -0700108 releaseSession();
RoboErikbfa153b2014-02-13 14:22:42 -0800109 if (mRenderer != null) {
110 mRenderer.unregisterListener(mRenderListener);
111 mRenderer.onDestroy();
112 }
113 }
114
Jeff Brownff0215d2014-07-14 04:05:08 -0700115 private void releaseSession() {
116 if (mSession != null) {
117 mSession.release();
118 mSession = null;
119 }
120 if (mRouter != null) {
121 mRouter.release();
122 mRouter = null;
123 }
124 }
125
RoboErik8ae0f342014-02-24 18:02:08 -0800126 public void setListener(Listener listener) {
127 mListener = listener;
128 }
129
Jeff Browndba34ba2014-06-24 20:46:03 -0700130 public MediaSession.Token getSessionToken() {
RoboErikbfa153b2014-02-13 14:22:42 -0800131 return mSession.getSessionToken();
132 }
133
134 public void setContent(Bundle request) {
135 mRenderer.setContent(request);
RoboErik07c70772014-03-20 13:33:52 -0700136 mContent = request.getString(RequestUtils.EXTRA_KEY_SOURCE);
RoboErikbfa153b2014-02-13 14:22:42 -0800137 }
138
139 public void setNextContent(Bundle request) {
140 mRenderer.setNextContent(request);
141 }
142
Daniel Sandler45f7ee82014-06-05 02:54:13 -0400143 public void setIcon(Bitmap icon) {
144 mMetadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, icon);
RoboErik03fce072014-09-24 09:46:57 -0700145 mQueue.clear();
146 mQueue.add(new QueueItem(mMetadataBuilder.build().getDescription(), 11));
Daniel Sandler45f7ee82014-06-05 02:54:13 -0400147 updateMetadata();
148 }
149
150 private void updateMetadata() {
151 // This is a mild abuse of metadata and shouldn't be duplicated in real
152 // code
153 if (mSession != null && mSession.isActive()) {
154 mSession.setMetadata(mMetadataBuilder.build());
RoboErik03fce072014-09-24 09:46:57 -0700155 // Just toggle the queue every time we update for testing
156 mSession.setQueue(mUseQueue ? mQueue : null);
157 mSession.setQueueTitle(mUseQueue ? "Queue title" : null);
158 mUseQueue = !mUseQueue;
Daniel Sandler45f7ee82014-06-05 02:54:13 -0400159 }
160 }
161
RoboErik07c70772014-03-20 13:33:52 -0700162 private void updateState(int newState) {
RoboErik79fa4632014-05-27 16:49:09 -0700163 float rate = newState == PlaybackState.STATE_PLAYING ? 1 : 0;
RoboErikf1372422014-04-23 14:38:17 -0700164 long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
RoboErikc785a782014-07-14 13:40:43 -0700165 PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
166 bob.setState(newState, position, rate, SystemClock.elapsedRealtime());
167 bob.setErrorMessage(null);
168 mPlaybackState = bob.build();
RoboErikc47fa842014-05-28 17:36:42 -0700169 mSession.setPlaybackState(mPlaybackState);
RoboErik07c70772014-03-20 13:33:52 -0700170 }
171
Daniel Sandler45f7ee82014-06-05 02:54:13 -0400172 private void initMetadata() {
173 mMetadataBuilder = new MediaMetadata.Builder();
174 mMetadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE,
175 "OneMedia display title");
176 mMetadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE,
177 "OneMedia display subtitle");
RoboErik03fce072014-09-24 09:46:57 -0700178
179 mQueue.add(new QueueItem(mMetadataBuilder.build().getDescription(), 11));
Daniel Sandler45f7ee82014-06-05 02:54:13 -0400180 }
181
RoboErik8ae0f342014-02-24 18:02:08 -0800182 public interface Listener {
183 public void onPlayStateChanged(PlaybackState state);
184 }
185
186 private class RenderListener implements Renderer.Listener {
RoboErikbfa153b2014-02-13 14:22:42 -0800187
188 @Override
189 public void onError(int type, int extra, Bundle extras, Throwable error) {
RoboErik8ae0f342014-02-24 18:02:08 -0800190 Log.d(TAG, "Sending onError with type " + type + " and extra " + extra);
RoboErikc785a782014-07-14 13:40:43 -0700191 PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
192 bob.setState(PlaybackState.STATE_ERROR, -1, 0, 0);
RoboErik8ae0f342014-02-24 18:02:08 -0800193 if (error != null) {
RoboErikc785a782014-07-14 13:40:43 -0700194 bob.setErrorMessage(error.getLocalizedMessage());
RoboErik8ae0f342014-02-24 18:02:08 -0800195 }
RoboErikc785a782014-07-14 13:40:43 -0700196 mPlaybackState = bob.build();
RoboErikc47fa842014-05-28 17:36:42 -0700197 mSession.setPlaybackState(mPlaybackState);
RoboErik8ae0f342014-02-24 18:02:08 -0800198 if (mListener != null) {
199 mListener.onPlayStateChanged(mPlaybackState);
200 }
RoboErikbfa153b2014-02-13 14:22:42 -0800201 }
202
203 @Override
204 public void onStateChanged(int newState) {
RoboErikf1372422014-04-23 14:38:17 -0700205 long position = -1;
206 if (mRenderer != null) {
207 position = mRenderer.getSeekPosition();
208 }
RoboErikc785a782014-07-14 13:40:43 -0700209 int pbState;
210 float rate = 0;
211 String errorMsg = null;
RoboErik8ae0f342014-02-24 18:02:08 -0800212 switch (newState) {
213 case Renderer.STATE_ENDED:
214 case Renderer.STATE_STOPPED:
RoboErikc785a782014-07-14 13:40:43 -0700215 pbState = PlaybackState.STATE_STOPPED;
RoboErik8ae0f342014-02-24 18:02:08 -0800216 break;
217 case Renderer.STATE_INIT:
218 case Renderer.STATE_PREPARING:
RoboErikc785a782014-07-14 13:40:43 -0700219 pbState = PlaybackState.STATE_BUFFERING;
RoboErik8ae0f342014-02-24 18:02:08 -0800220 break;
221 case Renderer.STATE_ERROR:
RoboErikc785a782014-07-14 13:40:43 -0700222 pbState = PlaybackState.STATE_ERROR;
RoboErik8ae0f342014-02-24 18:02:08 -0800223 break;
224 case Renderer.STATE_PAUSED:
RoboErikc785a782014-07-14 13:40:43 -0700225 pbState = PlaybackState.STATE_PAUSED;
RoboErik8ae0f342014-02-24 18:02:08 -0800226 break;
227 case Renderer.STATE_PLAYING:
RoboErikc785a782014-07-14 13:40:43 -0700228 pbState = PlaybackState.STATE_PLAYING;
229 rate = 1;
RoboErik8ae0f342014-02-24 18:02:08 -0800230 break;
231 default:
RoboErikc785a782014-07-14 13:40:43 -0700232 pbState = PlaybackState.STATE_ERROR;
233 errorMsg = "unknown state";
RoboErik8ae0f342014-02-24 18:02:08 -0800234 break;
235 }
RoboErikc785a782014-07-14 13:40:43 -0700236 PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
237 bob.setState(pbState, position, rate, SystemClock.elapsedRealtime());
238 bob.setErrorMessage(errorMsg);
239 mPlaybackState = bob.build();
RoboErikc47fa842014-05-28 17:36:42 -0700240 mSession.setPlaybackState(mPlaybackState);
RoboErik8ae0f342014-02-24 18:02:08 -0800241 if (mListener != null) {
242 mListener.onPlayStateChanged(mPlaybackState);
243 }
RoboErikbfa153b2014-02-13 14:22:42 -0800244 }
245
246 @Override
247 public void onBufferingUpdate(int percent) {
248 }
249
250 @Override
251 public void onFocusLost() {
RoboErik8ae0f342014-02-24 18:02:08 -0800252 Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
RoboErikf1372422014-04-23 14:38:17 -0700253 long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
RoboErikc785a782014-07-14 13:40:43 -0700254 PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
255 bob.setState(PlaybackState.STATE_PAUSED, position, 0, SystemClock.elapsedRealtime());
256 bob.setErrorMessage(null);
257 mPlaybackState = bob.build();
RoboErikc47fa842014-05-28 17:36:42 -0700258 mSession.setPlaybackState(mPlaybackState);
RoboErik8ae0f342014-02-24 18:02:08 -0800259 if (mListener != null) {
260 mListener.onPlayStateChanged(mPlaybackState);
261 }
RoboErikbfa153b2014-02-13 14:22:42 -0800262 }
263
264 @Override
265 public void onNextStarted() {
266 }
267
268 }
269
RoboErik42ea7ee2014-05-16 16:27:35 -0700270 private class SessionCb extends MediaSession.Callback {
RoboErikbfa153b2014-02-13 14:22:42 -0800271 @Override
RoboErik8ae0f342014-02-24 18:02:08 -0800272 public void onPlay() {
273 mRenderer.onPlay();
274 }
275
276 @Override
277 public void onPause() {
278 mRenderer.onPause();
279 }
280 }
281
Jeff Brownff0215d2014-07-14 04:05:08 -0700282 private class RoutingCallback extends MediaRouter.RoutingCallback {
RoboErik07c70772014-03-20 13:33:52 -0700283 @Override
Jeff Brownff0215d2014-07-14 04:05:08 -0700284 public void onConnectionStateChanged(int state) {
285 if (state == MediaRouter.CONNECTION_STATE_CONNECTING) {
286 if (mRenderer != null) {
287 mRenderer.onStop();
288 }
289 mRenderer = null;
290 updateState(PlaybackState.STATE_CONNECTING);
291 return;
292 }
293
294 MediaRouter.ConnectionInfo connection = mRouter.getConnection();
295 if (connection != null) {
296 MediaPlayerProtocol protocol =
297 connection.getProtocolObject(MediaPlayerProtocol.class);
298 if (protocol != null) {
299 Log.d(TAG, "Connected to route using media player protocol");
300
301 protocol.setCallback(new PlayerCallback(), null);
302 mRenderer = new OneMRPRenderer(protocol);
303 updateState(PlaybackState.STATE_NONE);
304 return;
305 }
306 }
307
308 // Use local route
309 mRenderer = new LocalRenderer(mContext, null);
310 mRenderer.registerListener(mRenderListener);
311 updateState(PlaybackState.STATE_NONE);
RoboErik07c70772014-03-20 13:33:52 -0700312 }
313 }
314
Jeff Brownff0215d2014-07-14 04:05:08 -0700315 private class PlayerCallback extends MediaPlayerProtocol.Callback {
316 @Override
317 public void onStatusUpdated(MediaStatus status, Bundle extras) {
318 if (status != null) {
319 Log.d(TAG, "Received status update: " + status.toBundle());
320 switch (status.getPlayerState()) {
321 case MediaStatus.PLAYER_STATE_BUFFERING:
322 updateState(PlaybackState.STATE_BUFFERING);
323 break;
324 case MediaStatus.PLAYER_STATE_IDLE:
325 updateState(PlaybackState.STATE_STOPPED);
326 break;
327 case MediaStatus.PLAYER_STATE_PAUSED:
328 updateState(PlaybackState.STATE_PAUSED);
329 break;
330 case MediaStatus.PLAYER_STATE_PLAYING:
331 updateState(PlaybackState.STATE_PLAYING);
332 break;
333 case MediaStatus.PLAYER_STATE_UNKNOWN:
334 updateState(PlaybackState.STATE_NONE);
335 break;
336 }
RoboErik477d1192014-08-06 13:43:22 -0700337 }
Jeff Brownff0215d2014-07-14 04:05:08 -0700338 }
339 }
RoboErikbfa153b2014-02-13 14:22:42 -0800340}