HTML5VideoView.java revision 156f97b6c8580b790600b09b679b9dab8b271c5d
1 2package android.webkit; 3 4import android.graphics.SurfaceTexture; 5import android.media.MediaPlayer; 6import android.net.Uri; 7import android.util.Log; 8import android.view.SurfaceView; 9import android.webkit.HTML5VideoViewProxy; 10import java.io.IOException; 11import java.util.HashMap; 12import java.util.Map; 13import java.util.Timer; 14import java.util.TimerTask; 15 16/** 17 * @hide This is only used by the browser 18 */ 19public class HTML5VideoView implements MediaPlayer.OnPreparedListener { 20 21 protected static final String LOGTAG = "HTML5VideoView"; 22 23 protected static final String COOKIE = "Cookie"; 24 protected static final String HIDE_URL_LOGS = "x-hide-urls-from-log"; 25 26 // For handling the seekTo before prepared, we need to know whether or not 27 // the video is prepared. Therefore, we differentiate the state between 28 // prepared and not prepared. 29 // When the video is not prepared, we will have to save the seekTo time, 30 // and use it when prepared to play. 31 // NOTE: these values are in sync with VideoLayerAndroid.h in webkit side. 32 // Please keep them in sync when changed. 33 static final int STATE_INITIALIZED = 0; 34 static final int STATE_PREPARING = 1; 35 static final int STATE_PREPARED = 2; 36 static final int STATE_PLAYING = 3; 37 static final int STATE_RESETTED = 4; 38 static final int STATE_RELEASED = 5; 39 40 protected HTML5VideoViewProxy mProxy; 41 42 // Save the seek time when not prepared. This can happen when switching 43 // video besides initial load. 44 protected int mSaveSeekTime; 45 46 // This is used to find the VideoLayer on the native side. 47 protected int mVideoLayerId; 48 49 // Given the fact we only have one SurfaceTexture, we cannot support multiple 50 // player at the same time. We may recreate a new one and abandon the old 51 // one at transition time. 52 protected static MediaPlayer mPlayer = null; 53 protected static int mCurrentState = -1; 54 55 // We need to save such info. 56 protected Uri mUri; 57 protected Map<String, String> mHeaders; 58 59 // The timer for timeupate events. 60 // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate 61 protected static Timer mTimer; 62 63 protected boolean mPauseDuringPreparing; 64 65 // The spec says the timer should fire every 250 ms or less. 66 private static final int TIMEUPDATE_PERIOD = 250; // ms 67 private boolean mSkipPrepare = false; 68 69 // common Video control FUNCTIONS: 70 public void start() { 71 if (mCurrentState == STATE_PREPARED) { 72 // When replaying the same video, there is no onPrepared call. 73 // Therefore, the timer should be set up here. 74 if (mTimer == null) 75 { 76 mTimer = new Timer(); 77 mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD, 78 TIMEUPDATE_PERIOD); 79 } 80 mPlayer.start(); 81 setPlayerBuffering(false); 82 } 83 } 84 85 public void pause() { 86 if (isPlaying()) { 87 mPlayer.pause(); 88 } else if (mCurrentState == STATE_PREPARING) { 89 mPauseDuringPreparing = true; 90 } 91 // Delete the Timer to stop it since there is no stop call. 92 if (mTimer != null) { 93 mTimer.purge(); 94 mTimer.cancel(); 95 mTimer = null; 96 } 97 } 98 99 public int getDuration() { 100 if (mCurrentState == STATE_PREPARED) { 101 return mPlayer.getDuration(); 102 } else { 103 return -1; 104 } 105 } 106 107 public int getCurrentPosition() { 108 if (mCurrentState == STATE_PREPARED) { 109 return mPlayer.getCurrentPosition(); 110 } 111 return 0; 112 } 113 114 public void seekTo(int pos) { 115 if (mCurrentState == STATE_PREPARED) 116 mPlayer.seekTo(pos); 117 else 118 mSaveSeekTime = pos; 119 } 120 121 public boolean isPlaying() { 122 if (mCurrentState == STATE_PREPARED) { 123 return mPlayer.isPlaying(); 124 } else { 125 return false; 126 } 127 } 128 129 public void reset() { 130 if (mCurrentState < STATE_RESETTED) { 131 mPlayer.reset(); 132 } 133 mCurrentState = STATE_RESETTED; 134 } 135 136 public void stopPlayback() { 137 if (mCurrentState == STATE_PREPARED) { 138 mPlayer.stop(); 139 } 140 } 141 142 public static void release() { 143 if (mPlayer != null && mCurrentState != STATE_RELEASED) { 144 mPlayer.release(); 145 mPlayer = null; 146 } 147 mCurrentState = STATE_RELEASED; 148 } 149 150 public boolean isReleased() { 151 return mCurrentState == STATE_RELEASED; 152 } 153 154 public boolean getPauseDuringPreparing() { 155 return mPauseDuringPreparing; 156 } 157 158 // Every time we start a new Video, we create a VideoView and a MediaPlayer 159 public void init(int videoLayerId, int position, boolean skipPrepare) { 160 if (mPlayer == null) { 161 mPlayer = new MediaPlayer(); 162 mCurrentState = STATE_INITIALIZED; 163 } 164 mSkipPrepare = skipPrepare; 165 // If we want to skip the prepare, then we keep the state. 166 if (!mSkipPrepare) { 167 mCurrentState = STATE_INITIALIZED; 168 } 169 mProxy = null; 170 mVideoLayerId = videoLayerId; 171 mSaveSeekTime = position; 172 mTimer = null; 173 mPauseDuringPreparing = false; 174 } 175 176 protected HTML5VideoView() { 177 } 178 179 protected static Map<String, String> generateHeaders(String url, 180 HTML5VideoViewProxy proxy) { 181 boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled(); 182 String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate); 183 Map<String, String> headers = new HashMap<String, String>(); 184 if (cookieValue != null) { 185 headers.put(COOKIE, cookieValue); 186 } 187 if (isPrivate) { 188 headers.put(HIDE_URL_LOGS, "true"); 189 } 190 191 return headers; 192 } 193 194 public void setVideoURI(String uri, HTML5VideoViewProxy proxy) { 195 // When switching players, surface texture will be reused. 196 mUri = Uri.parse(uri); 197 mHeaders = generateHeaders(uri, proxy); 198 } 199 200 // Listeners setup FUNCTIONS: 201 public void setOnCompletionListener(HTML5VideoViewProxy proxy) { 202 mPlayer.setOnCompletionListener(proxy); 203 } 204 205 public void setOnErrorListener(HTML5VideoViewProxy proxy) { 206 mPlayer.setOnErrorListener(proxy); 207 } 208 209 public void setOnPreparedListener(HTML5VideoViewProxy proxy) { 210 mProxy = proxy; 211 mPlayer.setOnPreparedListener(this); 212 } 213 214 public void setOnInfoListener(HTML5VideoViewProxy proxy) { 215 mPlayer.setOnInfoListener(proxy); 216 } 217 218 public void prepareDataCommon(HTML5VideoViewProxy proxy) { 219 if (!mSkipPrepare) { 220 try { 221 mPlayer.reset(); 222 mPlayer.setDataSource(proxy.getContext(), mUri, mHeaders); 223 mPlayer.prepareAsync(); 224 } catch (IllegalArgumentException e) { 225 e.printStackTrace(); 226 } catch (IllegalStateException e) { 227 e.printStackTrace(); 228 } catch (IOException e) { 229 e.printStackTrace(); 230 } 231 mCurrentState = STATE_PREPARING; 232 } else { 233 // If we skip prepare and the onPrepared happened in inline mode, we 234 // don't need to call prepare again, we just need to call onPrepared 235 // to refresh the state here. 236 if (mCurrentState >= STATE_PREPARED) { 237 onPrepared(mPlayer); 238 } 239 mSkipPrepare = false; 240 } 241 } 242 243 public void reprepareData(HTML5VideoViewProxy proxy) { 244 mPlayer.reset(); 245 prepareDataCommon(proxy); 246 } 247 248 // Normally called immediately after setVideoURI. But for full screen, 249 // this should be after surface holder created 250 public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { 251 // SurfaceTexture will be created lazily here for inline mode 252 decideDisplayMode(); 253 254 setOnCompletionListener(proxy); 255 setOnPreparedListener(proxy); 256 setOnErrorListener(proxy); 257 setOnInfoListener(proxy); 258 259 prepareDataCommon(proxy); 260 } 261 262 263 // Common code 264 public int getVideoLayerId() { 265 return mVideoLayerId; 266 } 267 268 269 public int getCurrentState() { 270 if (isPlaying()) { 271 return STATE_PLAYING; 272 } else { 273 return mCurrentState; 274 } 275 } 276 277 private static final class TimeupdateTask extends TimerTask { 278 private HTML5VideoViewProxy mProxy; 279 280 public TimeupdateTask(HTML5VideoViewProxy proxy) { 281 mProxy = proxy; 282 } 283 284 @Override 285 public void run() { 286 mProxy.onTimeupdate(); 287 } 288 } 289 290 @Override 291 public void onPrepared(MediaPlayer mp) { 292 mCurrentState = STATE_PREPARED; 293 seekTo(mSaveSeekTime); 294 if (mProxy != null) { 295 mProxy.onPrepared(mp); 296 } 297 if (mPauseDuringPreparing) { 298 pauseAndDispatch(mProxy); 299 mPauseDuringPreparing = false; 300 } 301 } 302 303 // Pause the play and update the play/pause button 304 public void pauseAndDispatch(HTML5VideoViewProxy proxy) { 305 pause(); 306 if (proxy != null) { 307 proxy.dispatchOnPaused(); 308 } 309 } 310 311 // Below are functions that are different implementation on inline and full- 312 // screen mode. Some are specific to one type, but currently are called 313 // directly from the proxy. 314 public void enterFullScreenVideoState(int layerId, 315 HTML5VideoViewProxy proxy, WebViewClassic webView) { 316 } 317 318 public boolean isFullScreenMode() { 319 return false; 320 } 321 322 public void decideDisplayMode() { 323 } 324 325 public boolean getReadyToUseSurfTex() { 326 return false; 327 } 328 329 public void deleteSurfaceTexture() { 330 } 331 332 public int getTextureName() { 333 return 0; 334 } 335 336 // This is true only when the player is buffering and paused 337 public boolean mPlayerBuffering = false; 338 339 public boolean getPlayerBuffering() { 340 return mPlayerBuffering; 341 } 342 343 public void setPlayerBuffering(boolean playerBuffering) { 344 mPlayerBuffering = playerBuffering; 345 switchProgressView(playerBuffering); 346 } 347 348 349 protected void switchProgressView(boolean playerBuffering) { 350 // Only used in HTML5VideoFullScreen 351 } 352 353 public boolean fullScreenExited() { 354 // Only meaningful for HTML5VideoFullScreen 355 return false; 356 } 357 358 private boolean mStartWhenPrepared = false; 359 360 public void setStartWhenPrepared(boolean willPlay) { 361 mStartWhenPrepared = willPlay; 362 } 363 364 public boolean getStartWhenPrepared() { 365 return mStartWhenPrepared; 366 } 367 368 public void showControllerInFullScreen() { 369 } 370 371} 372