Home | History | Annotate | Download | only in media
      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.supportv7.media;
     18 
     19 import android.content.Context;
     20 import android.net.Uri;
     21 import android.os.Build;
     22 import android.os.Handler;
     23 import android.util.Log;
     24 import android.media.MediaPlayer;
     25 import android.view.Surface;
     26 import android.view.SurfaceHolder;
     27 import java.io.IOException;
     28 
     29 /**
     30  * MediaPlayerWrapper handles playback of a single media item, and is used for
     31  * both local and remote playback.
     32  */
     33 public class MediaPlayerWrapper implements
     34         MediaPlayer.OnPreparedListener,
     35         MediaPlayer.OnCompletionListener,
     36         MediaPlayer.OnErrorListener,
     37         MediaPlayer.OnSeekCompleteListener,
     38         MediaSessionManager.Callback {
     39     private static final String TAG = "MediaPlayerWrapper";
     40     private static final boolean DEBUG = false;
     41 
     42     private static final int STATE_IDLE = 0;
     43     private static final int STATE_PLAY_PENDING = 1;
     44     private static final int STATE_READY = 2;
     45     private static final int STATE_PLAYING = 3;
     46     private static final int STATE_PAUSED = 4;
     47 
     48     private final Context mContext;
     49     private final Handler mHandler = new Handler();
     50     private MediaPlayer mMediaPlayer;
     51     private int mState = STATE_IDLE;
     52     private Callback mCallback;
     53     private Surface mSurface;
     54     private SurfaceHolder mSurfaceHolder;
     55     private int mSeekToPos;
     56 
     57     public MediaPlayerWrapper(Context context) {
     58         mContext = context;
     59         reset();
     60     }
     61 
     62     public void release() {
     63         onStop();
     64         mMediaPlayer.release();
     65     }
     66 
     67     public void setCallback(Callback cb) {
     68         mCallback = cb;
     69     }
     70 
     71     // MediaSessionManager.Callback
     72     @Override
     73     public void onNewItem(Uri uri) {
     74         reset();
     75         try {
     76             mMediaPlayer.setDataSource(mContext, uri);
     77             mMediaPlayer.prepareAsync();
     78         } catch (IllegalStateException e) {
     79             Log.e(TAG, "MediaPlayer throws IllegalStateException, uri=" + uri);
     80         } catch (IOException e) {
     81             Log.e(TAG, "MediaPlayer throws IOException, uri=" + uri);
     82         } catch (IllegalArgumentException e) {
     83             Log.e(TAG, "MediaPlayer throws IllegalArgumentException, uri=" + uri);
     84         } catch (SecurityException e) {
     85             Log.e(TAG, "MediaPlayer throws SecurityException, uri=" + uri);
     86         }
     87     }
     88 
     89     @Override
     90     public void onStart() {
     91         if (mState == STATE_READY || mState == STATE_PAUSED) {
     92             mMediaPlayer.start();
     93             mState = STATE_PLAYING;
     94         } else if (mState == STATE_IDLE){
     95             mState = STATE_PLAY_PENDING;
     96         }
     97     }
     98 
     99     @Override
    100     public void onStop() {
    101         if (mState == STATE_PLAYING || mState == STATE_PAUSED) {
    102             mMediaPlayer.stop();
    103             mState = STATE_IDLE;
    104         }
    105     }
    106 
    107     @Override
    108     public void onPause() {
    109         if (mState == STATE_PLAYING) {
    110             mMediaPlayer.pause();
    111             mState = STATE_PAUSED;
    112         }
    113     }
    114 
    115     @Override
    116     public void onSeek(long pos) {
    117         if (DEBUG) {
    118             Log.d(TAG, "onSeek: pos=" + pos);
    119         }
    120         if (mState == STATE_PLAYING || mState == STATE_PAUSED) {
    121             mMediaPlayer.seekTo((int)pos);
    122             mSeekToPos = (int)pos;
    123         } else if (mState == STATE_IDLE || mState == STATE_PLAY_PENDING) {
    124             // Seek before onPrepared() arrives,
    125             // need to performed delayed seek in onPrepared()
    126             mSeekToPos = (int)pos;
    127         }
    128     }
    129 
    130     @Override
    131     public void onGetStatus(MediaQueueItem item) {
    132         if (mState == STATE_PLAYING || mState == STATE_PAUSED) {
    133             // use mSeekToPos if we're currently seeking (mSeekToPos is reset
    134             // when seeking is completed)
    135             item.setContentDuration(mMediaPlayer.getDuration());
    136             item.setContentPosition(mSeekToPos > 0 ?
    137                     mSeekToPos : mMediaPlayer.getCurrentPosition());
    138         }
    139     }
    140 
    141     public void setSurface(Surface surface) {
    142         mSurface = surface;
    143         mSurfaceHolder = null;
    144         updateSurface();
    145     }
    146 
    147     public void setSurface(SurfaceHolder surfaceHolder) {
    148         mSurface = null;
    149         mSurfaceHolder = surfaceHolder;
    150         updateSurface();
    151     }
    152 
    153     //MediaPlayer Listeners
    154     @Override
    155     public void onPrepared(MediaPlayer mp) {
    156         if (DEBUG) {
    157             Log.d(TAG,"onPrepared");
    158         }
    159         mHandler.post(new Runnable() {
    160             @Override
    161             public void run() {
    162                 if (mState == STATE_IDLE) {
    163                     mState = STATE_READY;
    164                     updateVideoRect();
    165                 } else if (mState == STATE_PLAY_PENDING) {
    166                     mState = STATE_PLAYING;
    167                     updateVideoRect();
    168                     if (mSeekToPos > 0) {
    169                         Log.d(TAG, "Seeking to initial pos " + mSeekToPos);
    170                         mMediaPlayer.seekTo((int)mSeekToPos);
    171                     }
    172                     mMediaPlayer.start();
    173                 }
    174                 if (mCallback != null) {
    175                     mCallback.onStatusChanged();
    176                 }
    177             }
    178         });
    179     }
    180 
    181     @Override
    182     public void onCompletion(MediaPlayer mp) {
    183         if (DEBUG) {
    184             Log.d(TAG,"onCompletion");
    185         }
    186         mHandler.post(new Runnable() {
    187             @Override
    188             public void run() {
    189                 if (mCallback != null) {
    190                     mCallback.onCompletion();
    191                 }
    192             }
    193         });
    194     }
    195 
    196     @Override
    197     public boolean onError(MediaPlayer mp, int what, int extra) {
    198         if (DEBUG) {
    199             Log.d(TAG,"onError");
    200         }
    201         mHandler.post(new Runnable() {
    202             @Override
    203             public void run() {
    204                 if (mCallback != null) {
    205                     mCallback.onError();
    206                 }
    207             }
    208         });
    209         // return true so that onCompletion is not called
    210         return true;
    211     }
    212 
    213     @Override
    214     public void onSeekComplete(MediaPlayer mp) {
    215         if (DEBUG) {
    216             Log.d(TAG, "onSeekComplete");
    217         }
    218         mHandler.post(new Runnable() {
    219             @Override
    220             public void run() {
    221                 mSeekToPos = 0;
    222                 if (mCallback != null) {
    223                     mCallback.onStatusChanged();
    224                 }
    225             }
    226         });
    227     }
    228 
    229     public void reset() {
    230         if (mMediaPlayer != null) {
    231             mMediaPlayer.stop();
    232             mMediaPlayer.release();
    233             mMediaPlayer = null;
    234         }
    235         mMediaPlayer = new MediaPlayer();
    236         mMediaPlayer.setOnPreparedListener(this);
    237         mMediaPlayer.setOnCompletionListener(this);
    238         mMediaPlayer.setOnErrorListener(this);
    239         mMediaPlayer.setOnSeekCompleteListener(this);
    240         updateSurface();
    241         mState = STATE_IDLE;
    242         mSeekToPos = 0;
    243     }
    244 
    245     private void updateVideoRect() {
    246         if (mState != STATE_IDLE && mState != STATE_PLAY_PENDING) {
    247             int videoWidth = mMediaPlayer.getVideoWidth();
    248             int videoHeight = mMediaPlayer.getVideoHeight();
    249             if (videoWidth > 0 && videoHeight > 0) {
    250                 if (mCallback != null) {
    251                     mCallback.onSizeChanged(videoWidth, videoHeight);
    252                 }
    253             } else {
    254                 Log.e(TAG, "video rect is 0x0!");
    255             }
    256         }
    257     }
    258 
    259     private void updateSurface() {
    260         if (mSurface != null) {
    261             // The setSurface API does not exist until V14+.
    262             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
    263                 ICSMediaPlayer.setSurface(mMediaPlayer, mSurface);
    264             } else {
    265                 throw new UnsupportedOperationException("MediaPlayer does not support "
    266                         + "setSurface() on this version of the platform.");
    267             }
    268         } else if (mSurfaceHolder != null) {
    269             mMediaPlayer.setDisplay(mSurfaceHolder);
    270         } else {
    271             mMediaPlayer.setDisplay(null);
    272         }
    273     }
    274 
    275     public static abstract class Callback {
    276         public void onError() {}
    277         public void onCompletion() {}
    278         public void onStatusChanged() {}
    279         public void onSizeChanged(int width, int height) {}
    280     }
    281 
    282     private static final class ICSMediaPlayer {
    283         public static final void setSurface(MediaPlayer player, Surface surface) {
    284             player.setSurface(surface);
    285         }
    286     }
    287 }
    288