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