HTML5VideoView.java revision 4dd9dc8204105a25e37dacbc392ea3c7212a8063
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_NOTPREPARED = 1; 35 static final int STATE_PREPARED = 2; 36 static final int STATE_PLAYING = 3; 37 static final int STATE_RELEASED = 4; 38 protected int mCurrentState; 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 // Every video will have one MediaPlayer. Given the fact we only have one 50 // SurfaceTexture, there is only one MediaPlayer in action. Every time we 51 // switch videos, a new instance of MediaPlayer will be created in reset(). 52 // Switching between inline and full screen will also create a new instance. 53 protected MediaPlayer mPlayer; 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 // The spec says the timer should fire every 250 ms or less. 64 private static final int TIMEUPDATE_PERIOD = 250; // ms 65 66 protected boolean mPauseDuringPreparing; 67 // common Video control FUNCTIONS: 68 public void start() { 69 if (mCurrentState == STATE_PREPARED) { 70 // When replaying the same video, there is no onPrepared call. 71 // Therefore, the timer should be set up here. 72 if (mTimer == null) 73 { 74 mTimer = new Timer(); 75 mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD, 76 TIMEUPDATE_PERIOD); 77 } 78 mPlayer.start(); 79 setPlayerBuffering(false); 80 } 81 } 82 83 public void pause() { 84 if (isPlaying()) { 85 mPlayer.pause(); 86 } else if (mCurrentState == STATE_NOTPREPARED) { 87 mPauseDuringPreparing = true; 88 } 89 // Delete the Timer to stop it since there is no stop call. 90 if (mTimer != null) { 91 mTimer.purge(); 92 mTimer.cancel(); 93 mTimer = null; 94 } 95 } 96 97 public int getDuration() { 98 if (mCurrentState == STATE_PREPARED) { 99 return mPlayer.getDuration(); 100 } else { 101 return -1; 102 } 103 } 104 105 public int getCurrentPosition() { 106 if (mCurrentState == STATE_PREPARED) { 107 return mPlayer.getCurrentPosition(); 108 } 109 return 0; 110 } 111 112 public void seekTo(int pos) { 113 if (mCurrentState == STATE_PREPARED) 114 mPlayer.seekTo(pos); 115 else 116 mSaveSeekTime = pos; 117 } 118 119 public boolean isPlaying() { 120 if (mCurrentState == STATE_PREPARED) { 121 return mPlayer.isPlaying(); 122 } else { 123 return false; 124 } 125 } 126 127 public void release() { 128 if (mCurrentState != STATE_RELEASED) { 129 mPlayer.release(); 130 } 131 mCurrentState = STATE_RELEASED; 132 } 133 134 public void stopPlayback() { 135 if (mCurrentState == STATE_PREPARED) { 136 mPlayer.stop(); 137 } 138 } 139 140 public boolean getPauseDuringPreparing() { 141 return mPauseDuringPreparing; 142 } 143 144 // Every time we start a new Video, we create a VideoView and a MediaPlayer 145 public void init(int videoLayerId, int position) { 146 mPlayer = new MediaPlayer(); 147 mCurrentState = STATE_INITIALIZED; 148 mProxy = null; 149 mVideoLayerId = videoLayerId; 150 mSaveSeekTime = position; 151 mTimer = null; 152 mPauseDuringPreparing = false; 153 } 154 155 protected HTML5VideoView() { 156 } 157 158 protected static Map<String, String> generateHeaders(String url, 159 HTML5VideoViewProxy proxy) { 160 boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled(); 161 String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate); 162 Map<String, String> headers = new HashMap<String, String>(); 163 if (cookieValue != null) { 164 headers.put(COOKIE, cookieValue); 165 } 166 if (isPrivate) { 167 headers.put(HIDE_URL_LOGS, "true"); 168 } 169 170 return headers; 171 } 172 173 public void setVideoURI(String uri, HTML5VideoViewProxy proxy) { 174 // When switching players, surface texture will be reused. 175 mUri = Uri.parse(uri); 176 mHeaders = generateHeaders(uri, proxy); 177 } 178 179 // Listeners setup FUNCTIONS: 180 public void setOnCompletionListener(HTML5VideoViewProxy proxy) { 181 mPlayer.setOnCompletionListener(proxy); 182 } 183 184 public void setOnErrorListener(HTML5VideoViewProxy proxy) { 185 mPlayer.setOnErrorListener(proxy); 186 } 187 188 public void setOnPreparedListener(HTML5VideoViewProxy proxy) { 189 mProxy = proxy; 190 mPlayer.setOnPreparedListener(this); 191 } 192 193 public void setOnInfoListener(HTML5VideoViewProxy proxy) { 194 mPlayer.setOnInfoListener(proxy); 195 } 196 197 public void prepareDataCommon(HTML5VideoViewProxy proxy) { 198 try { 199 mPlayer.setDataSource(proxy.getContext(), mUri, mHeaders); 200 mPlayer.prepareAsync(); 201 } catch (IllegalArgumentException e) { 202 e.printStackTrace(); 203 } catch (IllegalStateException e) { 204 e.printStackTrace(); 205 } catch (IOException e) { 206 e.printStackTrace(); 207 } 208 mCurrentState = STATE_NOTPREPARED; 209 } 210 211 public void reprepareData(HTML5VideoViewProxy proxy) { 212 mPlayer.reset(); 213 prepareDataCommon(proxy); 214 } 215 216 // Normally called immediately after setVideoURI. But for full screen, 217 // this should be after surface holder created 218 public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { 219 // SurfaceTexture will be created lazily here for inline mode 220 decideDisplayMode(); 221 222 setOnCompletionListener(proxy); 223 setOnPreparedListener(proxy); 224 setOnErrorListener(proxy); 225 setOnInfoListener(proxy); 226 227 prepareDataCommon(proxy); 228 } 229 230 231 // Common code 232 public int getVideoLayerId() { 233 return mVideoLayerId; 234 } 235 236 237 public int getCurrentState() { 238 if (isPlaying()) { 239 return STATE_PLAYING; 240 } else { 241 return mCurrentState; 242 } 243 } 244 245 private static final class TimeupdateTask extends TimerTask { 246 private HTML5VideoViewProxy mProxy; 247 248 public TimeupdateTask(HTML5VideoViewProxy proxy) { 249 mProxy = proxy; 250 } 251 252 @Override 253 public void run() { 254 mProxy.onTimeupdate(); 255 } 256 } 257 258 @Override 259 public void onPrepared(MediaPlayer mp) { 260 mCurrentState = STATE_PREPARED; 261 seekTo(mSaveSeekTime); 262 if (mProxy != null) { 263 mProxy.onPrepared(mp); 264 } 265 if (mPauseDuringPreparing) { 266 pauseAndDispatch(mProxy); 267 mPauseDuringPreparing = false; 268 } 269 } 270 271 // Pause the play and update the play/pause button 272 public void pauseAndDispatch(HTML5VideoViewProxy proxy) { 273 pause(); 274 if (proxy != null) { 275 proxy.dispatchOnPaused(); 276 } 277 } 278 279 // Below are functions that are different implementation on inline and full- 280 // screen mode. Some are specific to one type, but currently are called 281 // directly from the proxy. 282 public void enterFullScreenVideoState(int layerId, 283 HTML5VideoViewProxy proxy, WebViewClassic webView) { 284 } 285 286 public boolean isFullScreenMode() { 287 return false; 288 } 289 290 public void decideDisplayMode() { 291 } 292 293 public boolean getReadyToUseSurfTex() { 294 return false; 295 } 296 297 public SurfaceTexture getSurfaceTexture(int videoLayerId) { 298 return null; 299 } 300 301 public void deleteSurfaceTexture() { 302 } 303 304 public int getTextureName() { 305 return 0; 306 } 307 308 // This is true only when the player is buffering and paused 309 public boolean mPlayerBuffering = false; 310 311 public boolean getPlayerBuffering() { 312 return mPlayerBuffering; 313 } 314 315 public void setPlayerBuffering(boolean playerBuffering) { 316 mPlayerBuffering = playerBuffering; 317 switchProgressView(playerBuffering); 318 } 319 320 321 protected void switchProgressView(boolean playerBuffering) { 322 // Only used in HTML5VideoFullScreen 323 } 324 325 public boolean surfaceTextureDeleted() { 326 // Only meaningful for HTML5VideoInline 327 return false; 328 } 329 330 public boolean fullScreenExited() { 331 // Only meaningful for HTML5VideoFullScreen 332 return false; 333 } 334 335 private boolean m_startWhenPrepared = false; 336 337 public void setStartWhenPrepared(boolean willPlay) { 338 m_startWhenPrepared = willPlay; 339 } 340 341 public boolean getStartWhenPrepared() { 342 return m_startWhenPrepared; 343 } 344 345 public void showControllerInFullScreen() { 346 } 347 348} 349