Skip to content

Most visited

Recently visited

navigation
MediaRouter / src / com.example.android.mediarouter / player /

RemotePlayer.java

1
/*
2
 * Copyright (C) 2013 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
 */
16
 
17
package com.example.android.mediarouter.player;
18
 
19
import android.content.Context;
20
import android.content.Intent;
21
import android.os.Bundle;
22
import android.support.v7.media.MediaItemStatus;
23
import android.support.v7.media.MediaRouter.ControlRequestCallback;
24
import android.support.v7.media.MediaRouter.RouteInfo;
25
import android.support.v7.media.MediaSessionStatus;
26
import android.support.v7.media.RemotePlaybackClient;
27
import android.support.v7.media.RemotePlaybackClient.ItemActionCallback;
28
import android.support.v7.media.RemotePlaybackClient.SessionActionCallback;
29
import android.support.v7.media.RemotePlaybackClient.StatusCallback;
30
import android.util.Log;
31
 
32
import com.example.android.mediarouter.player.Player;
33
import com.example.android.mediarouter.player.PlaylistItem;
34
import com.example.android.mediarouter.provider.SampleMediaRouteProvider;
35
 
36
import java.util.ArrayList;
37
import java.util.List;
38
 
39
/**
40
 * Handles playback of media items using a remote route.
41
 *
42
 * This class is used as a backend by PlaybackManager to feed media items to
43
 * the remote route. When the remote route doesn't support queuing, media items
44
 * are fed one-at-a-time; otherwise media items are enqueued to the remote side.
45
 */
46
public class RemotePlayer extends Player {
47
    private static final String TAG = "RemotePlayer";
48
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
49
    private Context mContext;
50
    private RouteInfo mRoute;
51
    private boolean mEnqueuePending;
52
    private String mStatsInfo = "";
53
    private List<PlaylistItem> mTempQueue = new ArrayList<PlaylistItem>();
54
 
55
    private RemotePlaybackClient mClient;
56
    private StatusCallback mStatusCallback = new StatusCallback() {
57
        @Override
58
        public void onItemStatusChanged(Bundle data,
59
                String sessionId, MediaSessionStatus sessionStatus,
60
                String itemId, MediaItemStatus itemStatus) {
61
            logStatus("onItemStatusChanged", sessionId, sessionStatus, itemId, itemStatus);
62
            if (mCallback != null) {
63
                if (itemStatus.getPlaybackState() ==
64
                        MediaItemStatus.PLAYBACK_STATE_FINISHED) {
65
                    mCallback.onCompletion();
66
                } else if (itemStatus.getPlaybackState() ==
67
                        MediaItemStatus.PLAYBACK_STATE_ERROR) {
68
                    mCallback.onError();
69
                }
70
            }
71
        }
72
 
73
        @Override
74
        public void onSessionStatusChanged(Bundle data,
75
                String sessionId, MediaSessionStatus sessionStatus) {
76
            logStatus("onSessionStatusChanged", sessionId, sessionStatus, null, null);
77
            if (mCallback != null) {
78
                mCallback.onPlaylistChanged();
79
            }
80
        }
81
 
82
        @Override
83
        public void onSessionChanged(String sessionId) {
84
            if (DEBUG) {
85
                Log.d(TAG, "onSessionChanged: sessionId=" + sessionId);
86
            }
87
        }
88
    };
89
 
90
    public RemotePlayer(Context context) {
91
        mContext = context;
92
    }
93
 
94
    @Override
95
    public boolean isRemotePlayback() {
96
        return true;
97
    }
98
 
99
    @Override
100
    public boolean isQueuingSupported() {
101
        return mClient.isQueuingSupported();
102
    }
103
 
104
    @Override
105
    public void connect(RouteInfo route) {
106
        mRoute = route;
107
        mClient = new RemotePlaybackClient(mContext, route);
108
        mClient.setStatusCallback(mStatusCallback);
109
 
110
        if (DEBUG) {
111
            Log.d(TAG, "connected to: " + route
112
                    + ", isRemotePlaybackSupported: " + mClient.isRemotePlaybackSupported()
113
                    + ", isQueuingSupported: "+ mClient.isQueuingSupported());
114
        }
115
    }
116
 
117
    @Override
118
    public void release() {
119
        mClient.release();
120
 
121
        if (DEBUG) {
122
            Log.d(TAG, "released.");
123
        }
124
    }
125
 
126
    // basic playback operations that are always supported
127
    @Override
128
    public void play(final PlaylistItem item) {
129
        if (DEBUG) {
130
            Log.d(TAG, "play: item=" + item);
131
        }
132
        mClient.play(item.getUri(), "video/mp4", null, 0, null, new ItemActionCallback() {
133
            @Override
134
            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
135
                    String itemId, MediaItemStatus itemStatus) {
136
                logStatus("play: succeeded", sessionId, sessionStatus, itemId, itemStatus);
137
                item.setRemoteItemId(itemId);
138
                if (item.getPosition() > 0) {
139
                    seekInternal(item);
140
                }
141
                if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
142
                    pause();
143
                }
144
                if (mCallback != null) {
145
                    mCallback.onPlaylistChanged();
146
                }
147
            }
148
 
149
            @Override
150
            public void onError(String error, int code, Bundle data) {
151
                logError("play: failed", error, code);
152
            }
153
        });
154
    }
155
 
156
    @Override
157
    public void seek(final PlaylistItem item) {
158
        seekInternal(item);
159
    }
160
 
161
    @Override
162
    public void getStatus(final PlaylistItem item, final boolean update) {
163
        if (!mClient.hasSession() || item.getRemoteItemId() == null) {
164
            // if session is not valid or item id not assigend yet.
165
            // just return, it's not fatal
166
            return;
167
        }
168
 
169
        if (DEBUG) {
170
            Log.d(TAG, "getStatus: item=" + item + ", update=" + update);
171
        }
172
        mClient.getStatus(item.getRemoteItemId(), null, new ItemActionCallback() {
173
            @Override
174
            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
175
                    String itemId, MediaItemStatus itemStatus) {
176
                logStatus("getStatus: succeeded", sessionId, sessionStatus, itemId, itemStatus);
177
                int state = itemStatus.getPlaybackState();
178
                if (state == MediaItemStatus.PLAYBACK_STATE_PLAYING
179
                        || state == MediaItemStatus.PLAYBACK_STATE_PAUSED
180
                        || state == MediaItemStatus.PLAYBACK_STATE_PENDING) {
181
                    item.setState(state);
182
                    item.setPosition(itemStatus.getContentPosition());
183
                    item.setDuration(itemStatus.getContentDuration());
184
                    item.setTimestamp(itemStatus.getTimestamp());
185
                }
186
                if (update && mCallback != null) {
187
                    mCallback.onPlaylistReady();
188
                }
189
            }
190
 
191
            @Override
192
            public void onError(String error, int code, Bundle data) {
193
                logError("getStatus: failed", error, code);
194
                if (update && mCallback != null) {
195
                    mCallback.onPlaylistReady();
196
                }
197
            }
198
        });
199
    }
200
 
201
    @Override
202
    public void pause() {
203
        if (!mClient.hasSession()) {
204
            // ignore if no session
205
            return;
206
        }
207
        if (DEBUG) {
208
            Log.d(TAG, "pause");
209
        }
210
        mClient.pause(null, new SessionActionCallback() {
211
            @Override
212
            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
213
                logStatus("pause: succeeded", sessionId, sessionStatus, null, null);
214
                if (mCallback != null) {
215
                    mCallback.onPlaylistChanged();
216
                }
217
            }
218
 
219
            @Override
220
            public void onError(String error, int code, Bundle data) {
221
                logError("pause: failed", error, code);
222
            }
223
        });
224
    }
225
 
226
    @Override
227
    public void resume() {
228
        if (!mClient.hasSession()) {
229
            // ignore if no session
230
            return;
231
        }
232
        if (DEBUG) {
233
            Log.d(TAG, "resume");
234
        }
235
        mClient.resume(null, new SessionActionCallback() {
236
            @Override
237
            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
238
                logStatus("resume: succeeded", sessionId, sessionStatus, null, null);
239
                if (mCallback != null) {
240
                    mCallback.onPlaylistChanged();
241
                }
242
            }
243
 
244
            @Override
245
            public void onError(String error, int code, Bundle data) {
246
                logError("resume: failed", error, code);
247
            }
248
        });
249
    }
250
 
251
    @Override
252
    public void stop() {
253
        if (!mClient.hasSession()) {
254
            // ignore if no session
255
            return;
256
        }
257
        if (DEBUG) {
258
            Log.d(TAG, "stop");
259
        }
260
        mClient.stop(null, new SessionActionCallback() {
261
            @Override
262
            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
263
                logStatus("stop: succeeded", sessionId, sessionStatus, null, null);
264
                if (mClient.isSessionManagementSupported()) {
265
                    endSession();
266
                }
267
                if (mCallback != null) {
268
                    mCallback.onPlaylistChanged();
269
                }
270
            }
271
 
272
            @Override
273
            public void onError(String error, int code, Bundle data) {
274
                logError("stop: failed", error, code);
275
            }
276
        });
277
    }
278
 
279
    // enqueue & remove are only supported if isQueuingSupported() returns true
280
    @Override
281
    public void enqueue(final PlaylistItem item) {
282
        throwIfQueuingUnsupported();
283
 
284
        if (!mClient.hasSession() && !mEnqueuePending) {
285
            mEnqueuePending = true;
286
            if (mClient.isSessionManagementSupported()) {
287
                startSession(item);
288
            } else {
289
                enqueueInternal(item);
290
            }
291
        } else if (mEnqueuePending){
292
            mTempQueue.add(item);
293
        } else {
294
            enqueueInternal(item);
295
        }
296
    }
297
 
298
    @Override
299
    public PlaylistItem remove(String itemId) {
300
        throwIfNoSession();
301
        throwIfQueuingUnsupported();
302
 
303
        if (DEBUG) {
304
            Log.d(TAG, "remove: itemId=" + itemId);
305
        }
306
        mClient.remove(itemId, null, new ItemActionCallback() {
307
            @Override
308
            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
309
                    String itemId, MediaItemStatus itemStatus) {
310
                logStatus("remove: succeeded", sessionId, sessionStatus, itemId, itemStatus);
311
            }
312
 
313
            @Override
314
            public void onError(String error, int code, Bundle data) {
315
                logError("remove: failed", error, code);
316
            }
317
        });
318
 
319
        return null;
320
    }
321
 
322
    @Override
323
    public void updateStatistics() {
324
        // clear stats info first
325
        mStatsInfo = "";
326
 
327
        Intent intent = new Intent(SampleMediaRouteProvider.ACTION_GET_STATISTICS);
328
        intent.addCategory(SampleMediaRouteProvider.CATEGORY_SAMPLE_ROUTE);
329
 
330
        if (mRoute != null && mRoute.supportsControlRequest(intent)) {
331
            ControlRequestCallback callback = new ControlRequestCallback() {
332
                @Override
333