HTML5VideoView.java revision 2fcf82aee30da977849adaaadf89d81c17afbac2
1 2package android.webkit; 3 4import android.graphics.SurfaceTexture; 5import android.media.MediaPlayer; 6import android.util.Log; 7import android.view.SurfaceView; 8import android.webkit.HTML5VideoViewProxy; 9import java.io.IOException; 10import java.util.HashMap; 11import java.util.Map; 12import java.util.Timer; 13import java.util.TimerTask; 14 15/** 16 * @hide This is only used by the browser 17 */ 18public class HTML5VideoView implements MediaPlayer.OnPreparedListener { 19 20 protected static final String LOGTAG = "HTML5VideoView"; 21 22 protected static final String COOKIE = "Cookie"; 23 protected static final String HIDE_URL_LOGS = "x-hide-urls-from-log"; 24 25 // For handling the seekTo before prepared, we need to know whether or not 26 // the video is prepared. Therefore, we differentiate the state between 27 // prepared and not prepared. 28 // When the video is not prepared, we will have to save the seekTo time, 29 // and use it when prepared to play. 30 // NOTE: these values are in sync with VideoLayerAndroid.h in webkit side. 31 // Please keep them in sync when changed. 32 static final int STATE_INITIALIZED = 0; 33 static final int STATE_NOTPREPARED = 1; 34 static final int STATE_PREPARED = 2; 35 static final int STATE_PLAYING = 3; 36 protected int mCurrentState; 37 38 protected HTML5VideoViewProxy mProxy; 39 40 // Save the seek time when not prepared. This can happen when switching 41 // video besides initial load. 42 protected int mSaveSeekTime; 43 44 // This is used to find the VideoLayer on the native side. 45 protected int mVideoLayerId; 46 47 // Every video will have one MediaPlayer. Given the fact we only have one 48 // SurfaceTexture, there is only one MediaPlayer in action. Every time we 49 // switch videos, a new instance of MediaPlayer will be created in reset(). 50 // Switching between inline and full screen will also create a new instance. 51 protected MediaPlayer mPlayer; 52 53 // This will be set up every time we create the Video View object. 54 // Set to true only when switching into full screen while playing 55 protected boolean mAutostart; 56 57 // We need to save such info. 58 protected String mUri; 59 protected Map<String, String> mHeaders; 60 61 // The timer for timeupate events. 62 // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate 63 protected static Timer mTimer; 64 65 // The spec says the timer should fire every 250 ms or less. 66 private static final int TIMEUPDATE_PERIOD = 250; // ms 67 68 protected boolean mPauseDuringPreparing; 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 (mCurrentState == STATE_PREPARED && mPlayer.isPlaying()) { 87 mPlayer.pause(); 88 } else if (mCurrentState == STATE_NOTPREPARED) { 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 return mPlayer.isPlaying(); 123 } 124 125 public void release() { 126 mPlayer.release(); 127 } 128 129 public void stopPlayback() { 130 if (mCurrentState == STATE_PREPARED) { 131 mPlayer.stop(); 132 } 133 } 134 135 public boolean getAutostart() { 136 return mAutostart; 137 } 138 139 public boolean getPauseDuringPreparing() { 140 return mPauseDuringPreparing; 141 } 142 143 // Every time we start a new Video, we create a VideoView and a MediaPlayer 144 public void init(int videoLayerId, int position, boolean autoStart) { 145 mPlayer = new MediaPlayer(); 146 mCurrentState = STATE_INITIALIZED; 147 mProxy = null; 148 mVideoLayerId = videoLayerId; 149 mSaveSeekTime = position; 150 mAutostart = autoStart; 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; 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 // Normally called immediately after setVideoURI. But for full screen, 198 // this should be after surface holder created 199 public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { 200 // SurfaceTexture will be created lazily here for inline mode 201 decideDisplayMode(); 202 203 setOnCompletionListener(proxy); 204 setOnPreparedListener(proxy); 205 setOnErrorListener(proxy); 206 setOnInfoListener(proxy); 207 // When there is exception, we could just bail out silently. 208 // No Video will be played though. Write the stack for debug 209 try { 210 mPlayer.setDataSource(mUri, mHeaders); 211 mPlayer.prepareAsync(); 212 } catch (IllegalArgumentException e) { 213 e.printStackTrace(); 214 } catch (IllegalStateException e) { 215 e.printStackTrace(); 216 } catch (IOException e) { 217 e.printStackTrace(); 218 } 219 mCurrentState = STATE_NOTPREPARED; 220 } 221 222 223 // Common code 224 public int getVideoLayerId() { 225 return mVideoLayerId; 226 } 227 228 229 public int getCurrentState() { 230 if (mPlayer.isPlaying()) { 231 return STATE_PLAYING; 232 } else { 233 return mCurrentState; 234 } 235 } 236 237 private static final class TimeupdateTask extends TimerTask { 238 private HTML5VideoViewProxy mProxy; 239 240 public TimeupdateTask(HTML5VideoViewProxy proxy) { 241 mProxy = proxy; 242 } 243 244 @Override 245 public void run() { 246 mProxy.onTimeupdate(); 247 } 248 } 249 250 @Override 251 public void onPrepared(MediaPlayer mp) { 252 mCurrentState = STATE_PREPARED; 253 seekTo(mSaveSeekTime); 254 if (mProxy != null) { 255 mProxy.onPrepared(mp); 256 } 257 if (mPauseDuringPreparing) { 258 pauseAndDispatch(mProxy); 259 mPauseDuringPreparing = false; 260 } 261 } 262 263 // Pause the play and update the play/pause button 264 public void pauseAndDispatch(HTML5VideoViewProxy proxy) { 265 pause(); 266 if (proxy != null) { 267 proxy.dispatchOnPaused(); 268 } 269 } 270 271 // Below are functions that are different implementation on inline and full- 272 // screen mode. Some are specific to one type, but currently are called 273 // directly from the proxy. 274 public void enterFullScreenVideoState(int layerId, 275 HTML5VideoViewProxy proxy, WebView webView) { 276 } 277 278 public boolean isFullScreenMode() { 279 return false; 280 } 281 282 public void decideDisplayMode() { 283 } 284 285 public boolean getReadyToUseSurfTex() { 286 return false; 287 } 288 289 public SurfaceTexture getSurfaceTexture() { 290 return null; 291 } 292 293 public void deleteSurfaceTexture() { 294 } 295 296 public int getTextureName() { 297 return 0; 298 } 299 300 // This is true only when the player is buffering and paused 301 public boolean mPlayerBuffering = false; 302 303 public boolean getPlayerBuffering() { 304 return mPlayerBuffering; 305 } 306 307 public void setPlayerBuffering(boolean playerBuffering) { 308 mPlayerBuffering = playerBuffering; 309 switchProgressView(playerBuffering); 310 } 311 312 313 protected void switchProgressView(boolean playerBuffering) { 314 // Only used in HTML5VideoFullScreen 315 } 316 317} 318