blob: d1b8cfc199a2936e8ef70be430af5ba4f4fd32cb [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,
Teng-Hui Zhuc0fccd12011-03-29 10:35:11 -070049 MediaPlayer.OnInfoListener,
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -080050 SurfaceTexture.OnFrameAvailableListener {
Andrei Popescu6fa29582009-06-19 14:54:09 +010051 // Logging tag.
52 private static final String LOGTAG = "HTML5VideoViewProxy";
53
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010054 // Message Ids for WebCore thread -> UI thread communication.
Andrei Popescu50c86232009-09-30 16:51:25 +010055 private static final int PLAY = 100;
56 private static final int SEEK = 101;
57 private static final int PAUSE = 102;
58 private static final int ERROR = 103;
59 private static final int LOAD_DEFAULT_POSTER = 104;
Teng-Hui Zhuc0fccd12011-03-29 10:35:11 -070060 private static final int BUFFERING_START = 105;
61 private static final int BUFFERING_END = 106;
Andrei Popescu6fa29582009-06-19 14:54:09 +010062
Andrei Popescu64b86a12009-09-15 20:34:18 +010063 // Message Ids to be handled on the WebCore thread
Andrei Popescu290c34a2009-09-17 15:55:24 +010064 private static final int PREPARED = 200;
65 private static final int ENDED = 201;
Andrei Popescu50c86232009-09-30 16:51:25 +010066 private static final int POSTER_FETCHED = 202;
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -070067 private static final int PAUSED = 203;
Andrei Popescu64b86a12009-09-15 20:34:18 +010068
Andrei Popescu048eb3b2010-01-11 21:12:54 +000069 // Timer thread -> UI thread
70 private static final int TIMEUPDATE = 300;
71
Andrei Popescu290c34a2009-09-17 15:55:24 +010072 // The C++ MediaPlayerPrivateAndroid object.
73 int mNativePointer;
Andrei Popescu64b86a12009-09-15 20:34:18 +010074 // The handler for WebCore thread messages;
75 private Handler mWebCoreHandler;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010076 // The WebView instance that created this view.
77 private WebView mWebView;
Andrei Popescu64b86a12009-09-15 20:34:18 +010078 // The poster image to be shown when the video is not playing.
Andrei Popescu50c86232009-09-30 16:51:25 +010079 // This ref prevents the bitmap from being GC'ed.
80 private Bitmap mPoster;
Andrei Popescu64b86a12009-09-15 20:34:18 +010081 // The poster downloader.
82 private PosterDownloader mPosterDownloader;
Andrei Popescu290c34a2009-09-17 15:55:24 +010083 // The seek position.
84 private int mSeekPosition;
Andrei Popescu64b86a12009-09-15 20:34:18 +010085 // A helper class to control the playback. This executes on the UI thread!
86 private static final class VideoPlayer {
87 // The proxy that is currently playing (if any).
88 private static HTML5VideoViewProxy mCurrentProxy;
89 // The VideoView instance. This is a singleton for now, at least until
90 // http://b/issue?id=1973663 is fixed.
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -080091 private static HTML5VideoView mHTML5VideoView;
Andrei Popescu048eb3b2010-01-11 21:12:54 +000092
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -080093 private static boolean isVideoSelfEnded = false;
94 // By using the baseLayer and the current video Layer ID, we can
95 // identify the exact layer on the UI thread to use the SurfaceTexture.
96 private static int mBaseLayer = 0;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010097
Teng-Hui Zhuf4d4e9e2011-03-30 14:39:56 -070098 private static void setPlayerBuffering(boolean playerBuffering) {
99 mHTML5VideoView.setPlayerBuffering(playerBuffering);
100 }
101
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800102 // Every time webView setBaseLayer, this will be called.
103 // When we found the Video layer, then we set the Surface Texture to it.
104 // Otherwise, we may want to delete the Surface Texture to save memory.
105 public static void setBaseLayer(int layer) {
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700106 // Don't do this for full screen mode.
107 if (mHTML5VideoView != null
108 && !mHTML5VideoView.isFullScreenMode()) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800109 mBaseLayer = layer;
110 SurfaceTexture surfTexture = mHTML5VideoView.getSurfaceTexture();
111 int textureName = mHTML5VideoView.getTextureName();
112
113 int currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
114 if (layer != 0 && surfTexture != null && currentVideoLayerId != -1) {
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700115 int playerState = mHTML5VideoView.getCurrentState();
Teng-Hui Zhuf4d4e9e2011-03-30 14:39:56 -0700116 if (mHTML5VideoView.getPlayerBuffering())
Teng-Hui Zhuc0fccd12011-03-29 10:35:11 -0700117 playerState = HTML5VideoView.STATE_NOTPREPARED;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800118 boolean foundInTree = nativeSendSurfaceTexture(surfTexture,
119 layer, currentVideoLayerId, textureName,
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700120 playerState);
Teng-Hui Zhu1e26d822011-03-23 16:54:20 -0700121 if (playerState >= HTML5VideoView.STATE_PREPARED
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700122 && !foundInTree) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800123 mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
124 mHTML5VideoView.deleteSurfaceTexture();
Andrei Popescu31d2aa12010-04-08 15:19:22 +0100125 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100126 }
Andrei Popescua41f97b2010-01-11 18:36:25 +0000127 }
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800128 }
Andrei Popescua41f97b2010-01-11 18:36:25 +0000129
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800130 // When a WebView is paused, we also want to pause the video in it.
131 public static void pauseAndDispatch() {
132 if (mHTML5VideoView != null) {
133 mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
134 // When switching out, clean the video content on the old page
135 // by telling the layer not readyToUseSurfTex.
136 setBaseLayer(mBaseLayer);
137 }
138 }
139
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700140 public static void enterFullScreenVideo(int layerId, String url,
141 HTML5VideoViewProxy proxy, WebView webView) {
142 // Save the inline video info and inherit it in the full screen
143 int savePosition = 0;
144 boolean savedIsPlaying = false;
145 if (mHTML5VideoView != null) {
146 // If we are playing the same video, then it is better to
147 // save the current position.
148 if (layerId == mHTML5VideoView.getVideoLayerId()) {
149 savePosition = mHTML5VideoView.getCurrentPosition();
150 savedIsPlaying = mHTML5VideoView.isPlaying();
151 }
152 mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
153 mHTML5VideoView.release();
154 }
155 mHTML5VideoView = new HTML5VideoFullScreen(proxy.getContext(),
156 layerId, savePosition, savedIsPlaying);
157 mCurrentProxy = proxy;
158
159 mHTML5VideoView.setVideoURI(url, mCurrentProxy);
160
161 mHTML5VideoView.enterFullScreenVideoState(layerId, proxy, webView);
162 }
163
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800164 // This is on the UI thread.
165 // When native tell Java to play, we need to check whether or not it is
166 // still the same video by using videoLayerId and treat it differently.
167 public static void play(String url, int time, HTML5VideoViewProxy proxy,
168 WebChromeClient client, int videoLayerId) {
169 int currentVideoLayerId = -1;
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700170 boolean backFromFullScreenMode = false;
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700171 if (mHTML5VideoView != null) {
172 currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
173 if (mHTML5VideoView instanceof HTML5VideoFullScreen) {
174 backFromFullScreenMode = true;
175 }
176 }
177
178 if (backFromFullScreenMode
179 || currentVideoLayerId != videoLayerId
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800180 || mHTML5VideoView.getSurfaceTexture() == null) {
181 // Here, we handle the case when switching to a new video,
182 // either inside a WebView or across WebViews
183 // For switching videos within a WebView or across the WebView,
184 // we need to pause the old one and re-create a new media player
185 // inside the HTML5VideoView.
186 if (mHTML5VideoView != null) {
Teng-Hui Zhu22954d42011-04-07 17:13:18 -0700187 if (!backFromFullScreenMode) {
188 mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
189 }
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800190 // release the media player to avoid finalize error
191 mHTML5VideoView.release();
192 }
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800193 mCurrentProxy = proxy;
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700194 mHTML5VideoView = new HTML5VideoInline(videoLayerId, time, false);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800195
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700196 mHTML5VideoView.setVideoURI(url, mCurrentProxy);
197 mHTML5VideoView.prepareDataAndDisplayMode(proxy);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800198 } else if (mCurrentProxy == proxy) {
199 // Here, we handle the case when we keep playing with one video
200 if (!mHTML5VideoView.isPlaying()) {
201 mHTML5VideoView.seekTo(time);
202 mHTML5VideoView.start();
203 }
204 } else if (mCurrentProxy != null) {
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700205 // Some other video is already playing. Notify the caller that
206 // its playback ended.
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100207 proxy.dispatchOnEnded();
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100208 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100209 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100210
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000211 public static boolean isPlaying(HTML5VideoViewProxy proxy) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800212 return (mCurrentProxy == proxy && mHTML5VideoView != null
213 && mHTML5VideoView.isPlaying());
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000214 }
215
216 public static int getCurrentPosition() {
217 int currentPosMs = 0;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800218 if (mHTML5VideoView != null) {
219 currentPosMs = mHTML5VideoView.getCurrentPosition();
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000220 }
221 return currentPosMs;
222 }
223
Andrei Popescu290c34a2009-09-17 15:55:24 +0100224 public static void seek(int time, HTML5VideoViewProxy proxy) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800225 if (mCurrentProxy == proxy && time >= 0 && mHTML5VideoView != null) {
226 mHTML5VideoView.seekTo(time);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100227 }
228 }
229
230 public static void pause(HTML5VideoViewProxy proxy) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800231 if (mCurrentProxy == proxy && mHTML5VideoView != null) {
232 mHTML5VideoView.pause();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100233 }
234 }
Andrei Popescubf385d72009-09-18 18:59:52 +0100235
236 public static void onPrepared() {
Teng-Hui Zhu05049672011-04-06 18:09:27 -0700237 if (!mHTML5VideoView.isFullScreenMode() || mHTML5VideoView.getAutostart()) {
238 mHTML5VideoView.start();
239 }
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700240 if (mBaseLayer != 0) {
241 setBaseLayer(mBaseLayer);
242 }
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800243 }
244
245 public static void end() {
246 if (mCurrentProxy != null) {
247 if (isVideoSelfEnded)
248 mCurrentProxy.dispatchOnEnded();
249 else
250 mCurrentProxy.dispatchOnPaused();
251 }
252 isVideoSelfEnded = false;
Andrei Popescubf385d72009-09-18 18:59:52 +0100253 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100254 }
255
256 // A bunch event listeners for our VideoView
257 // MediaPlayer.OnPreparedListener
258 public void onPrepared(MediaPlayer mp) {
Andrei Popescubf385d72009-09-18 18:59:52 +0100259 VideoPlayer.onPrepared();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100260 Message msg = Message.obtain(mWebCoreHandler, PREPARED);
261 Map<String, Object> map = new HashMap<String, Object>();
262 map.put("dur", new Integer(mp.getDuration()));
263 map.put("width", new Integer(mp.getVideoWidth()));
264 map.put("height", new Integer(mp.getVideoHeight()));
265 msg.obj = map;
266 mWebCoreHandler.sendMessage(msg);
267 }
268
269 // MediaPlayer.OnCompletionListener;
270 public void onCompletion(MediaPlayer mp) {
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100271 // The video ended by itself, so we need to
272 // send a message to the UI thread to dismiss
273 // the video view and to return to the WebView.
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700274 // arg1 == 1 means the video ends by itself.
275 sendMessage(obtainMessage(ENDED, 1, 0));
Andrei Popescu290c34a2009-09-17 15:55:24 +0100276 }
277
Andrei Popescu50c86232009-09-30 16:51:25 +0100278 // MediaPlayer.OnErrorListener
279 public boolean onError(MediaPlayer mp, int what, int extra) {
280 sendMessage(obtainMessage(ERROR));
281 return false;
282 }
283
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100284 public void dispatchOnEnded() {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100285 Message msg = Message.obtain(mWebCoreHandler, ENDED);
286 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100287 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100288
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700289 public void dispatchOnPaused() {
290 Message msg = Message.obtain(mWebCoreHandler, PAUSED);
291 mWebCoreHandler.sendMessage(msg);
292 }
293
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000294 public void onTimeupdate() {
295 sendMessage(obtainMessage(TIMEUPDATE));
296 }
297
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800298 // When there is a frame ready from surface texture, we should tell WebView
299 // to refresh.
300 @Override
301 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
302 // TODO: This should support partial invalidation too.
303 mWebView.invalidate();
304 }
305
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000306 // Handler for the messages from WebCore or Timer thread to the UI thread.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100307 @Override
308 public void handleMessage(Message msg) {
309 // This executes on the UI thread.
310 switch (msg.what) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100311 case PLAY: {
312 String url = (String) msg.obj;
313 WebChromeClient client = mWebView.getWebChromeClient();
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800314 int videoLayerID = msg.arg1;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100315 if (client != null) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800316 VideoPlayer.play(url, mSeekPosition, this, client, videoLayerID);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100317 }
318 break;
319 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100320 case SEEK: {
321 Integer time = (Integer) msg.obj;
322 mSeekPosition = time;
323 VideoPlayer.seek(mSeekPosition, this);
324 break;
325 }
326 case PAUSE: {
327 VideoPlayer.pause(this);
328 break;
329 }
Andrei Popescu46a83b42009-10-23 13:49:46 +0100330 case ENDED:
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700331 if (msg.arg1 == 1)
332 VideoPlayer.isVideoSelfEnded = true;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800333 VideoPlayer.end();
334 break;
Andrei Popescu50c86232009-09-30 16:51:25 +0100335 case ERROR: {
336 WebChromeClient client = mWebView.getWebChromeClient();
337 if (client != null) {
338 client.onHideCustomView();
339 }
340 break;
341 }
342 case LOAD_DEFAULT_POSTER: {
343 WebChromeClient client = mWebView.getWebChromeClient();
344 if (client != null) {
345 doSetPoster(client.getDefaultVideoPoster());
346 }
347 break;
348 }
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000349 case TIMEUPDATE: {
350 if (VideoPlayer.isPlaying(this)) {
351 sendTimeupdate();
352 }
353 break;
354 }
Teng-Hui Zhuc0fccd12011-03-29 10:35:11 -0700355 case BUFFERING_START: {
Teng-Hui Zhuf4d4e9e2011-03-30 14:39:56 -0700356 VideoPlayer.setPlayerBuffering(true);
Teng-Hui Zhuc0fccd12011-03-29 10:35:11 -0700357 break;
358 }
359 case BUFFERING_END: {
Teng-Hui Zhuf4d4e9e2011-03-30 14:39:56 -0700360 VideoPlayer.setPlayerBuffering(false);
Teng-Hui Zhuc0fccd12011-03-29 10:35:11 -0700361 break;
362 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100363 }
364 }
365
Andrei Popescu64b86a12009-09-15 20:34:18 +0100366 // Everything below this comment executes on the WebCore thread, except for
367 // the EventHandler methods, which are called on the network thread.
368
369 // A helper class that knows how to download posters
370 private static final class PosterDownloader implements EventHandler {
371 // The request queue. This is static as we have one queue for all posters.
372 private static RequestQueue mRequestQueue;
373 private static int mQueueRefCount = 0;
374 // The poster URL
Ben Murdoch42509792011-02-18 12:34:35 +0000375 private URL mUrl;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100376 // The proxy we're doing this for.
377 private final HTML5VideoViewProxy mProxy;
378 // The poster bytes. We only touch this on the network thread.
379 private ByteArrayOutputStream mPosterBytes;
380 // The request handle. We only touch this on the WebCore thread.
381 private RequestHandle mRequestHandle;
382 // The response status code.
383 private int mStatusCode;
384 // The response headers.
385 private Headers mHeaders;
386 // The handler to handle messages on the WebCore thread.
387 private Handler mHandler;
388
389 public PosterDownloader(String url, HTML5VideoViewProxy proxy) {
Ben Murdoch42509792011-02-18 12:34:35 +0000390 try {
391 mUrl = new URL(url);
392 } catch (MalformedURLException e) {
393 mUrl = null;
394 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100395 mProxy = proxy;
396 mHandler = new Handler();
397 }
398 // Start the download. Called on WebCore thread.
399 public void start() {
400 retainQueue();
Ben Murdoch42509792011-02-18 12:34:35 +0000401
402 if (mUrl == null) {
403 return;
404 }
405
406 // Only support downloading posters over http/https.
407 // FIXME: Add support for other schemes. WebKit seems able to load
408 // posters over other schemes e.g. file://, but gets the dimensions wrong.
409 String protocol = mUrl.getProtocol();
410 if ("http".equals(protocol) || "https".equals(protocol)) {
411 mRequestHandle = mRequestQueue.queueRequest(mUrl.toString(), "GET", null,
412 this, null, 0);
413 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100414 }
415 // Cancel the download if active and release the queue. Called on WebCore thread.
416 public void cancelAndReleaseQueue() {
417 if (mRequestHandle != null) {
418 mRequestHandle.cancel();
419 mRequestHandle = null;
420 }
421 releaseQueue();
422 }
423 // EventHandler methods. Executed on the network thread.
424 public void status(int major_version,
425 int minor_version,
426 int code,
427 String reason_phrase) {
428 mStatusCode = code;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100429 }
430
Andrei Popescu64b86a12009-09-15 20:34:18 +0100431 public void headers(Headers headers) {
432 mHeaders = headers;
433 }
434
435 public void data(byte[] data, int len) {
436 if (mPosterBytes == null) {
437 mPosterBytes = new ByteArrayOutputStream();
438 }
439 mPosterBytes.write(data, 0, len);
440 }
441
442 public void endData() {
443 if (mStatusCode == 200) {
444 if (mPosterBytes.size() > 0) {
445 Bitmap poster = BitmapFactory.decodeByteArray(
446 mPosterBytes.toByteArray(), 0, mPosterBytes.size());
Andrei Popescu50c86232009-09-30 16:51:25 +0100447 mProxy.doSetPoster(poster);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100448 }
449 cleanup();
450 } else if (mStatusCode >= 300 && mStatusCode < 400) {
451 // We have a redirect.
Ben Murdoch42509792011-02-18 12:34:35 +0000452 try {
453 mUrl = new URL(mHeaders.getLocation());
454 } catch (MalformedURLException e) {
455 mUrl = null;
456 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100457 if (mUrl != null) {
458 mHandler.post(new Runnable() {
459 public void run() {
460 if (mRequestHandle != null) {
Ben Murdoch42509792011-02-18 12:34:35 +0000461 mRequestHandle.setupRedirect(mUrl.toString(), mStatusCode,
Andrei Popescu64b86a12009-09-15 20:34:18 +0100462 new HashMap<String, String>());
463 }
464 }
465 });
466 }
467 }
468 }
469
470 public void certificate(SslCertificate certificate) {
471 // Don't care.
472 }
473
474 public void error(int id, String description) {
475 cleanup();
476 }
477
478 public boolean handleSslErrorRequest(SslError error) {
479 // Don't care. If this happens, data() will never be called so
480 // mPosterBytes will never be created, so no need to call cleanup.
481 return false;
482 }
483 // Tears down the poster bytes stream. Called on network thread.
484 private void cleanup() {
485 if (mPosterBytes != null) {
486 try {
487 mPosterBytes.close();
488 } catch (IOException ignored) {
489 // Ignored.
490 } finally {
491 mPosterBytes = null;
492 }
493 }
494 }
495
496 // Queue management methods. Called on WebCore thread.
497 private void retainQueue() {
498 if (mRequestQueue == null) {
499 mRequestQueue = new RequestQueue(mProxy.getContext());
500 }
501 mQueueRefCount++;
502 }
503
504 private void releaseQueue() {
505 if (mQueueRefCount == 0) {
506 return;
507 }
508 if (--mQueueRefCount == 0) {
509 mRequestQueue.shutdown();
510 mRequestQueue = null;
511 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100512 }
513 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100514
515 /**
516 * Private constructor.
Andrei Popescu290c34a2009-09-17 15:55:24 +0100517 * @param webView is the WebView that hosts the video.
518 * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100519 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100520 private HTML5VideoViewProxy(WebView webView, int nativePtr) {
Andrei Popescu6fa29582009-06-19 14:54:09 +0100521 // This handler is for the main (UI) thread.
522 super(Looper.getMainLooper());
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100523 // Save the WebView object.
524 mWebView = webView;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800525 // Pass Proxy into webview, such that every time we have a setBaseLayer
526 // call, we tell this Proxy to call the native to update the layer tree
527 // for the Video Layer's surface texture info
528 mWebView.setHTML5VideoViewProxy(this);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100529 // Save the native ptr
530 mNativePointer = nativePtr;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100531 // create the message handler for this thread
532 createWebCoreHandler();
Andrei Popescu6fa29582009-06-19 14:54:09 +0100533 }
534
Andrei Popescu64b86a12009-09-15 20:34:18 +0100535 private void createWebCoreHandler() {
536 mWebCoreHandler = new Handler() {
537 @Override
538 public void handleMessage(Message msg) {
539 switch (msg.what) {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100540 case PREPARED: {
541 Map<String, Object> map = (Map<String, Object>) msg.obj;
542 Integer duration = (Integer) map.get("dur");
543 Integer width = (Integer) map.get("width");
544 Integer height = (Integer) map.get("height");
545 nativeOnPrepared(duration.intValue(), width.intValue(),
546 height.intValue(), mNativePointer);
547 break;
548 }
549 case ENDED:
Ben Murdochff19d192011-01-17 18:08:56 +0000550 mSeekPosition = 0;
Andrei Popescu290c34a2009-09-17 15:55:24 +0100551 nativeOnEnded(mNativePointer);
552 break;
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700553 case PAUSED:
554 nativeOnPaused(mNativePointer);
555 break;
Andrei Popescu50c86232009-09-30 16:51:25 +0100556 case POSTER_FETCHED:
557 Bitmap poster = (Bitmap) msg.obj;
558 nativeOnPosterFetched(poster, mNativePointer);
559 break;
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000560 case TIMEUPDATE:
561 nativeOnTimeupdate(msg.arg1, mNativePointer);
562 break;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100563 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100564 }
565 };
Andrei Popescu6fa29582009-06-19 14:54:09 +0100566 }
567
Andrei Popescu64b86a12009-09-15 20:34:18 +0100568 private void doSetPoster(Bitmap poster) {
569 if (poster == null) {
570 return;
571 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100572 // Save a ref to the bitmap and send it over to the WebCore thread.
573 mPoster = poster;
574 Message msg = Message.obtain(mWebCoreHandler, POSTER_FETCHED);
575 msg.obj = poster;
576 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100577 }
578
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000579 private void sendTimeupdate() {
580 Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE);
581 msg.arg1 = VideoPlayer.getCurrentPosition();
582 mWebCoreHandler.sendMessage(msg);
583 }
584
Andrei Popescu64b86a12009-09-15 20:34:18 +0100585 public Context getContext() {
586 return mWebView.getContext();
587 }
588
589 // The public methods below are all called from WebKit only.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100590 /**
591 * Play a video stream.
592 * @param url is the URL of the video stream.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100593 */
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800594 public void play(String url, int position, int videoLayerID) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100595 if (url == null) {
596 return;
597 }
Ben Murdochff19d192011-01-17 18:08:56 +0000598
599 if (position > 0) {
600 seek(position);
601 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100602 Message message = obtainMessage(PLAY);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800603 message.arg1 = videoLayerID;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100604 message.obj = url;
Andrei Popescu6fa29582009-06-19 14:54:09 +0100605 sendMessage(message);
606 }
607
Andrei Popescu64b86a12009-09-15 20:34:18 +0100608 /**
Andrei Popescu290c34a2009-09-17 15:55:24 +0100609 * Seek into the video stream.
610 * @param time is the position in the video stream.
611 */
612 public void seek(int time) {
613 Message message = obtainMessage(SEEK);
614 message.obj = new Integer(time);
615 sendMessage(message);
616 }
617
618 /**
619 * Pause the playback.
620 */
621 public void pause() {
622 Message message = obtainMessage(PAUSE);
623 sendMessage(message);
624 }
625
626 /**
Andrei Popescu50c86232009-09-30 16:51:25 +0100627 * Tear down this proxy object.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100628 */
Andrei Popescu50c86232009-09-30 16:51:25 +0100629 public void teardown() {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100630 // This is called by the C++ MediaPlayerPrivate dtor.
631 // Cancel any active poster download.
632 if (mPosterDownloader != null) {
633 mPosterDownloader.cancelAndReleaseQueue();
634 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100635 mNativePointer = 0;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100636 }
637
638 /**
639 * Load the poster image.
640 * @param url is the URL of the poster image.
641 */
642 public void loadPoster(String url) {
643 if (url == null) {
Andrei Popescu50c86232009-09-30 16:51:25 +0100644 Message message = obtainMessage(LOAD_DEFAULT_POSTER);
645 sendMessage(message);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100646 return;
647 }
648 // Cancel any active poster download.
649 if (mPosterDownloader != null) {
650 mPosterDownloader.cancelAndReleaseQueue();
651 }
652 // Load the poster asynchronously
653 mPosterDownloader = new PosterDownloader(url, this);
654 mPosterDownloader.start();
Patrick Scott0a5ce012009-07-02 08:56:10 -0400655 }
656
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700657 // These three function are called from UI thread only by WebView.
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800658 public void setBaseLayer(int layer) {
659 VideoPlayer.setBaseLayer(layer);
660 }
661
662 public void pauseAndDispatch() {
663 VideoPlayer.pauseAndDispatch();
664 }
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700665
666 public void enterFullScreenVideo(int layerId, String url) {
667 VideoPlayer.enterFullScreenVideo(layerId, url, this, mWebView);
668 }
669
Andrei Popescu6fa29582009-06-19 14:54:09 +0100670 /**
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100671 * The factory for HTML5VideoViewProxy instances.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100672 * @param webViewCore is the WebViewCore that is requesting the proxy.
673 *
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100674 * @return a new HTML5VideoViewProxy object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100675 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100676 public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) {
677 return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100678 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100679
Ben Murdoch1708ad52010-11-11 15:56:16 +0000680 /* package */ WebView getWebView() {
681 return mWebView;
682 }
683
Andrei Popescu290c34a2009-09-17 15:55:24 +0100684 private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
685 private native void nativeOnEnded(int nativePointer);
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700686 private native void nativeOnPaused(int nativePointer);
Andrei Popescu50c86232009-09-30 16:51:25 +0100687 private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000688 private native void nativeOnTimeupdate(int position, int nativePointer);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800689 private native static boolean nativeSendSurfaceTexture(SurfaceTexture texture,
690 int baseLayer, int videoLayerId, int textureName,
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700691 int playerState);
Teng-Hui Zhuc0fccd12011-03-29 10:35:11 -0700692
693 @Override
694 public boolean onInfo(MediaPlayer mp, int what, int extra) {
695 if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) {
696 sendMessage(obtainMessage(BUFFERING_START, what, extra));
697 } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
698 sendMessage(obtainMessage(BUFFERING_END, what, extra));
699 }
700 return false;
701 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100702}