HTML5VideoView.java revision 3fafd39d0776a15c3613795183043a2c28277691
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 protected int mCurrentState; 38 39 protected HTML5VideoViewProxy mProxy; 40 41 // Save the seek time when not prepared. This can happen when switching 42 // video besides initial load. 43 protected int mSaveSeekTime; 44 45 // This is used to find the VideoLayer on the native side. 46 protected int mVideoLayerId; 47 48 // Every video will have one MediaPlayer. Given the fact we only have one 49 // SurfaceTexture, there is only one MediaPlayer in action. Every time we 50 // switch videos, a new instance of MediaPlayer will be created in reset(). 51 // Switching between inline and full screen will also create a new instance. 52 protected MediaPlayer mPlayer; 53 54 // This will be set up every time we create the Video View object. 55 // Set to true only when switching into full screen while playing 56 protected boolean mAutostart; 57 58 // We need to save such info. 59 protected Uri mUri; 60 protected Map<String, String> mHeaders; 61 62 // The timer for timeupate events. 63 // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate 64 protected static Timer mTimer; 65 66 // The spec says the timer should fire every 250 ms or less. 67 private static final int TIMEUPDATE_PERIOD = 250; // ms 68 69 protected boolean mPauseDuringPreparing; 70 // common Video control FUNCTIONS: 71 public void start() { 72 if (mCurrentState == STATE_PREPARED) { 73 // When replaying the same video, there is no onPrepared call. 74 // Therefore, the timer should be set up here. 75 if (mTimer == null) 76 { 77 mTimer = new Timer(); 78 mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD, 79 TIMEUPDATE_PERIOD); 80 } 81 mPlayer.start(); 82 setPlayerBuffering(false); 83 } 84 } 85 86 public void pause() { 87 if (mCurrentState == STATE_PREPARED && mPlayer.isPlaying()) { 88 mPlayer.pause(); 89 } else if (mCurrentState == STATE_NOTPREPARED) { 90 mPauseDuringPreparing = true; 91 } 92 // Delete the Timer to stop it since there is no stop call. 93 if (mTimer != null) { 94 mTimer.purge(); 95 mTimer.cancel(); 96 mTimer = null; 97 } 98 } 99 100 public int getDuration() { 101 if (mCurrentState == STATE_PREPARED) { 102 return mPlayer.getDuration(); 103 } else { 104 return -1; 105 } 106 } 107 108 public int getCurrentPosition() { 109 if (mCurrentState == STATE_PREPARED) { 110 return mPlayer.getCurrentPosition(); 111 } 112 return 0; 113 } 114 115 public void seekTo(int pos) { 116 if (mCurrentState == STATE_PREPARED) 117 mPlayer.seekTo(pos); 118 else 119 mSaveSeekTime = pos; 120 } 121 122 public boolean isPlaying() { 123 return mPlayer.isPlaying(); 124 } 125 126 public void release() { 127 mPlayer.release(); 128 } 129 130 public void stopPlayback() { 131 if (mCurrentState == STATE_PREPARED) { 132 mPlayer.stop(); 133 } 134 } 135 136 public boolean getAutostart() { 137 return mAutostart; 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, boolean autoStart) { 146 mPlayer = new MediaPlayer(); 147 mCurrentState = STATE_INITIALIZED; 148 mProxy = null; 149 mVideoLayerId = videoLayerId; 150 mSaveSeekTime = position; 151 mAutostart = autoStart; 152 mTimer = null; 153 mPauseDuringPreparing = false; 154 } 155 156 protected HTML5VideoView() { 157 } 158 159 protected static Map<String, String> generateHeaders(String url, 160 HTML5VideoViewProxy proxy) { 161 boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled(); 162 String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate); 163 Map<String, String> headers = new HashMap<String, String>(); 164 if (cookieValue != null) { 165 headers.put(COOKIE, cookieValue); 166 } 167 if (isPrivate) { 168 headers.put(HIDE_URL_LOGS, "true"); 169 } 170 171 return headers; 172 } 173 174 public void setVideoURI(String uri, HTML5VideoViewProxy proxy) { 175 // When switching players, surface texture will be reused. 176 mUri = Uri.parse(uri); 177 mHeaders = generateHeaders(uri, proxy); 178 } 179 180 // Listeners setup FUNCTIONS: 181 public void setOnCompletionListener(HTML5VideoViewProxy proxy) { 182 mPlayer.setOnCompletionListener(proxy); 183 } 184 185 public void setOnErrorListener(HTML5VideoViewProxy proxy) { 186 mPlayer.setOnErrorListener(proxy); 187 } 188 189 public void setOnPreparedListener(HTML5VideoViewProxy proxy) { 190 mProxy = proxy; 191 mPlayer.setOnPreparedListener(this); 192 } 193 194 public void setOnInfoListener(HTML5VideoViewProxy proxy) { 195 mPlayer.setOnInfoListener(proxy); 196 } 197 198 // Normally called immediately after setVideoURI. But for full screen, 199 // this should be after surface holder created 200 public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { 201 // SurfaceTexture will be created lazily here for inline mode 202 decideDisplayMode(); 203 204 setOnCompletionListener(proxy); 205 setOnPreparedListener(proxy); 206 setOnErrorListener(proxy); 207 setOnInfoListener(proxy); 208 // When there is exception, we could just bail out silently. 209 // No Video will be played though. Write the stack for debug 210 try { 211 mPlayer.setDataSource(mProxy.getContext(), mUri, mHeaders); 212 mPlayer.prepareAsync(); 213 } catch (IllegalArgumentException e) { 214 e.printStackTrace(); 215 } catch (IllegalStateException e) { 216 e.printStackTrace(); 217 } catch (IOException e) { 218 e.printStackTrace(); 219 } 220 mCurrentState = STATE_NOTPREPARED; 221 } 222 223 224 // Common code 225 public int getVideoLayerId() { 226 return mVideoLayerId; 227 } 228 229 230 public int getCurrentState() { 231 if (mPlayer.isPlaying()) { 232 return STATE_PLAYING; 233 } else { 234 return mCurrentState; 235 } 236 } 237 238 private static final class TimeupdateTask extends TimerTask { 239 private HTML5VideoViewProxy mProxy; 240 241 public TimeupdateTask(HTML5VideoViewProxy proxy) { 242 mProxy = proxy; 243 } 244 245 @Override 246 public void run() { 247 mProxy.onTimeupdate(); 248 } 249 } 250 251 @Override 252 public void onPrepared(MediaPlayer mp) { 253 mCurrentState = STATE_PREPARED; 254 seekTo(mSaveSeekTime); 255 if (mProxy != null) { 256 mProxy.onPrepared(mp); 257 } 258 if (mPauseDuringPreparing) { 259 pauseAndDispatch(mProxy); 260 mPauseDuringPreparing = false; 261 } 262 } 263 264 // Pause the play and update the play/pause button 265 public void pauseAndDispatch(HTML5VideoViewProxy proxy) { 266 pause(); 267 if (proxy != null) { 268 proxy.dispatchOnPaused(); 269 } 270 } 271 272 // Below are functions that are different implementation on inline and full- 273 // screen mode. Some are specific to one type, but currently are called 274 // directly from the proxy. 275 public void enterFullScreenVideoState(int layerId, 276 HTML5VideoViewProxy proxy, WebView webView) { 277 } 278 279 public boolean isFullScreenMode() { 280 return false; 281 } 282 283 public void decideDisplayMode() { 284 } 285 286 public boolean getReadyToUseSurfTex() { 287 return false; 288 } 289 290 public SurfaceTexture getSurfaceTexture(int videoLayerId) { 291 return null; 292 } 293 294 public void deleteSurfaceTexture() { 295 } 296 297 public int getTextureName() { 298 return 0; 299 } 300 301 // This is true only when the player is buffering and paused 302 public boolean mPlayerBuffering = false; 303 304 public boolean getPlayerBuffering() { 305 return mPlayerBuffering; 306 } 307 308 public void setPlayerBuffering(boolean playerBuffering) { 309 mPlayerBuffering = playerBuffering; 310 switchProgressView(playerBuffering); 311 } 312 313 314 protected void switchProgressView(boolean playerBuffering) { 315 // Only used in HTML5VideoFullScreen 316 } 317 318 public boolean surfaceTextureDeleted() { 319 // Only meaningful for HTML5VideoInline 320 return false; 321 } 322 323} 324