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