close
The Wayback Machine - https://web.archive.org/web/20171115151120/https://developer.android.com/samples/MediaRouter/src/com.example.android.mediarouter/player/LocalPlayer.html
Skip to content

Most visited

Recently visited

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

LocalPlayer.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.app.Activity;
20
import android.app.Presentation;
21
import android.content.Context;
22
import android.content.DialogInterface;
23
import android.media.MediaPlayer;
24
import android.os.Build;
25
import android.os.Bundle;
26
import android.os.Handler;
27
import android.os.SystemClock;
28
import android.support.v7.media.MediaItemStatus;
29
import android.support.v7.media.MediaRouter.RouteInfo;
30
import android.util.Log;
31
import android.view.Display;
32
import android.view.Gravity;
33
import android.view.Surface;
34
import android.view.SurfaceHolder;
35
import android.view.SurfaceView;
36
import android.view.View;
37
import android.view.ViewGroup;
38
import android.view.WindowManager;
39
import android.widget.FrameLayout;
40
 
41
import com.example.android.mediarouter.R;
42
 
43
import java.io.IOException;
44
 
45
/**
46
 * Handles playback of a single media item using MediaPlayer.
47
 */
48
public abstract class LocalPlayer extends Player implements
49
        MediaPlayer.OnPreparedListener,
50
        MediaPlayer.OnCompletionListener,
51
        MediaPlayer.OnErrorListener,
52
        MediaPlayer.OnSeekCompleteListener {
53
    private static final String TAG = "LocalPlayer";
54
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
55
 
56
    private static final int STATE_IDLE = 0;
57
    private static final int STATE_PLAY_PENDING = 1;
58
    private static final int STATE_READY = 2;
59
    private static final int STATE_PLAYING = 3;
60
    private static final int STATE_PAUSED = 4;
61
 
62
    private final Context mContext;
63
    private final Handler mHandler = new Handler();
64
    private MediaPlayer mMediaPlayer;
65
    private int mState = STATE_IDLE;
66
    private int mSeekToPos;
67
    private int mVideoWidth;
68
    private int mVideoHeight;
69
    private Surface mSurface;
70
    private SurfaceHolder mSurfaceHolder;
71
 
72
    public LocalPlayer(Context context) {
73
        mContext = context;
74
 
75
        // reset media player
76
        reset();
77
    }
78
 
79
    @Override
80
    public boolean isRemotePlayback() {
81
        return false;
82
    }
83
 
84
    @Override
85
    public boolean isQueuingSupported() {
86
        return false;
87
    }
88
 
89
    @Override
90
    public void connect(RouteInfo route) {
91
        if (DEBUG) {
92
            Log.d(TAG, "connecting to: " + route);
93
        }
94
    }
95
 
96
    @Override
97
    public void release() {
98
        if (DEBUG) {
99
            Log.d(TAG, "releasing");
100
        }
101
        // release media player
102
        if (mMediaPlayer != null) {
103
            mMediaPlayer.stop();
104
            mMediaPlayer.release();
105
            mMediaPlayer = null;
106
        }
107
    }
108
 
109
    // Player
110
    @Override
111
    public void play(final PlaylistItem item) {
112
        if (DEBUG) {
113
            Log.d(TAG, "play: item=" + item);
114
        }
115
        reset();
116
        mSeekToPos = (int)item.getPosition();
117
        try {
118
            mMediaPlayer.setDataSource(mContext, item.getUri());
119
            mMediaPlayer.prepareAsync();
120
        } catch (IllegalStateException e) {
121
            Log.e(TAG, "MediaPlayer throws IllegalStateException, uri=" + item.getUri());
122
        } catch (IOException e) {
123
            Log.e(TAG, "MediaPlayer throws IOException, uri=" + item.getUri());
124
        } catch (IllegalArgumentException e) {
125
            Log.e(TAG, "MediaPlayer throws IllegalArgumentException, uri=" + item.getUri());
126
        } catch (SecurityException e) {
127
            Log.e(TAG, "MediaPlayer throws SecurityException, uri=" + item.getUri());
128
        }
129
        if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING) {
130
            resume();
131
        } else {
132
            pause();
133
        }
134
    }
135
 
136
    @Override
137
    public void seek(final PlaylistItem item) {
138
        if (DEBUG) {
139
            Log.d(TAG, "seek: item=" + item);
140
        }
141
        int pos = (int)item.getPosition();
142
        if (mState == STATE_PLAYING || mState == STATE_PAUSED) {
143
            mMediaPlayer.seekTo(pos);
144
            mSeekToPos = pos;
145
        } else if (mState == STATE_IDLE || mState == STATE_PLAY_PENDING) {
146
            // Seek before onPrepared() arrives,
147
            // need to performed delayed seek in onPrepared()
148
            mSeekToPos = pos;
149
        }
150
    }
151
 
152
    @Override
153
    public void getStatus(final PlaylistItem item, final boolean update) {
154
        if (mState == STATE_PLAYING || mState == STATE_PAUSED) {
155
            // use mSeekToPos if we're currently seeking (mSeekToPos is reset
156
            // when seeking is completed)
157
            item.setDuration(mMediaPlayer.getDuration());
158
            item.setPosition(mSeekToPos > 0 ?
159
                    mSeekToPos : mMediaPlayer.getCurrentPosition());
160
            item.setTimestamp(SystemClock.elapsedRealtime());
161
        }
162
        if (update && mCallback != null) {
163
            mCallback.onPlaylistReady();
164
        }
165
    }
166
 
167
    @Override
168
    public void pause() {
169
        if (DEBUG) {
170
            Log.d(TAG, "pause");
171
        }
172
        if (mState == STATE_PLAYING) {
173
            mMediaPlayer.pause();
174
            mState = STATE_PAUSED;
175
        }
176
    }
177
 
178
    @Override
179
    public void resume() {
180
        if (DEBUG) {
181
            Log.d(TAG, "resume");
182
        }
183
        if (mState == STATE_READY || mState == STATE_PAUSED) {
184
            mMediaPlayer.start();
185
            mState = STATE_PLAYING;
186
        } else if (mState == STATE_IDLE){
187
            mState = STATE_PLAY_PENDING;
188
        }
189
    }
190
 
191
    @Override
192
    public void stop() {
193
        if (DEBUG) {
194
            Log.d(TAG, "stop");
195
        }
196
        if (mState == STATE_PLAYING || mState == STATE_PAUSED) {
197
            mMediaPlayer.stop();
198
            mState = STATE_IDLE;
199
        }
200
    }
201
 
202
    @Override
203
    public void enqueue(final PlaylistItem item) {
204
        throw new UnsupportedOperationException("LocalPlayer doesn't support enqueue!");
205
    }
206
 
207
    @Override
208
    public PlaylistItem remove(String iid) {
209
        throw new UnsupportedOperationException("LocalPlayer doesn't support remove!");
210
    }
211
 
212
    //MediaPlayer Listeners
213
    @Override
214
    public void onPrepared(MediaPlayer mp) {
215
        if (DEBUG) {
216
            Log.d(TAG, "onPrepared");
217
        }
218
        mHandler.post(new Runnable() {
219
            @Override
220
            public void run() {
221
                if (mState == STATE_IDLE) {
222
                    mState = STATE_READY;
223
                    updateVideoRect();
224
                } else if (mState == STATE_PLAY_PENDING) {
225
                    mState = STATE_PLAYING;
226
                    updateVideoRect();
227
                    if (mSeekToPos > 0) {
228
                        if (DEBUG) {
229
                            Log.d(TAG, "seek to initial pos: " + mSeekToPos);
230
                        }
231
                        mMediaPlayer.seekTo(mSeekToPos);
232
                    }
233
                    mMediaPlayer.start();
234
                }
235
                if (mCallback != null) {
236
                    mCallback.onPlaylistChanged();
237
                }
238
            }
239
        });
240
    }
241
 
242
    @Override
243
    public void onCompletion(MediaPlayer mp) {
244
        if (DEBUG) {
245
            Log.d(TAG, "onCompletion");
246
        }
247
        mHandler.post(new Runnable() {
248
            @Override
249
            public void run() {
250
                if (mCallback != null) {
251
                    mCallback.onCompletion();
252
                }
253
            }
254
        });
255
    }
256
 
257
    @Override
258
    public boolean onError(MediaPlayer mp, int what, int extra) {
259
        if (DEBUG) {
260
            Log.d(TAG, "onError");
261
        }
262
        mHandler.post(new Runnable() {
263
            @Override
264
            public void run() {
265
                if (mCallback != null) {
266
                    mCallback.onError();
267
                }
268
            }
269
        });
270
        // return true so that onCompletion is not called
271
        return true;
272
    }
273
 
274
    @Override
275
    public void onSeekComplete(MediaPlayer mp) {
276
        if (DEBUG) {
277
            Log.d(TAG, "onSeekComplete");
278
        }
279
        mHandler.post(new Runnable() {
280
            @Override
281
            public void run() {
282
                mSeekToPos = 0;
283
                if (mCallback != null) {
284
                    mCallback.onPlaylistChanged();
285
                }
286
            }
287
        });
288
    }
289
 
290
    protected Context getContext() { return mContext; }
291
    protected MediaPlayer getMediaPlayer() { return mMediaPlayer; }
292
    protected int getVideoWidth() { return mVideoWidth; }
293
    protected int getVideoHeight() { return mVideoHeight; }
294
    protected void setSurface(Surface surface) {
295
        mSurface = surface;
296
        mSurfaceHolder = null;
297
        updateSurface();
298
    }
299
 
300
    protected void setSurface(SurfaceHolder surfaceHolder) {
301
        mSurface = null;
302
        mSurfaceHolder = surfaceHolder;
303
        updateSurface();
304
    }
305
 
306
    protected void removeSurface(SurfaceHolder surfaceHolder) {
307
        if (surfaceHolder == mSurfaceHolder) {
308
            setSurface((SurfaceHolder)null);
309
        }
310
    }
311
 
312
    protected void updateSurface() {
313
        if (mMediaPlayer == null) {
314
            // just return if media player is already gone
315
            return;
316
        }
317
        if (mSurface != null) {
318
            // The setSurface API does not exist until V14+.
319
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
320
                ICSMediaPlayer.setSurface(mMediaPlayer, mSurface);
321
            } else {
322
                throw new UnsupportedOperationException("MediaPlayer does not support "
323
                        + "setSurface() on this version of the platform.");
324
            }
325
        } else if (mSurfaceHolder != null) {
326
            mMediaPlayer.setDisplay(mSurfaceHolder);
327
        } else {
328
            mMediaPlayer.setDisplay(null);
329
        }
330
    }
331
 
332
    protected abstract void updateSize();
333
 
334
    private void reset() {
335
        if (mMediaPlayer != null) {
336
            mMediaPlayer.stop();
337
            mMediaPlayer.release();
338
            mMediaPlayer = null;
339
        }
340
        mMediaPlayer = new MediaPlayer();
341
        mMediaPlayer.setOnPreparedListener(this);
342
        mMediaPlayer.setOnCompletionListener(this);
343
        mMediaPlayer.setOnErrorListener(this);
344
        mMediaPlayer.setOnSeekCompleteListener(this);
345
        updateSurface();
346
        mState = STATE_IDLE;
347
        mSeekToPos = 0;
348
    }
349
 
350
    private void updateVideoRect() {
351
        if (mState != STATE_IDLE && mState != STATE_PLAY_PENDING) {
352
            int width = mMediaPlayer.getVideoWidth();
353
            int height = mMediaPlayer.getVideoHeight();
354
            if (width > 0 && height > 0) {
355
                mVideoWidth = width;
356
                mVideoHeight = height;
357
                updateSize();
358
            } else {
359
                Log.e(TAG, "video rect is 0x0!");
360
                mVideoWidth = mVideoHeight = 0;
361
            }
362
        }
363
    }
364
 
365
    private static final class ICSMediaPlayer {
366
        public static final void setSurface(MediaPlayer player, Surface surface) {
367
            player.setSurface(surface);
368
        }
369
    }
370
 
371
    /**
372
     * Handles playback of a single media item using MediaPlayer in SurfaceView
373
     */
374
    public static class SurfaceViewPlayer extends LocalPlayer implements
375
            SurfaceHolder.Callback {
376
        private static final String TAG = "SurfaceViewPlayer";
377
        private RouteInfo mRoute;
378
        private final SurfaceView mSurfaceView;
379
        private final FrameLayout mLayout;
380
        private DemoPresentation mPresentation;
381
 
382
        public SurfaceViewPlayer(Context context) {
383
            super(context);
384
 
385
            mLayout = (FrameLayout)((Activity)context).findViewById(R.id.player);
386
            mSurfaceView = (SurfaceView)((Activity)context).findViewById(R.id.surface_view);
387
 
388
            // add surface holder callback
389
            SurfaceHolder holder = mSurfaceView.getHolder();
390
            holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
391
            holder.addCallback(this);
392
        }
393
 
394
        @Override
395
        public void connect(RouteInfo route) {
396
            super.connect(route);
397
            mRoute = route;
398
        }
399
 
400
        @Override
401
        public void release() {
402
            super.release();
403
 
404
            // dismiss presentation display
405
            if (mPresentation != null) {
406
                Log.i(TAG, "Dismissing presentation because the activity is no longer visible.");
407
                mPresentation.dismiss();
408
                mPresentation = null;
409
            }
410
 
411
            // remove surface holder callback
412
            SurfaceHolder holder = mSurfaceView.getHolder();
413
            holder.removeCallback(this);
414
 
415
            // hide the surface view when SurfaceViewPlayer is destroyed
416
            mSurfaceView.setVisibility(View.GONE);
417
            mLayout.setVisibility(View.GONE);
418
        }
419
 
420
        @Override
421
        public void updatePresentation() {
422
            // Get the current route and its presentation display.
423
            Display presentationDisplay = mRoute != null ? mRoute.getPresentationDisplay() : null;
424
 
425
            // Dismiss the current presentation if the display has changed.
426
            if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
427
                Log.i(TAG, "Dismissing presentation because the current route no longer "
428
                        + "has a presentation display.");
429
                mPresentation.dismiss();
430
                mPresentation = null;
431
            }
432
 
433
            // Show a new presentation if needed.
434
            if (mPresentation == null && presentationDisplay != null) {
435
                Log.i(TAG, "Showing presentation on display: " + presentationDisplay);
436
                mPresentation = new DemoPresentation(getContext(), presentationDisplay);
437
                mPresentation.setOnDismissListener(mOnDismissListener);
438
                try {
439
                    mPresentation.show();
440
                } catch (WindowManager.InvalidDisplayException ex) {
441
                    Log.w(TAG, "Couldn't show presentation!  Display was removed in "
442
                              + "the meantime.", ex);
443
                    mPresentation = null;
444
                }
445
            }
446
 
447
            updateContents();
448
        }
449
 
450
        // SurfaceHolder.Callback
451
        @Override
452
        public void surfaceChanged(SurfaceHolder holder, int format,
453
                int width, int height) {
454
            if (DEBUG) {
455
                Log.d(TAG, "surfaceChanged: " + width + "x" + height);
456
            }
457
            setSurface(holder);
458
        }
459
 
460
        @Override
461
        public void surfaceCreated(SurfaceHolder holder) {
462
            if (DEBUG) {
463
                Log.d(TAG, "surfaceCreated");
464
            }
465
            setSurface(holder);
466
            updateSize();
467
        }
468
 
469
        @Override
470
        public void surfaceDestroyed(SurfaceHolder holder) {
471
            if (DEBUG) {
472
                Log.d(TAG, "surfaceDestroyed");
473
            }
474
            removeSurface(holder);
475
        }
476
 
477
        @Override
478
        protected void updateSize() {
479
            int width = getVideoWidth();
480
            int height = getVideoHeight();
481
            if (width > 0 && height > 0) {
482
                if (mPresentation == null) {
483
                    int surfaceWidth = mLayout.getWidth();
484
                    int surfaceHeight = mLayout.getHeight();
485
 
486
                    // Calculate the new size of mSurfaceView, so that video is centered
487
                    // inside the framelayout with proper letterboxing/pillarboxing
488
                    ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();
489
                    if (surfaceWidth * height < surfaceHeight * width) {
490
                        // Black bars on top&bottom, mSurfaceView has full layout width,
491
                        // while height is derived from video's aspect ratio
492
                        lp.width = surfaceWidth;
493
                        lp.height = surfaceWidth * height / width;
494
                    } else {
495
                        // Black bars on left&right, mSurfaceView has full layout height,
496
                        // while width is derived from video's aspect ratio
497
                        lp.width = surfaceHeight * width / height;
498
                        lp.height = surfaceHeight;
499
                    }
500
                    Log.i(TAG, "video rect is " + lp.width + "x" + lp.height);
501
                    mSurfaceView.setLayoutParams(lp);
502
                } else {
503
                    mPresentation.updateSize(width, height);
504
                }
505
            }
506
        }
507
 
508
        private void updateContents() {
509
            // Show either the content in the main activity or the content in the presentation
510
            if (mPresentation != null) {
511
                mLayout.setVisibility(View.GONE);
512
                mSurfaceView.setVisibility(View.GONE);
513
            } else {
514
                mLayout.setVisibility(View.VISIBLE);
515
                mSurfaceView.setVisibility(View.VISIBLE);
516
            }
517
        }
518
 
519
        // Listens for when presentations are dismissed.
520
        private final DialogInterface.OnDismissListener mOnDismissListener =
521
                new DialogInterface.OnDismissListener() {
522
            @Override
523
            public void onDismiss(DialogInterface dialog) {
524
                if (dialog == mPresentation) {
525
                    Log.i(TAG, "Presentation dismissed.");
526
                    mPresentation = null;
527
                    updateContents();
528
                }
529
            }
530
        };
531
 
532
        // Presentation
533
        private final class DemoPresentation extends Presentation {
534
            private SurfaceView mPresentationSurfaceView;
535
 
536
            public DemoPresentation(Context context, Display display) {
537
                super(context, display);
538
            }
539
 
540
            @Override
541
            protected void onCreate(Bundle savedInstanceState) {
542
                // Be sure to call the super class.
543
                super.onCreate(savedInstanceState);
544
 
545
                // Inflate the layout.
546
                setContentView(R.layout.sample_media_router_presentation);
547
 
548
                // Set up the surface view.
549
                mPresentationSurfaceView = (SurfaceView)findViewById(R.id.surface_view);
550
                SurfaceHolder holder = mPresentationSurfaceView.getHolder();
551
                holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
552
                holder.addCallback(SurfaceViewPlayer.this);
553
                Log.i(TAG, "Presentation created");
554
            }
555
 
556
            public void updateSize(int width, int height) {
557
                int surfaceHeight = getWindow().getDecorView().getHeight();
558
                int surfaceWidth = getWindow().getDecorView().getWidth();
559
                ViewGroup.LayoutParams lp = mPresentationSurfaceView.getLayoutParams();
560
                if (surfaceWidth * height < surfaceHeight * width) {
561
                    lp.width = surfaceWidth;
562
                    lp.height = surfaceWidth * height / width;
563
                } else {
564
                    lp.width = surfaceHeight * width / height;
565
                    lp.height = surfaceHeight;
566
                }
567
                Log.i(TAG, "Presentation video rect is " + lp.width + "x" + lp.height);
568
                mPresentationSurfaceView.setLayoutParams(lp);
569
            }
570
        }
571
    }
572
 
573
    /**
574
     * Handles playback of a single media item using MediaPlayer in
575
     * OverlayDisplayWindow.
576
     */
577
    public static class OverlayPlayer extends LocalPlayer implements
578
            OverlayDisplayWindow.OverlayWindowListener {
579
        private static final String TAG = "OverlayPlayer";
580
        private final OverlayDisplayWindow mOverlay;
581
 
582
        public OverlayPlayer(Context context) {
583
            super(context);
584
 
585
            mOverlay = OverlayDisplayWindow.create(getContext(),
586
                    getContext().getResources().getString(
587
                            R.string.sample_media_route_provider_remote),
588
                    1024, 768, Gravity.CENTER);
589
 
590
            mOverlay.setOverlayWindowListener(this);
591
        }
592
 
593
        @Override
594
        public void connect(RouteInfo route) {
595
            super.connect(route);
596
            mOverlay.show();
597
        }
598
 
599
        @Override
600
        public void release() {
601
            super.release();
602
            mOverlay.dismiss();
603
        }
604
 
605
        @Override
606
        protected void updateSize() {
607
            int width = getVideoWidth();
608
            int height = getVideoHeight();
609
            if (width > 0 && height > 0) {
610
                mOverlay.updateAspectRatio(width, height);
611
            }
612
        }
613
 
614
        // OverlayDisplayWindow.OverlayWindowListener
615
        @Override
616
        public void onWindowCreated(Surface surface) {
617
            setSurface(surface);
618
        }
619
 
620
        @Override
621
        public void onWindowCreated(SurfaceHolder surfaceHolder) {
622
            setSurface(surfaceHolder);
623
        }
624
 
625
        @Override
626
        public void onWindowDestroyed() {
627
            setSurface((SurfaceHolder)null);
628
        }
629
    }
630
}
This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

BERJAYA

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)