blob: c7270299bec063b5721c93ce996a6aa2967fbcad [file] [log] [blame]
Andrei Popescu6fa29582009-06-19 14:54:09 +01001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.content.Context;
Andrei Popescu64b86a12009-09-15 20:34:18 +010020import android.graphics.Bitmap;
21import android.graphics.BitmapFactory;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -080022import android.graphics.SurfaceTexture;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010023import android.media.MediaPlayer;
Andrei Popescu64b86a12009-09-15 20:34:18 +010024import android.net.http.EventHandler;
25import android.net.http.Headers;
26import android.net.http.RequestHandle;
27import android.net.http.RequestQueue;
28import android.net.http.SslCertificate;
29import android.net.http.SslError;
Andrei Popescu6fa29582009-06-19 14:54:09 +010030import android.os.Handler;
31import android.os.Looper;
32import android.os.Message;
33import android.util.Log;
Andrei Popescu6fa29582009-06-19 14:54:09 +010034
Andrei Popescu64b86a12009-09-15 20:34:18 +010035import java.io.ByteArrayOutputStream;
36import java.io.IOException;
Ben Murdoch42509792011-02-18 12:34:35 +000037import java.net.MalformedURLException;
38import java.net.URL;
Andrei Popescu6fa29582009-06-19 14:54:09 +010039import java.util.HashMap;
Andrei Popescu290c34a2009-09-17 15:55:24 +010040import java.util.Map;
Andrei Popescu6fa29582009-06-19 14:54:09 +010041
42/**
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010043 * <p>Proxy for HTML5 video views.
Andrei Popescu6fa29582009-06-19 14:54:09 +010044 */
Andrei Popescu290c34a2009-09-17 15:55:24 +010045class HTML5VideoViewProxy extends Handler
46 implements MediaPlayer.OnPreparedListener,
Andrei Popescu50c86232009-09-30 16:51:25 +010047 MediaPlayer.OnCompletionListener,
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -080048 MediaPlayer.OnErrorListener,
49 SurfaceTexture.OnFrameAvailableListener {
Andrei Popescu6fa29582009-06-19 14:54:09 +010050 // Logging tag.
51 private static final String LOGTAG = "HTML5VideoViewProxy";
52
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010053 // Message Ids for WebCore thread -> UI thread communication.
Andrei Popescu50c86232009-09-30 16:51:25 +010054 private static final int PLAY = 100;
55 private static final int SEEK = 101;
56 private static final int PAUSE = 102;
57 private static final int ERROR = 103;
58 private static final int LOAD_DEFAULT_POSTER = 104;
Andrei Popescu6fa29582009-06-19 14:54:09 +010059
Andrei Popescu64b86a12009-09-15 20:34:18 +010060 // Message Ids to be handled on the WebCore thread
Andrei Popescu290c34a2009-09-17 15:55:24 +010061 private static final int PREPARED = 200;
62 private static final int ENDED = 201;
Andrei Popescu50c86232009-09-30 16:51:25 +010063 private static final int POSTER_FETCHED = 202;
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -070064 private static final int PAUSED = 203;
Andrei Popescu64b86a12009-09-15 20:34:18 +010065
Andrei Popescu048eb3b2010-01-11 21:12:54 +000066 // Timer thread -> UI thread
67 private static final int TIMEUPDATE = 300;
68
Andrei Popescu290c34a2009-09-17 15:55:24 +010069 // The C++ MediaPlayerPrivateAndroid object.
70 int mNativePointer;
Andrei Popescu64b86a12009-09-15 20:34:18 +010071 // The handler for WebCore thread messages;
72 private Handler mWebCoreHandler;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010073 // The WebView instance that created this view.
74 private WebView mWebView;
Andrei Popescu64b86a12009-09-15 20:34:18 +010075 // The poster image to be shown when the video is not playing.
Andrei Popescu50c86232009-09-30 16:51:25 +010076 // This ref prevents the bitmap from being GC'ed.
77 private Bitmap mPoster;
Andrei Popescu64b86a12009-09-15 20:34:18 +010078 // The poster downloader.
79 private PosterDownloader mPosterDownloader;
Andrei Popescu290c34a2009-09-17 15:55:24 +010080 // The seek position.
81 private int mSeekPosition;
Andrei Popescu64b86a12009-09-15 20:34:18 +010082 // A helper class to control the playback. This executes on the UI thread!
83 private static final class VideoPlayer {
84 // The proxy that is currently playing (if any).
85 private static HTML5VideoViewProxy mCurrentProxy;
86 // The VideoView instance. This is a singleton for now, at least until
87 // http://b/issue?id=1973663 is fixed.
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -080088 private static HTML5VideoView mHTML5VideoView;
Andrei Popescu048eb3b2010-01-11 21:12:54 +000089
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -080090 private static boolean isVideoSelfEnded = false;
91 // By using the baseLayer and the current video Layer ID, we can
92 // identify the exact layer on the UI thread to use the SurfaceTexture.
93 private static int mBaseLayer = 0;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010094
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -080095 // Every time webView setBaseLayer, this will be called.
96 // When we found the Video layer, then we set the Surface Texture to it.
97 // Otherwise, we may want to delete the Surface Texture to save memory.
98 public static void setBaseLayer(int layer) {
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -070099 // Don't do this for full screen mode.
100 if (mHTML5VideoView != null
101 && !mHTML5VideoView.isFullScreenMode()) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800102 mBaseLayer = layer;
103 SurfaceTexture surfTexture = mHTML5VideoView.getSurfaceTexture();
104 int textureName = mHTML5VideoView.getTextureName();
105
106 int currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
107 if (layer != 0 && surfTexture != null && currentVideoLayerId != -1) {
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700108 int playerState = mHTML5VideoView.getCurrentState();
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800109 boolean foundInTree = nativeSendSurfaceTexture(surfTexture,
110 layer, currentVideoLayerId, textureName,
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700111 playerState);
Teng-Hui Zhu1e26d822011-03-23 16:54:20 -0700112 if (playerState >= HTML5VideoView.STATE_PREPARED
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700113 && !foundInTree) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800114 mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
115 mHTML5VideoView.deleteSurfaceTexture();
Andrei Popescu31d2aa12010-04-08 15:19:22 +0100116 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100117 }
Andrei Popescua41f97b2010-01-11 18:36:25 +0000118 }
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800119 }
Andrei Popescua41f97b2010-01-11 18:36:25 +0000120
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800121 // When a WebView is paused, we also want to pause the video in it.
122 public static void pauseAndDispatch() {
123 if (mHTML5VideoView != null) {
124 mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
125 // When switching out, clean the video content on the old page
126 // by telling the layer not readyToUseSurfTex.
127 setBaseLayer(mBaseLayer);
128 }
129 }
130
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700131 public static void enterFullScreenVideo(int layerId, String url,
132 HTML5VideoViewProxy proxy, WebView webView) {
133 // Save the inline video info and inherit it in the full screen
134 int savePosition = 0;
135 boolean savedIsPlaying = false;
136 if (mHTML5VideoView != null) {
137 // If we are playing the same video, then it is better to
138 // save the current position.
139 if (layerId == mHTML5VideoView.getVideoLayerId()) {
140 savePosition = mHTML5VideoView.getCurrentPosition();
141 savedIsPlaying = mHTML5VideoView.isPlaying();
142 }
143 mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
144 mHTML5VideoView.release();
145 }
146 mHTML5VideoView = new HTML5VideoFullScreen(proxy.getContext(),
147 layerId, savePosition, savedIsPlaying);
148 mCurrentProxy = proxy;
149
150 mHTML5VideoView.setVideoURI(url, mCurrentProxy);
151
152 mHTML5VideoView.enterFullScreenVideoState(layerId, proxy, webView);
153 }
154
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800155 // This is on the UI thread.
156 // When native tell Java to play, we need to check whether or not it is
157 // still the same video by using videoLayerId and treat it differently.
158 public static void play(String url, int time, HTML5VideoViewProxy proxy,
159 WebChromeClient client, int videoLayerId) {
160 int currentVideoLayerId = -1;
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700161 boolean backFromFullScreenMode = false;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800162
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700163 if (mHTML5VideoView != null) {
164 currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
165 if (mHTML5VideoView instanceof HTML5VideoFullScreen) {
166 backFromFullScreenMode = true;
167 }
168 }
169
170 if (backFromFullScreenMode
171 || currentVideoLayerId != videoLayerId
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800172 || mHTML5VideoView.getSurfaceTexture() == null) {
173 // Here, we handle the case when switching to a new video,
174 // either inside a WebView or across WebViews
175 // For switching videos within a WebView or across the WebView,
176 // we need to pause the old one and re-create a new media player
177 // inside the HTML5VideoView.
178 if (mHTML5VideoView != null) {
179 mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
180 // release the media player to avoid finalize error
181 mHTML5VideoView.release();
182 }
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800183 mCurrentProxy = proxy;
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700184 mHTML5VideoView = new HTML5VideoInline(videoLayerId, time, false);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800185
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700186 mHTML5VideoView.setVideoURI(url, mCurrentProxy);
187 mHTML5VideoView.prepareDataAndDisplayMode(proxy);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800188 } else if (mCurrentProxy == proxy) {
189 // Here, we handle the case when we keep playing with one video
190 if (!mHTML5VideoView.isPlaying()) {
191 mHTML5VideoView.seekTo(time);
192 mHTML5VideoView.start();
193 }
194 } else if (mCurrentProxy != null) {
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700195 // Some other video is already playing. Notify the caller that
196 // its playback ended.
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100197 proxy.dispatchOnEnded();
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100198 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100199 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100200
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000201 public static boolean isPlaying(HTML5VideoViewProxy proxy) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800202 return (mCurrentProxy == proxy && mHTML5VideoView != null
203 && mHTML5VideoView.isPlaying());
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000204 }
205
206 public static int getCurrentPosition() {
207 int currentPosMs = 0;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800208 if (mHTML5VideoView != null) {
209 currentPosMs = mHTML5VideoView.getCurrentPosition();
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000210 }
211 return currentPosMs;
212 }
213
Andrei Popescu290c34a2009-09-17 15:55:24 +0100214 public static void seek(int time, HTML5VideoViewProxy proxy) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800215 if (mCurrentProxy == proxy && time >= 0 && mHTML5VideoView != null) {
216 mHTML5VideoView.seekTo(time);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100217 }
218 }
219
220 public static void pause(HTML5VideoViewProxy proxy) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800221 if (mCurrentProxy == proxy && mHTML5VideoView != null) {
222 mHTML5VideoView.pause();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100223 }
224 }
Andrei Popescubf385d72009-09-18 18:59:52 +0100225
226 public static void onPrepared() {
Teng-Hui Zhu05049672011-04-06 18:09:27 -0700227 if (!mHTML5VideoView.isFullScreenMode() || mHTML5VideoView.getAutostart()) {
228 mHTML5VideoView.start();
229 }
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700230 if (mBaseLayer != 0) {
231 setBaseLayer(mBaseLayer);
232 }
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800233 }
234
235 public static void end() {
236 if (mCurrentProxy != null) {
237 if (isVideoSelfEnded)
238 mCurrentProxy.dispatchOnEnded();
239 else
240 mCurrentProxy.dispatchOnPaused();
241 }
242 isVideoSelfEnded = false;
Andrei Popescubf385d72009-09-18 18:59:52 +0100243 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100244 }
245
246 // A bunch event listeners for our VideoView
247 // MediaPlayer.OnPreparedListener
248 public void onPrepared(MediaPlayer mp) {
Andrei Popescubf385d72009-09-18 18:59:52 +0100249 VideoPlayer.onPrepared();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100250 Message msg = Message.obtain(mWebCoreHandler, PREPARED);
251 Map<String, Object> map = new HashMap<String, Object>();
252 map.put("dur", new Integer(mp.getDuration()));
253 map.put("width", new Integer(mp.getVideoWidth()));
254 map.put("height", new Integer(mp.getVideoHeight()));
255 msg.obj = map;
256 mWebCoreHandler.sendMessage(msg);
257 }
258
259 // MediaPlayer.OnCompletionListener;
260 public void onCompletion(MediaPlayer mp) {
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100261 // The video ended by itself, so we need to
262 // send a message to the UI thread to dismiss
263 // the video view and to return to the WebView.
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700264 // arg1 == 1 means the video ends by itself.
265 sendMessage(obtainMessage(ENDED, 1, 0));
Andrei Popescu290c34a2009-09-17 15:55:24 +0100266 }
267
Andrei Popescu50c86232009-09-30 16:51:25 +0100268 // MediaPlayer.OnErrorListener
269 public boolean onError(MediaPlayer mp, int what, int extra) {
270 sendMessage(obtainMessage(ERROR));
271 return false;
272 }
273
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100274 public void dispatchOnEnded() {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100275 Message msg = Message.obtain(mWebCoreHandler, ENDED);
276 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100277 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100278
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700279 public void dispatchOnPaused() {
280 Message msg = Message.obtain(mWebCoreHandler, PAUSED);
281 mWebCoreHandler.sendMessage(msg);
282 }
283
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000284 public void onTimeupdate() {
285 sendMessage(obtainMessage(TIMEUPDATE));
286 }
287
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800288 // When there is a frame ready from surface texture, we should tell WebView
289 // to refresh.
290 @Override
291 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
292 // TODO: This should support partial invalidation too.
293 mWebView.invalidate();
294 }
295
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000296 // Handler for the messages from WebCore or Timer thread to the UI thread.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100297 @Override
298 public void handleMessage(Message msg) {
299 // This executes on the UI thread.
300 switch (msg.what) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100301 case PLAY: {
302 String url = (String) msg.obj;
303 WebChromeClient client = mWebView.getWebChromeClient();
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800304 int videoLayerID = msg.arg1;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100305 if (client != null) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800306 VideoPlayer.play(url, mSeekPosition, this, client, videoLayerID);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100307 }
308 break;
309 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100310 case SEEK: {
311 Integer time = (Integer) msg.obj;
312 mSeekPosition = time;
313 VideoPlayer.seek(mSeekPosition, this);
314 break;
315 }
316 case PAUSE: {
317 VideoPlayer.pause(this);
318 break;
319 }
Andrei Popescu46a83b42009-10-23 13:49:46 +0100320 case ENDED:
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700321 if (msg.arg1 == 1)
322 VideoPlayer.isVideoSelfEnded = true;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800323 VideoPlayer.end();
324 break;
Andrei Popescu50c86232009-09-30 16:51:25 +0100325 case ERROR: {
326 WebChromeClient client = mWebView.getWebChromeClient();
327 if (client != null) {
328 client.onHideCustomView();
329 }
330 break;
331 }
332 case LOAD_DEFAULT_POSTER: {
333 WebChromeClient client = mWebView.getWebChromeClient();
334 if (client != null) {
335 doSetPoster(client.getDefaultVideoPoster());
336 }
337 break;
338 }
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000339 case TIMEUPDATE: {
340 if (VideoPlayer.isPlaying(this)) {
341 sendTimeupdate();
342 }
343 break;
344 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100345 }
346 }
347
Andrei Popescu64b86a12009-09-15 20:34:18 +0100348 // Everything below this comment executes on the WebCore thread, except for
349 // the EventHandler methods, which are called on the network thread.
350
351 // A helper class that knows how to download posters
352 private static final class PosterDownloader implements EventHandler {
353 // The request queue. This is static as we have one queue for all posters.
354 private static RequestQueue mRequestQueue;
355 private static int mQueueRefCount = 0;
356 // The poster URL
Ben Murdoch42509792011-02-18 12:34:35 +0000357 private URL mUrl;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100358 // The proxy we're doing this for.
359 private final HTML5VideoViewProxy mProxy;
360 // The poster bytes. We only touch this on the network thread.
361 private ByteArrayOutputStream mPosterBytes;
362 // The request handle. We only touch this on the WebCore thread.
363 private RequestHandle mRequestHandle;
364 // The response status code.
365 private int mStatusCode;
366 // The response headers.
367 private Headers mHeaders;
368 // The handler to handle messages on the WebCore thread.
369 private Handler mHandler;
370
371 public PosterDownloader(String url, HTML5VideoViewProxy proxy) {
Ben Murdoch42509792011-02-18 12:34:35 +0000372 try {
373 mUrl = new URL(url);
374 } catch (MalformedURLException e) {
375 mUrl = null;
376 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100377 mProxy = proxy;
378 mHandler = new Handler();
379 }
380 // Start the download. Called on WebCore thread.
381 public void start() {
382 retainQueue();
Ben Murdoch42509792011-02-18 12:34:35 +0000383
384 if (mUrl == null) {
385 return;
386 }
387
388 // Only support downloading posters over http/https.
389 // FIXME: Add support for other schemes. WebKit seems able to load
390 // posters over other schemes e.g. file://, but gets the dimensions wrong.
391 String protocol = mUrl.getProtocol();
392 if ("http".equals(protocol) || "https".equals(protocol)) {
393 mRequestHandle = mRequestQueue.queueRequest(mUrl.toString(), "GET", null,
394 this, null, 0);
395 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100396 }
397 // Cancel the download if active and release the queue. Called on WebCore thread.
398 public void cancelAndReleaseQueue() {
399 if (mRequestHandle != null) {
400 mRequestHandle.cancel();
401 mRequestHandle = null;
402 }
403 releaseQueue();
404 }
405 // EventHandler methods. Executed on the network thread.
406 public void status(int major_version,
407 int minor_version,
408 int code,
409 String reason_phrase) {
410 mStatusCode = code;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100411 }
412
Andrei Popescu64b86a12009-09-15 20:34:18 +0100413 public void headers(Headers headers) {
414 mHeaders = headers;
415 }
416
417 public void data(byte[] data, int len) {
418 if (mPosterBytes == null) {
419 mPosterBytes = new ByteArrayOutputStream();
420 }
421 mPosterBytes.write(data, 0, len);
422 }
423
424 public void endData() {
425 if (mStatusCode == 200) {
426 if (mPosterBytes.size() > 0) {
427 Bitmap poster = BitmapFactory.decodeByteArray(
428 mPosterBytes.toByteArray(), 0, mPosterBytes.size());
Andrei Popescu50c86232009-09-30 16:51:25 +0100429 mProxy.doSetPoster(poster);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100430 }
431 cleanup();
432 } else if (mStatusCode >= 300 && mStatusCode < 400) {
433 // We have a redirect.
Ben Murdoch42509792011-02-18 12:34:35 +0000434 try {
435 mUrl = new URL(mHeaders.getLocation());
436 } catch (MalformedURLException e) {
437 mUrl = null;
438 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100439 if (mUrl != null) {
440 mHandler.post(new Runnable() {
441 public void run() {
442 if (mRequestHandle != null) {
Ben Murdoch42509792011-02-18 12:34:35 +0000443 mRequestHandle.setupRedirect(mUrl.toString(), mStatusCode,
Andrei Popescu64b86a12009-09-15 20:34:18 +0100444 new HashMap<String, String>());
445 }
446 }
447 });
448 }
449 }
450 }
451
452 public void certificate(SslCertificate certificate) {
453 // Don't care.
454 }
455
456 public void error(int id, String description) {
457 cleanup();
458 }
459
460 public boolean handleSslErrorRequest(SslError error) {
461 // Don't care. If this happens, data() will never be called so
462 // mPosterBytes will never be created, so no need to call cleanup.
463 return false;
464 }
465 // Tears down the poster bytes stream. Called on network thread.
466 private void cleanup() {
467 if (mPosterBytes != null) {
468 try {
469 mPosterBytes.close();
470 } catch (IOException ignored) {
471 // Ignored.
472 } finally {
473 mPosterBytes = null;
474 }
475 }
476 }
477
478 // Queue management methods. Called on WebCore thread.
479 private void retainQueue() {
480 if (mRequestQueue == null) {
481 mRequestQueue = new RequestQueue(mProxy.getContext());
482 }
483 mQueueRefCount++;
484 }
485
486 private void releaseQueue() {
487 if (mQueueRefCount == 0) {
488 return;
489 }
490 if (--mQueueRefCount == 0) {
491 mRequestQueue.shutdown();
492 mRequestQueue = null;
493 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100494 }
495 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100496
497 /**
498 * Private constructor.
Andrei Popescu290c34a2009-09-17 15:55:24 +0100499 * @param webView is the WebView that hosts the video.
500 * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100501 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100502 private HTML5VideoViewProxy(WebView webView, int nativePtr) {
Andrei Popescu6fa29582009-06-19 14:54:09 +0100503 // This handler is for the main (UI) thread.
504 super(Looper.getMainLooper());
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100505 // Save the WebView object.
506 mWebView = webView;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800507 // Pass Proxy into webview, such that every time we have a setBaseLayer
508 // call, we tell this Proxy to call the native to update the layer tree
509 // for the Video Layer's surface texture info
510 mWebView.setHTML5VideoViewProxy(this);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100511 // Save the native ptr
512 mNativePointer = nativePtr;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100513 // create the message handler for this thread
514 createWebCoreHandler();
Andrei Popescu6fa29582009-06-19 14:54:09 +0100515 }
516
Andrei Popescu64b86a12009-09-15 20:34:18 +0100517 private void createWebCoreHandler() {
518 mWebCoreHandler = new Handler() {
519 @Override
520 public void handleMessage(Message msg) {
521 switch (msg.what) {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100522 case PREPARED: {
523 Map<String, Object> map = (Map<String, Object>) msg.obj;
524 Integer duration = (Integer) map.get("dur");
525 Integer width = (Integer) map.get("width");
526 Integer height = (Integer) map.get("height");
527 nativeOnPrepared(duration.intValue(), width.intValue(),
528 height.intValue(), mNativePointer);
529 break;
530 }
531 case ENDED:
Ben Murdochff19d192011-01-17 18:08:56 +0000532 mSeekPosition = 0;
Andrei Popescu290c34a2009-09-17 15:55:24 +0100533 nativeOnEnded(mNativePointer);
534 break;
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700535 case PAUSED:
536 nativeOnPaused(mNativePointer);
537 break;
Andrei Popescu50c86232009-09-30 16:51:25 +0100538 case POSTER_FETCHED:
539 Bitmap poster = (Bitmap) msg.obj;
540 nativeOnPosterFetched(poster, mNativePointer);
541 break;
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000542 case TIMEUPDATE:
543 nativeOnTimeupdate(msg.arg1, mNativePointer);
544 break;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100545 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100546 }
547 };
Andrei Popescu6fa29582009-06-19 14:54:09 +0100548 }
549
Andrei Popescu64b86a12009-09-15 20:34:18 +0100550 private void doSetPoster(Bitmap poster) {
551 if (poster == null) {
552 return;
553 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100554 // Save a ref to the bitmap and send it over to the WebCore thread.
555 mPoster = poster;
556 Message msg = Message.obtain(mWebCoreHandler, POSTER_FETCHED);
557 msg.obj = poster;
558 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100559 }
560
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000561 private void sendTimeupdate() {
562 Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE);
563 msg.arg1 = VideoPlayer.getCurrentPosition();
564 mWebCoreHandler.sendMessage(msg);
565 }
566
Andrei Popescu64b86a12009-09-15 20:34:18 +0100567 public Context getContext() {
568 return mWebView.getContext();
569 }
570
571 // The public methods below are all called from WebKit only.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100572 /**
573 * Play a video stream.
574 * @param url is the URL of the video stream.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100575 */
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800576 public void play(String url, int position, int videoLayerID) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100577 if (url == null) {
578 return;
579 }
Ben Murdochff19d192011-01-17 18:08:56 +0000580
581 if (position > 0) {
582 seek(position);
583 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100584 Message message = obtainMessage(PLAY);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800585 message.arg1 = videoLayerID;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100586 message.obj = url;
Andrei Popescu6fa29582009-06-19 14:54:09 +0100587 sendMessage(message);
588 }
589
Andrei Popescu64b86a12009-09-15 20:34:18 +0100590 /**
Andrei Popescu290c34a2009-09-17 15:55:24 +0100591 * Seek into the video stream.
592 * @param time is the position in the video stream.
593 */
594 public void seek(int time) {
595 Message message = obtainMessage(SEEK);
596 message.obj = new Integer(time);
597 sendMessage(message);
598 }
599
600 /**
601 * Pause the playback.
602 */
603 public void pause() {
604 Message message = obtainMessage(PAUSE);
605 sendMessage(message);
606 }
607
608 /**
Andrei Popescu50c86232009-09-30 16:51:25 +0100609 * Tear down this proxy object.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100610 */
Andrei Popescu50c86232009-09-30 16:51:25 +0100611 public void teardown() {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100612 // This is called by the C++ MediaPlayerPrivate dtor.
613 // Cancel any active poster download.
614 if (mPosterDownloader != null) {
615 mPosterDownloader.cancelAndReleaseQueue();
616 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100617 mNativePointer = 0;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100618 }
619
620 /**
621 * Load the poster image.
622 * @param url is the URL of the poster image.
623 */
624 public void loadPoster(String url) {
625 if (url == null) {
Andrei Popescu50c86232009-09-30 16:51:25 +0100626 Message message = obtainMessage(LOAD_DEFAULT_POSTER);
627 sendMessage(message);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100628 return;
629 }
630 // Cancel any active poster download.
631 if (mPosterDownloader != null) {
632 mPosterDownloader.cancelAndReleaseQueue();
633 }
634 // Load the poster asynchronously
635 mPosterDownloader = new PosterDownloader(url, this);
636 mPosterDownloader.start();
Patrick Scott0a5ce012009-07-02 08:56:10 -0400637 }
638
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700639 // These three function are called from UI thread only by WebView.
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800640 public void setBaseLayer(int layer) {
641 VideoPlayer.setBaseLayer(layer);
642 }
643
644 public void pauseAndDispatch() {
645 VideoPlayer.pauseAndDispatch();
646 }
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700647
648 public void enterFullScreenVideo(int layerId, String url) {
649 VideoPlayer.enterFullScreenVideo(layerId, url, this, mWebView);
650 }
651
Andrei Popescu6fa29582009-06-19 14:54:09 +0100652 /**
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100653 * The factory for HTML5VideoViewProxy instances.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100654 * @param webViewCore is the WebViewCore that is requesting the proxy.
655 *
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100656 * @return a new HTML5VideoViewProxy object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100657 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100658 public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) {
659 return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100660 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100661
Ben Murdoch1708ad52010-11-11 15:56:16 +0000662 /* package */ WebView getWebView() {
663 return mWebView;
664 }
665
Andrei Popescu290c34a2009-09-17 15:55:24 +0100666 private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
667 private native void nativeOnEnded(int nativePointer);
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700668 private native void nativeOnPaused(int nativePointer);
Andrei Popescu50c86232009-09-30 16:51:25 +0100669 private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000670 private native void nativeOnTimeupdate(int position, int nativePointer);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800671 private native static boolean nativeSendSurfaceTexture(SurfaceTexture texture,
672 int baseLayer, int videoLayerId, int textureName,
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700673 int playerState);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100674}