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