HTML5VideoView.java revision a9cadefde4d5f79a0f3ed093bb96d9c919ce65c3
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 } 82 } 83 84 public void pause() { 85 if (mCurrentState == STATE_PREPARED && mPlayer.isPlaying()) { 86 mPlayer.pause(); 87 } else if (mCurrentState == STATE_NOTPREPARED) { 88 mPauseDuringPreparing = true; 89 } 90 // Delete the Timer to stop it since there is no stop call. 91 if (mTimer != null) { 92 mTimer.purge(); 93 mTimer.cancel(); 94 mTimer = null; 95 } 96 } 97 98 public int getDuration() { 99 if (mCurrentState == STATE_PREPARED) { 100 return mPlayer.getDuration(); 101 } else { 102 return -1; 103 } 104 } 105 106 public int getCurrentPosition() { 107 if (mCurrentState == STATE_PREPARED) { 108 return mPlayer.getCurrentPosition(); 109 } 110 return 0; 111 } 112 113 public void seekTo(int pos) { 114 if (mCurrentState == STATE_PREPARED) 115 mPlayer.seekTo(pos); 116 else 117 mSaveSeekTime = pos; 118 } 119 120 public boolean isPlaying() { 121 return mPlayer.isPlaying(); 122 } 123 124 public void release() { 125 mPlayer.release(); 126 } 127 128 public void stopPlayback() { 129 if (mCurrentState == STATE_PREPARED) { 130 mPlayer.stop(); 131 } 132 } 133 134 public boolean getAutostart() { 135 return mAutostart; 136 } 137 138 public boolean getPauseDuringPreparing() { 139 return mPauseDuringPreparing; 140 } 141 142 // Every time we start a new Video, we create a VideoView and a MediaPlayer 143 public void init(int videoLayerId, int position, boolean autoStart) { 144 mPlayer = new MediaPlayer(); 145 mCurrentState = STATE_INITIALIZED; 146 mProxy = null; 147 mVideoLayerId = videoLayerId; 148 mSaveSeekTime = position; 149 mAutostart = autoStart; 150 mTimer = null; 151 mPauseDuringPreparing = false; 152 } 153 154 protected HTML5VideoView() { 155 } 156 157 protected static Map<String, String> generateHeaders(String url, 158 HTML5VideoViewProxy proxy) { 159 boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled(); 160 String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate); 161 Map<String, String> headers = new HashMap<String, String>(); 162 if (cookieValue != null) { 163 headers.put(COOKIE, cookieValue); 164 } 165 if (isPrivate) { 166 headers.put(HIDE_URL_LOGS, "true"); 167 } 168 169 return headers; 170 } 171 172 public void setVideoURI(String uri, HTML5VideoViewProxy proxy) { 173 // When switching players, surface texture will be reused. 174 mUri = uri; 175 mHeaders = generateHeaders(uri, proxy); 176 } 177 178 // Listeners setup FUNCTIONS: 179 public void setOnCompletionListener(HTML5VideoViewProxy proxy) { 180 mPlayer.setOnCompletionListener(proxy); 181 } 182 183 public void setOnErrorListener(HTML5VideoViewProxy proxy) { 184 mPlayer.setOnErrorListener(proxy); 185 } 186 187 public void setOnPreparedListener(HTML5VideoViewProxy proxy) { 188 mProxy = proxy; 189 mPlayer.setOnPreparedListener(this); 190 } 191 192 public void setOnInfoListener(HTML5VideoViewProxy proxy) { 193 mPlayer.setOnInfoListener(proxy); 194 } 195 196 // Normally called immediately after setVideoURI. But for full screen, 197 // this should be after surface holder created 198 public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { 199 // SurfaceTexture will be created lazily here for inline mode 200 decideDisplayMode(); 201 202 setOnCompletionListener(proxy); 203 setOnPreparedListener(proxy); 204 setOnErrorListener(proxy); 205 setOnInfoListener(proxy); 206 // When there is exception, we could just bail out silently. 207 // No Video will be played though. Write the stack for debug 208 try { 209 mPlayer.setDataSource(mUri, mHeaders); 210 mPlayer.prepareAsync(); 211 } catch (IllegalArgumentException e) { 212 e.printStackTrace(); 213 } catch (IllegalStateException e) { 214 e.printStackTrace(); 215 } catch (IOException e) { 216 e.printStackTrace(); 217 } 218 mCurrentState = STATE_NOTPREPARED; 219 } 220 221 222 // Common code 223 public int getVideoLayerId() { 224 return mVideoLayerId; 225 } 226 227 228 public int getCurrentState() { 229 if (mPlayer.isPlaying()) { 230 return STATE_PLAYING; 231 } else { 232 return mCurrentState; 233 } 234 } 235 236 private static final class TimeupdateTask extends TimerTask { 237 private HTML5VideoViewProxy mProxy; 238 239 public TimeupdateTask(HTML5VideoViewProxy proxy) { 240 mProxy = proxy; 241 } 242 243 @Override 244 public void run() { 245 mProxy.onTimeupdate(); 246 } 247 } 248 249 @Override 250 public void onPrepared(MediaPlayer mp) { 251 mCurrentState = STATE_PREPARED; 252 seekTo(mSaveSeekTime); 253 if (mProxy != null) { 254 mProxy.onPrepared(mp); 255 } 256 if (mPauseDuringPreparing) { 257 pauseAndDispatch(mProxy); 258 mPauseDuringPreparing = false; 259 } 260 } 261 262 // Pause the play and update the play/pause button 263 public void pauseAndDispatch(HTML5VideoViewProxy proxy) { 264 pause(); 265 if (proxy != null) { 266 proxy.dispatchOnPaused(); 267 } 268 } 269 270 // Below are functions that are different implementation on inline and full- 271 // screen mode. Some are specific to one type, but currently are called 272 // directly from the proxy. 273 public void enterFullScreenVideoState(int layerId, 274 HTML5VideoViewProxy proxy, WebView webView) { 275 } 276 277 public boolean isFullScreenMode() { 278 return false; 279 } 280 281 public void decideDisplayMode() { 282 } 283 284 public boolean getReadyToUseSurfTex() { 285 return false; 286 } 287 288 public SurfaceTexture getSurfaceTexture() { 289 return null; 290 } 291 292 public void deleteSurfaceTexture() { 293 } 294 295 public int getTextureName() { 296 return 0; 297 } 298 299} 300