blob: d12b965f66f027bf90c169646ca1dee32842162a [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);
112 if (playerState == HTML5VideoView.STATE_PREPARED
113 && !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 Zhu10ab6542011-03-16 16:42:32 -0700227 if (!mHTML5VideoView.isFullScreenMode() ||
228 mHTML5VideoView.isFullScreenMode() &&
229 mHTML5VideoView.getAutostart() )
230 mHTML5VideoView.start();
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700231 if (mBaseLayer != 0) {
232 setBaseLayer(mBaseLayer);
233 }
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800234 }
235
236 public static void end() {
237 if (mCurrentProxy != null) {
238 if (isVideoSelfEnded)
239 mCurrentProxy.dispatchOnEnded();
240 else
241 mCurrentProxy.dispatchOnPaused();
242 }
243 isVideoSelfEnded = false;
Andrei Popescubf385d72009-09-18 18:59:52 +0100244 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100245 }
246
247 // A bunch event listeners for our VideoView
248 // MediaPlayer.OnPreparedListener
249 public void onPrepared(MediaPlayer mp) {
Andrei Popescubf385d72009-09-18 18:59:52 +0100250 VideoPlayer.onPrepared();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100251 Message msg = Message.obtain(mWebCoreHandler, PREPARED);
252 Map<String, Object> map = new HashMap<String, Object>();
253 map.put("dur", new Integer(mp.getDuration()));
254 map.put("width", new Integer(mp.getVideoWidth()));
255 map.put("height", new Integer(mp.getVideoHeight()));
256 msg.obj = map;
257 mWebCoreHandler.sendMessage(msg);
258 }
259
260 // MediaPlayer.OnCompletionListener;
261 public void onCompletion(MediaPlayer mp) {
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100262 // The video ended by itself, so we need to
263 // send a message to the UI thread to dismiss
264 // the video view and to return to the WebView.
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700265 // arg1 == 1 means the video ends by itself.
266 sendMessage(obtainMessage(ENDED, 1, 0));
Andrei Popescu290c34a2009-09-17 15:55:24 +0100267 }
268
Andrei Popescu50c86232009-09-30 16:51:25 +0100269 // MediaPlayer.OnErrorListener
270 public boolean onError(MediaPlayer mp, int what, int extra) {
271 sendMessage(obtainMessage(ERROR));
272 return false;
273 }
274
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100275 public void dispatchOnEnded() {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100276 Message msg = Message.obtain(mWebCoreHandler, ENDED);
277 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100278 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100279
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700280 public void dispatchOnPaused() {
281 Message msg = Message.obtain(mWebCoreHandler, PAUSED);
282 mWebCoreHandler.sendMessage(msg);
283 }
284
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000285 public void onTimeupdate() {
286 sendMessage(obtainMessage(TIMEUPDATE));
287 }
288
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800289 // When there is a frame ready from surface texture, we should tell WebView
290 // to refresh.
291 @Override
292 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
293 // TODO: This should support partial invalidation too.
294 mWebView.invalidate();
295 }
296
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000297 // Handler for the messages from WebCore or Timer thread to the UI thread.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100298 @Override
299 public void handleMessage(Message msg) {
300 // This executes on the UI thread.
301 switch (msg.what) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100302 case PLAY: {
303 String url = (String) msg.obj;
304 WebChromeClient client = mWebView.getWebChromeClient();
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800305 int videoLayerID = msg.arg1;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100306 if (client != null) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800307 VideoPlayer.play(url, mSeekPosition, this, client, videoLayerID);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100308 }
309 break;
310 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100311 case SEEK: {
312 Integer time = (Integer) msg.obj;
313 mSeekPosition = time;
314 VideoPlayer.seek(mSeekPosition, this);
315 break;
316 }
317 case PAUSE: {
318 VideoPlayer.pause(this);
319 break;
320 }
Andrei Popescu46a83b42009-10-23 13:49:46 +0100321 case ENDED:
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700322 if (msg.arg1 == 1)
323 VideoPlayer.isVideoSelfEnded = true;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800324 VideoPlayer.end();
325 break;
Andrei Popescu50c86232009-09-30 16:51:25 +0100326 case ERROR: {
327 WebChromeClient client = mWebView.getWebChromeClient();
328 if (client != null) {
329 client.onHideCustomView();
330 }
331 break;
332 }
333 case LOAD_DEFAULT_POSTER: {
334 WebChromeClient client = mWebView.getWebChromeClient();
335 if (client != null) {
336 doSetPoster(client.getDefaultVideoPoster());
337 }
338 break;
339 }
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000340 case TIMEUPDATE: {
341 if (VideoPlayer.isPlaying(this)) {
342 sendTimeupdate();
343 }
344 break;
345 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100346 }
347 }
348
Andrei Popescu64b86a12009-09-15 20:34:18 +0100349 // Everything below this comment executes on the WebCore thread, except for
350 // the EventHandler methods, which are called on the network thread.
351
352 // A helper class that knows how to download posters
353 private static final class PosterDownloader implements EventHandler {
354 // The request queue. This is static as we have one queue for all posters.
355 private static RequestQueue mRequestQueue;
356 private static int mQueueRefCount = 0;
357 // The poster URL
Ben Murdoch42509792011-02-18 12:34:35 +0000358 private URL mUrl;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100359 // The proxy we're doing this for.
360 private final HTML5VideoViewProxy mProxy;
361 // The poster bytes. We only touch this on the network thread.
362 private ByteArrayOutputStream mPosterBytes;
363 // The request handle. We only touch this on the WebCore thread.
364 private RequestHandle mRequestHandle;
365 // The response status code.
366 private int mStatusCode;
367 // The response headers.
368 private Headers mHeaders;
369 // The handler to handle messages on the WebCore thread.
370 private Handler mHandler;
371
372 public PosterDownloader(String url, HTML5VideoViewProxy proxy) {
Ben Murdoch42509792011-02-18 12:34:35 +0000373 try {
374 mUrl = new URL(url);
375 } catch (MalformedURLException e) {
376 mUrl = null;
377 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100378 mProxy = proxy;
379 mHandler = new Handler();
380 }
381 // Start the download. Called on WebCore thread.
382 public void start() {
383 retainQueue();
Ben Murdoch42509792011-02-18 12:34:35 +0000384
385 if (mUrl == null) {
386 return;
387 }
388
389 // Only support downloading posters over http/https.
390 // FIXME: Add support for other schemes. WebKit seems able to load
391 // posters over other schemes e.g. file://, but gets the dimensions wrong.
392 String protocol = mUrl.getProtocol();
393 if ("http".equals(protocol) || "https".equals(protocol)) {
394 mRequestHandle = mRequestQueue.queueRequest(mUrl.toString(), "GET", null,
395 this, null, 0);
396 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100397 }
398 // Cancel the download if active and release the queue. Called on WebCore thread.
399 public void cancelAndReleaseQueue() {
400 if (mRequestHandle != null) {
401 mRequestHandle.cancel();
402 mRequestHandle = null;
403 }
404 releaseQueue();
405 }
406 // EventHandler methods. Executed on the network thread.
407 public void status(int major_version,
408 int minor_version,
409 int code,
410 String reason_phrase) {
411 mStatusCode = code;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100412 }
413
Andrei Popescu64b86a12009-09-15 20:34:18 +0100414 public void headers(Headers headers) {
415 mHeaders = headers;
416 }
417
418 public void data(byte[] data, int len) {
419 if (mPosterBytes == null) {
420 mPosterBytes = new ByteArrayOutputStream();
421 }
422 mPosterBytes.write(data, 0, len);
423 }
424
425 public void endData() {
426 if (mStatusCode == 200) {
427 if (mPosterBytes.size() > 0) {
428 Bitmap poster = BitmapFactory.decodeByteArray(
429 mPosterBytes.toByteArray(), 0, mPosterBytes.size());
Andrei Popescu50c86232009-09-30 16:51:25 +0100430 mProxy.doSetPoster(poster);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100431 }
432 cleanup();
433 } else if (mStatusCode >= 300 && mStatusCode < 400) {
434 // We have a redirect.
Ben Murdoch42509792011-02-18 12:34:35 +0000435 try {
436 mUrl = new URL(mHeaders.getLocation());
437 } catch (MalformedURLException e) {
438 mUrl = null;
439 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100440 if (mUrl != null) {
441 mHandler.post(new Runnable() {
442 public void run() {
443 if (mRequestHandle != null) {
Ben Murdoch42509792011-02-18 12:34:35 +0000444 mRequestHandle.setupRedirect(mUrl.toString(), mStatusCode,
Andrei Popescu64b86a12009-09-15 20:34:18 +0100445 new HashMap<String, String>());
446 }
447 }
448 });
449 }
450 }
451 }
452
453 public void certificate(SslCertificate certificate) {
454 // Don't care.
455 }
456
457 public void error(int id, String description) {
458 cleanup();
459 }
460
461 public boolean handleSslErrorRequest(SslError error) {
462 // Don't care. If this happens, data() will never be called so
463 // mPosterBytes will never be created, so no need to call cleanup.
464 return false;
465 }
466 // Tears down the poster bytes stream. Called on network thread.
467 private void cleanup() {
468 if (mPosterBytes != null) {
469 try {
470 mPosterBytes.close();
471 } catch (IOException ignored) {
472 // Ignored.
473 } finally {
474 mPosterBytes = null;
475 }
476 }
477 }
478
479 // Queue management methods. Called on WebCore thread.
480 private void retainQueue() {
481 if (mRequestQueue == null) {
482 mRequestQueue = new RequestQueue(mProxy.getContext());
483 }
484 mQueueRefCount++;
485 }
486
487 private void releaseQueue() {
488 if (mQueueRefCount == 0) {
489 return;
490 }
491 if (--mQueueRefCount == 0) {
492 mRequestQueue.shutdown();
493 mRequestQueue = null;
494 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100495 }
496 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100497
498 /**
499 * Private constructor.
Andrei Popescu290c34a2009-09-17 15:55:24 +0100500 * @param webView is the WebView that hosts the video.
501 * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100502 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100503 private HTML5VideoViewProxy(WebView webView, int nativePtr) {
Andrei Popescu6fa29582009-06-19 14:54:09 +0100504 // This handler is for the main (UI) thread.
505 super(Looper.getMainLooper());
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100506 // Save the WebView object.
507 mWebView = webView;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800508 // Pass Proxy into webview, such that every time we have a setBaseLayer
509 // call, we tell this Proxy to call the native to update the layer tree
510 // for the Video Layer's surface texture info
511 mWebView.setHTML5VideoViewProxy(this);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100512 // Save the native ptr
513 mNativePointer = nativePtr;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100514 // create the message handler for this thread
515 createWebCoreHandler();
Andrei Popescu6fa29582009-06-19 14:54:09 +0100516 }
517
Andrei Popescu64b86a12009-09-15 20:34:18 +0100518 private void createWebCoreHandler() {
519 mWebCoreHandler = new Handler() {
520 @Override
521 public void handleMessage(Message msg) {
522 switch (msg.what) {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100523 case PREPARED: {
524 Map<String, Object> map = (Map<String, Object>) msg.obj;
525 Integer duration = (Integer) map.get("dur");
526 Integer width = (Integer) map.get("width");
527 Integer height = (Integer) map.get("height");
528 nativeOnPrepared(duration.intValue(), width.intValue(),
529 height.intValue(), mNativePointer);
530 break;
531 }
532 case ENDED:
Ben Murdochff19d192011-01-17 18:08:56 +0000533 mSeekPosition = 0;
Andrei Popescu290c34a2009-09-17 15:55:24 +0100534 nativeOnEnded(mNativePointer);
535 break;
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700536 case PAUSED:
537 nativeOnPaused(mNativePointer);
538 break;
Andrei Popescu50c86232009-09-30 16:51:25 +0100539 case POSTER_FETCHED:
540 Bitmap poster = (Bitmap) msg.obj;
541 nativeOnPosterFetched(poster, mNativePointer);
542 break;
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000543 case TIMEUPDATE:
544 nativeOnTimeupdate(msg.arg1, mNativePointer);
545 break;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100546 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100547 }
548 };
Andrei Popescu6fa29582009-06-19 14:54:09 +0100549 }
550
Andrei Popescu64b86a12009-09-15 20:34:18 +0100551 private void doSetPoster(Bitmap poster) {
552 if (poster == null) {
553 return;
554 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100555 // Save a ref to the bitmap and send it over to the WebCore thread.
556 mPoster = poster;
557 Message msg = Message.obtain(mWebCoreHandler, POSTER_FETCHED);
558 msg.obj = poster;
559 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100560 }
561
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000562 private void sendTimeupdate() {
563 Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE);
564 msg.arg1 = VideoPlayer.getCurrentPosition();
565 mWebCoreHandler.sendMessage(msg);
566 }
567
Andrei Popescu64b86a12009-09-15 20:34:18 +0100568 public Context getContext() {
569 return mWebView.getContext();
570 }
571
572 // The public methods below are all called from WebKit only.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100573 /**
574 * Play a video stream.
575 * @param url is the URL of the video stream.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100576 */
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800577 public void play(String url, int position, int videoLayerID) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100578 if (url == null) {
579 return;
580 }
Ben Murdochff19d192011-01-17 18:08:56 +0000581
582 if (position > 0) {
583 seek(position);
584 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100585 Message message = obtainMessage(PLAY);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800586 message.arg1 = videoLayerID;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100587 message.obj = url;
Andrei Popescu6fa29582009-06-19 14:54:09 +0100588 sendMessage(message);
589 }
590
Andrei Popescu64b86a12009-09-15 20:34:18 +0100591 /**
Andrei Popescu290c34a2009-09-17 15:55:24 +0100592 * Seek into the video stream.
593 * @param time is the position in the video stream.
594 */
595 public void seek(int time) {
596 Message message = obtainMessage(SEEK);
597 message.obj = new Integer(time);
598 sendMessage(message);
599 }
600
601 /**
602 * Pause the playback.
603 */
604 public void pause() {
605 Message message = obtainMessage(PAUSE);
606 sendMessage(message);
607 }
608
609 /**
Andrei Popescu50c86232009-09-30 16:51:25 +0100610 * Tear down this proxy object.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100611 */
Andrei Popescu50c86232009-09-30 16:51:25 +0100612 public void teardown() {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100613 // This is called by the C++ MediaPlayerPrivate dtor.
614 // Cancel any active poster download.
615 if (mPosterDownloader != null) {
616 mPosterDownloader.cancelAndReleaseQueue();
617 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100618 mNativePointer = 0;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100619 }
620
621 /**
622 * Load the poster image.
623 * @param url is the URL of the poster image.
624 */
625 public void loadPoster(String url) {
626 if (url == null) {
Andrei Popescu50c86232009-09-30 16:51:25 +0100627 Message message = obtainMessage(LOAD_DEFAULT_POSTER);
628 sendMessage(message);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100629 return;
630 }
631 // Cancel any active poster download.
632 if (mPosterDownloader != null) {
633 mPosterDownloader.cancelAndReleaseQueue();
634 }
635 // Load the poster asynchronously
636 mPosterDownloader = new PosterDownloader(url, this);
637 mPosterDownloader.start();
Patrick Scott0a5ce012009-07-02 08:56:10 -0400638 }
639
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700640 // These three function are called from UI thread only by WebView.
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800641 public void setBaseLayer(int layer) {
642 VideoPlayer.setBaseLayer(layer);
643 }
644
645 public void pauseAndDispatch() {
646 VideoPlayer.pauseAndDispatch();
647 }
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700648
649 public void enterFullScreenVideo(int layerId, String url) {
650 VideoPlayer.enterFullScreenVideo(layerId, url, this, mWebView);
651 }
652
Andrei Popescu6fa29582009-06-19 14:54:09 +0100653 /**
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100654 * The factory for HTML5VideoViewProxy instances.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100655 * @param webViewCore is the WebViewCore that is requesting the proxy.
656 *
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100657 * @return a new HTML5VideoViewProxy object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100658 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100659 public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) {
660 return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100661 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100662
Ben Murdoch1708ad52010-11-11 15:56:16 +0000663 /* package */ WebView getWebView() {
664 return mWebView;
665 }
666
Andrei Popescu290c34a2009-09-17 15:55:24 +0100667 private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
668 private native void nativeOnEnded(int nativePointer);
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700669 private native void nativeOnPaused(int nativePointer);
Andrei Popescu50c86232009-09-30 16:51:25 +0100670 private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000671 private native void nativeOnTimeupdate(int position, int nativePointer);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800672 private native static boolean nativeSendSurfaceTexture(SurfaceTexture texture,
673 int baseLayer, int videoLayerId, int textureName,
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700674 int playerState);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100675}