blob: aa254d9d43537521b51ed71ec1085f58adee8da7 [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;
Teng-Hui Zhub109c882011-05-04 16:19:49 -070068 private static final int STOPFULLSCREEN = 204;
Andrei Popescu64b86a12009-09-15 20:34:18 +010069
Andrei Popescu048eb3b2010-01-11 21:12:54 +000070 // Timer thread -> UI thread
71 private static final int TIMEUPDATE = 300;
72
Andrei Popescu290c34a2009-09-17 15:55:24 +010073 // The C++ MediaPlayerPrivateAndroid object.
74 int mNativePointer;
Andrei Popescu64b86a12009-09-15 20:34:18 +010075 // The handler for WebCore thread messages;
76 private Handler mWebCoreHandler;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010077 // The WebView instance that created this view.
78 private WebView mWebView;
Andrei Popescu64b86a12009-09-15 20:34:18 +010079 // The poster image to be shown when the video is not playing.
Andrei Popescu50c86232009-09-30 16:51:25 +010080 // This ref prevents the bitmap from being GC'ed.
81 private Bitmap mPoster;
Andrei Popescu64b86a12009-09-15 20:34:18 +010082 // The poster downloader.
83 private PosterDownloader mPosterDownloader;
Andrei Popescu290c34a2009-09-17 15:55:24 +010084 // The seek position.
85 private int mSeekPosition;
Andrei Popescu64b86a12009-09-15 20:34:18 +010086 // A helper class to control the playback. This executes on the UI thread!
87 private static final class VideoPlayer {
88 // The proxy that is currently playing (if any).
89 private static HTML5VideoViewProxy mCurrentProxy;
90 // The VideoView instance. This is a singleton for now, at least until
91 // http://b/issue?id=1973663 is fixed.
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -080092 private static HTML5VideoView mHTML5VideoView;
Andrei Popescu048eb3b2010-01-11 21:12:54 +000093
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -080094 private static boolean isVideoSelfEnded = false;
95 // By using the baseLayer and the current video Layer ID, we can
96 // identify the exact layer on the UI thread to use the SurfaceTexture.
97 private static int mBaseLayer = 0;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010098
Teng-Hui Zhuf4d4e9e2011-03-30 14:39:56 -070099 private static void setPlayerBuffering(boolean playerBuffering) {
100 mHTML5VideoView.setPlayerBuffering(playerBuffering);
101 }
102
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800103 // Every time webView setBaseLayer, this will be called.
104 // When we found the Video layer, then we set the Surface Texture to it.
105 // Otherwise, we may want to delete the Surface Texture to save memory.
106 public static void setBaseLayer(int layer) {
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700107 // Don't do this for full screen mode.
108 if (mHTML5VideoView != null
Teng-Hui Zhu3fafd392011-05-31 15:15:31 -0700109 && !mHTML5VideoView.isFullScreenMode()
110 && !mHTML5VideoView.surfaceTextureDeleted()) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800111 mBaseLayer = layer;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800112
113 int currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
Teng-Hui Zhu3fafd392011-05-31 15:15:31 -0700114 SurfaceTexture surfTexture = mHTML5VideoView.getSurfaceTexture(currentVideoLayerId);
115 int textureName = mHTML5VideoView.getTextureName();
116
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800117 if (layer != 0 && surfTexture != null && currentVideoLayerId != -1) {
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700118 int playerState = mHTML5VideoView.getCurrentState();
Teng-Hui Zhuf4d4e9e2011-03-30 14:39:56 -0700119 if (mHTML5VideoView.getPlayerBuffering())
Teng-Hui Zhuc0fccd12011-03-29 10:35:11 -0700120 playerState = HTML5VideoView.STATE_NOTPREPARED;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800121 boolean foundInTree = nativeSendSurfaceTexture(surfTexture,
122 layer, currentVideoLayerId, textureName,
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700123 playerState);
Teng-Hui Zhu1e26d822011-03-23 16:54:20 -0700124 if (playerState >= HTML5VideoView.STATE_PREPARED
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700125 && !foundInTree) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800126 mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
127 mHTML5VideoView.deleteSurfaceTexture();
Andrei Popescu31d2aa12010-04-08 15:19:22 +0100128 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100129 }
Andrei Popescua41f97b2010-01-11 18:36:25 +0000130 }
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800131 }
Andrei Popescua41f97b2010-01-11 18:36:25 +0000132
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800133 // When a WebView is paused, we also want to pause the video in it.
134 public static void pauseAndDispatch() {
135 if (mHTML5VideoView != null) {
136 mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
137 // When switching out, clean the video content on the old page
138 // by telling the layer not readyToUseSurfTex.
139 setBaseLayer(mBaseLayer);
140 }
141 }
142
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700143 public static void enterFullScreenVideo(int layerId, String url,
144 HTML5VideoViewProxy proxy, WebView webView) {
145 // Save the inline video info and inherit it in the full screen
146 int savePosition = 0;
147 boolean savedIsPlaying = false;
148 if (mHTML5VideoView != null) {
149 // If we are playing the same video, then it is better to
150 // save the current position.
151 if (layerId == mHTML5VideoView.getVideoLayerId()) {
152 savePosition = mHTML5VideoView.getCurrentPosition();
153 savedIsPlaying = mHTML5VideoView.isPlaying();
154 }
155 mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
156 mHTML5VideoView.release();
157 }
158 mHTML5VideoView = new HTML5VideoFullScreen(proxy.getContext(),
159 layerId, savePosition, savedIsPlaying);
160 mCurrentProxy = proxy;
161
162 mHTML5VideoView.setVideoURI(url, mCurrentProxy);
163
164 mHTML5VideoView.enterFullScreenVideoState(layerId, proxy, webView);
165 }
166
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800167 // This is on the UI thread.
168 // When native tell Java to play, we need to check whether or not it is
169 // still the same video by using videoLayerId and treat it differently.
170 public static void play(String url, int time, HTML5VideoViewProxy proxy,
171 WebChromeClient client, int videoLayerId) {
172 int currentVideoLayerId = -1;
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700173 boolean backFromFullScreenMode = false;
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700174 if (mHTML5VideoView != null) {
175 currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
176 if (mHTML5VideoView instanceof HTML5VideoFullScreen) {
177 backFromFullScreenMode = true;
178 }
179 }
180
181 if (backFromFullScreenMode
Teng-Hui Zhu3fafd392011-05-31 15:15:31 -0700182 || currentVideoLayerId != videoLayerId
183 || mHTML5VideoView.surfaceTextureDeleted()) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800184 // Here, we handle the case when switching to a new video,
185 // either inside a WebView or across WebViews
186 // For switching videos within a WebView or across the WebView,
187 // we need to pause the old one and re-create a new media player
188 // inside the HTML5VideoView.
189 if (mHTML5VideoView != null) {
Teng-Hui Zhu22954d42011-04-07 17:13:18 -0700190 if (!backFromFullScreenMode) {
191 mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
192 }
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800193 // release the media player to avoid finalize error
194 mHTML5VideoView.release();
195 }
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800196 mCurrentProxy = proxy;
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700197 mHTML5VideoView = new HTML5VideoInline(videoLayerId, time, false);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800198
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700199 mHTML5VideoView.setVideoURI(url, mCurrentProxy);
200 mHTML5VideoView.prepareDataAndDisplayMode(proxy);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800201 } else if (mCurrentProxy == proxy) {
202 // Here, we handle the case when we keep playing with one video
203 if (!mHTML5VideoView.isPlaying()) {
204 mHTML5VideoView.seekTo(time);
205 mHTML5VideoView.start();
206 }
207 } else if (mCurrentProxy != null) {
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700208 // Some other video is already playing. Notify the caller that
209 // its playback ended.
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100210 proxy.dispatchOnEnded();
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100211 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100212 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100213
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000214 public static boolean isPlaying(HTML5VideoViewProxy proxy) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800215 return (mCurrentProxy == proxy && mHTML5VideoView != null
216 && mHTML5VideoView.isPlaying());
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000217 }
218
219 public static int getCurrentPosition() {
220 int currentPosMs = 0;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800221 if (mHTML5VideoView != null) {
222 currentPosMs = mHTML5VideoView.getCurrentPosition();
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000223 }
224 return currentPosMs;
225 }
226
Andrei Popescu290c34a2009-09-17 15:55:24 +0100227 public static void seek(int time, HTML5VideoViewProxy proxy) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800228 if (mCurrentProxy == proxy && time >= 0 && mHTML5VideoView != null) {
229 mHTML5VideoView.seekTo(time);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100230 }
231 }
232
233 public static void pause(HTML5VideoViewProxy proxy) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800234 if (mCurrentProxy == proxy && mHTML5VideoView != null) {
235 mHTML5VideoView.pause();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100236 }
237 }
Andrei Popescubf385d72009-09-18 18:59:52 +0100238
239 public static void onPrepared() {
Teng-Hui Zhu05049672011-04-06 18:09:27 -0700240 if (!mHTML5VideoView.isFullScreenMode() || mHTML5VideoView.getAutostart()) {
241 mHTML5VideoView.start();
242 }
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700243 if (mBaseLayer != 0) {
244 setBaseLayer(mBaseLayer);
245 }
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800246 }
247
248 public static void end() {
249 if (mCurrentProxy != null) {
250 if (isVideoSelfEnded)
251 mCurrentProxy.dispatchOnEnded();
252 else
253 mCurrentProxy.dispatchOnPaused();
254 }
255 isVideoSelfEnded = false;
Andrei Popescubf385d72009-09-18 18:59:52 +0100256 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100257 }
258
259 // A bunch event listeners for our VideoView
260 // MediaPlayer.OnPreparedListener
261 public void onPrepared(MediaPlayer mp) {
Andrei Popescubf385d72009-09-18 18:59:52 +0100262 VideoPlayer.onPrepared();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100263 Message msg = Message.obtain(mWebCoreHandler, PREPARED);
264 Map<String, Object> map = new HashMap<String, Object>();
265 map.put("dur", new Integer(mp.getDuration()));
266 map.put("width", new Integer(mp.getVideoWidth()));
267 map.put("height", new Integer(mp.getVideoHeight()));
268 msg.obj = map;
269 mWebCoreHandler.sendMessage(msg);
270 }
271
272 // MediaPlayer.OnCompletionListener;
273 public void onCompletion(MediaPlayer mp) {
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100274 // The video ended by itself, so we need to
275 // send a message to the UI thread to dismiss
276 // the video view and to return to the WebView.
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700277 // arg1 == 1 means the video ends by itself.
278 sendMessage(obtainMessage(ENDED, 1, 0));
Andrei Popescu290c34a2009-09-17 15:55:24 +0100279 }
280
Andrei Popescu50c86232009-09-30 16:51:25 +0100281 // MediaPlayer.OnErrorListener
282 public boolean onError(MediaPlayer mp, int what, int extra) {
283 sendMessage(obtainMessage(ERROR));
284 return false;
285 }
286
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100287 public void dispatchOnEnded() {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100288 Message msg = Message.obtain(mWebCoreHandler, ENDED);
289 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100290 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100291
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700292 public void dispatchOnPaused() {
Teng-Hui Zhub109c882011-05-04 16:19:49 -0700293 Message msg = Message.obtain(mWebCoreHandler, PAUSED);
294 mWebCoreHandler.sendMessage(msg);
295 }
296
297 public void dispatchOnStopFullScreen() {
298 Message msg = Message.obtain(mWebCoreHandler, STOPFULLSCREEN);
299 mWebCoreHandler.sendMessage(msg);
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700300 }
301
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000302 public void onTimeupdate() {
303 sendMessage(obtainMessage(TIMEUPDATE));
304 }
305
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800306 // When there is a frame ready from surface texture, we should tell WebView
307 // to refresh.
308 @Override
309 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
310 // TODO: This should support partial invalidation too.
311 mWebView.invalidate();
312 }
313
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000314 // Handler for the messages from WebCore or Timer thread to the UI thread.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100315 @Override
316 public void handleMessage(Message msg) {
317 // This executes on the UI thread.
318 switch (msg.what) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100319 case PLAY: {
320 String url = (String) msg.obj;
321 WebChromeClient client = mWebView.getWebChromeClient();
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800322 int videoLayerID = msg.arg1;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100323 if (client != null) {
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800324 VideoPlayer.play(url, mSeekPosition, this, client, videoLayerID);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100325 }
326 break;
327 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100328 case SEEK: {
329 Integer time = (Integer) msg.obj;
330 mSeekPosition = time;
331 VideoPlayer.seek(mSeekPosition, this);
332 break;
333 }
334 case PAUSE: {
335 VideoPlayer.pause(this);
336 break;
337 }
Andrei Popescu46a83b42009-10-23 13:49:46 +0100338 case ENDED:
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700339 if (msg.arg1 == 1)
340 VideoPlayer.isVideoSelfEnded = true;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800341 VideoPlayer.end();
342 break;
Andrei Popescu50c86232009-09-30 16:51:25 +0100343 case ERROR: {
344 WebChromeClient client = mWebView.getWebChromeClient();
345 if (client != null) {
346 client.onHideCustomView();
347 }
348 break;
349 }
350 case LOAD_DEFAULT_POSTER: {
351 WebChromeClient client = mWebView.getWebChromeClient();
352 if (client != null) {
353 doSetPoster(client.getDefaultVideoPoster());
354 }
355 break;
356 }
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000357 case TIMEUPDATE: {
358 if (VideoPlayer.isPlaying(this)) {
359 sendTimeupdate();
360 }
361 break;
362 }
Teng-Hui Zhuc0fccd12011-03-29 10:35:11 -0700363 case BUFFERING_START: {
Teng-Hui Zhuf4d4e9e2011-03-30 14:39:56 -0700364 VideoPlayer.setPlayerBuffering(true);
Teng-Hui Zhuc0fccd12011-03-29 10:35:11 -0700365 break;
366 }
367 case BUFFERING_END: {
Teng-Hui Zhuf4d4e9e2011-03-30 14:39:56 -0700368 VideoPlayer.setPlayerBuffering(false);
Teng-Hui Zhuc0fccd12011-03-29 10:35:11 -0700369 break;
370 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100371 }
372 }
373
Andrei Popescu64b86a12009-09-15 20:34:18 +0100374 // Everything below this comment executes on the WebCore thread, except for
375 // the EventHandler methods, which are called on the network thread.
376
377 // A helper class that knows how to download posters
378 private static final class PosterDownloader implements EventHandler {
379 // The request queue. This is static as we have one queue for all posters.
380 private static RequestQueue mRequestQueue;
381 private static int mQueueRefCount = 0;
382 // The poster URL
Ben Murdoch42509792011-02-18 12:34:35 +0000383 private URL mUrl;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100384 // The proxy we're doing this for.
385 private final HTML5VideoViewProxy mProxy;
386 // The poster bytes. We only touch this on the network thread.
387 private ByteArrayOutputStream mPosterBytes;
388 // The request handle. We only touch this on the WebCore thread.
389 private RequestHandle mRequestHandle;
390 // The response status code.
391 private int mStatusCode;
392 // The response headers.
393 private Headers mHeaders;
394 // The handler to handle messages on the WebCore thread.
395 private Handler mHandler;
396
397 public PosterDownloader(String url, HTML5VideoViewProxy proxy) {
Ben Murdoch42509792011-02-18 12:34:35 +0000398 try {
399 mUrl = new URL(url);
400 } catch (MalformedURLException e) {
401 mUrl = null;
402 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100403 mProxy = proxy;
404 mHandler = new Handler();
405 }
406 // Start the download. Called on WebCore thread.
407 public void start() {
408 retainQueue();
Ben Murdoch42509792011-02-18 12:34:35 +0000409
410 if (mUrl == null) {
411 return;
412 }
413
414 // Only support downloading posters over http/https.
415 // FIXME: Add support for other schemes. WebKit seems able to load
416 // posters over other schemes e.g. file://, but gets the dimensions wrong.
417 String protocol = mUrl.getProtocol();
418 if ("http".equals(protocol) || "https".equals(protocol)) {
419 mRequestHandle = mRequestQueue.queueRequest(mUrl.toString(), "GET", null,
420 this, null, 0);
421 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100422 }
423 // Cancel the download if active and release the queue. Called on WebCore thread.
424 public void cancelAndReleaseQueue() {
425 if (mRequestHandle != null) {
426 mRequestHandle.cancel();
427 mRequestHandle = null;
428 }
429 releaseQueue();
430 }
431 // EventHandler methods. Executed on the network thread.
432 public void status(int major_version,
433 int minor_version,
434 int code,
435 String reason_phrase) {
436 mStatusCode = code;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100437 }
438
Andrei Popescu64b86a12009-09-15 20:34:18 +0100439 public void headers(Headers headers) {
440 mHeaders = headers;
441 }
442
443 public void data(byte[] data, int len) {
444 if (mPosterBytes == null) {
445 mPosterBytes = new ByteArrayOutputStream();
446 }
447 mPosterBytes.write(data, 0, len);
448 }
449
450 public void endData() {
451 if (mStatusCode == 200) {
452 if (mPosterBytes.size() > 0) {
453 Bitmap poster = BitmapFactory.decodeByteArray(
454 mPosterBytes.toByteArray(), 0, mPosterBytes.size());
Andrei Popescu50c86232009-09-30 16:51:25 +0100455 mProxy.doSetPoster(poster);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100456 }
457 cleanup();
458 } else if (mStatusCode >= 300 && mStatusCode < 400) {
459 // We have a redirect.
Ben Murdoch42509792011-02-18 12:34:35 +0000460 try {
461 mUrl = new URL(mHeaders.getLocation());
462 } catch (MalformedURLException e) {
463 mUrl = null;
464 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100465 if (mUrl != null) {
466 mHandler.post(new Runnable() {
467 public void run() {
468 if (mRequestHandle != null) {
Ben Murdoch42509792011-02-18 12:34:35 +0000469 mRequestHandle.setupRedirect(mUrl.toString(), mStatusCode,
Andrei Popescu64b86a12009-09-15 20:34:18 +0100470 new HashMap<String, String>());
471 }
472 }
473 });
474 }
475 }
476 }
477
478 public void certificate(SslCertificate certificate) {
479 // Don't care.
480 }
481
482 public void error(int id, String description) {
483 cleanup();
484 }
485
486 public boolean handleSslErrorRequest(SslError error) {
487 // Don't care. If this happens, data() will never be called so
488 // mPosterBytes will never be created, so no need to call cleanup.
489 return false;
490 }
491 // Tears down the poster bytes stream. Called on network thread.
492 private void cleanup() {
493 if (mPosterBytes != null) {
494 try {
495 mPosterBytes.close();
496 } catch (IOException ignored) {
497 // Ignored.
498 } finally {
499 mPosterBytes = null;
500 }
501 }
502 }
503
504 // Queue management methods. Called on WebCore thread.
505 private void retainQueue() {
506 if (mRequestQueue == null) {
507 mRequestQueue = new RequestQueue(mProxy.getContext());
508 }
509 mQueueRefCount++;
510 }
511
512 private void releaseQueue() {
513 if (mQueueRefCount == 0) {
514 return;
515 }
516 if (--mQueueRefCount == 0) {
517 mRequestQueue.shutdown();
518 mRequestQueue = null;
519 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100520 }
521 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100522
523 /**
524 * Private constructor.
Andrei Popescu290c34a2009-09-17 15:55:24 +0100525 * @param webView is the WebView that hosts the video.
526 * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100527 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100528 private HTML5VideoViewProxy(WebView webView, int nativePtr) {
Andrei Popescu6fa29582009-06-19 14:54:09 +0100529 // This handler is for the main (UI) thread.
530 super(Looper.getMainLooper());
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100531 // Save the WebView object.
532 mWebView = webView;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800533 // Pass Proxy into webview, such that every time we have a setBaseLayer
534 // call, we tell this Proxy to call the native to update the layer tree
535 // for the Video Layer's surface texture info
536 mWebView.setHTML5VideoViewProxy(this);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100537 // Save the native ptr
538 mNativePointer = nativePtr;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100539 // create the message handler for this thread
540 createWebCoreHandler();
Andrei Popescu6fa29582009-06-19 14:54:09 +0100541 }
542
Andrei Popescu64b86a12009-09-15 20:34:18 +0100543 private void createWebCoreHandler() {
544 mWebCoreHandler = new Handler() {
545 @Override
546 public void handleMessage(Message msg) {
547 switch (msg.what) {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100548 case PREPARED: {
549 Map<String, Object> map = (Map<String, Object>) msg.obj;
550 Integer duration = (Integer) map.get("dur");
551 Integer width = (Integer) map.get("width");
552 Integer height = (Integer) map.get("height");
553 nativeOnPrepared(duration.intValue(), width.intValue(),
554 height.intValue(), mNativePointer);
555 break;
556 }
557 case ENDED:
Ben Murdochff19d192011-01-17 18:08:56 +0000558 mSeekPosition = 0;
Andrei Popescu290c34a2009-09-17 15:55:24 +0100559 nativeOnEnded(mNativePointer);
560 break;
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700561 case PAUSED:
562 nativeOnPaused(mNativePointer);
563 break;
Andrei Popescu50c86232009-09-30 16:51:25 +0100564 case POSTER_FETCHED:
565 Bitmap poster = (Bitmap) msg.obj;
566 nativeOnPosterFetched(poster, mNativePointer);
567 break;
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000568 case TIMEUPDATE:
569 nativeOnTimeupdate(msg.arg1, mNativePointer);
570 break;
Teng-Hui Zhub109c882011-05-04 16:19:49 -0700571 case STOPFULLSCREEN:
572 nativeOnStopFullscreen(mNativePointer);
573 break;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100574 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100575 }
576 };
Andrei Popescu6fa29582009-06-19 14:54:09 +0100577 }
578
Andrei Popescu64b86a12009-09-15 20:34:18 +0100579 private void doSetPoster(Bitmap poster) {
580 if (poster == null) {
581 return;
582 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100583 // Save a ref to the bitmap and send it over to the WebCore thread.
584 mPoster = poster;
585 Message msg = Message.obtain(mWebCoreHandler, POSTER_FETCHED);
586 msg.obj = poster;
587 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100588 }
589
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000590 private void sendTimeupdate() {
591 Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE);
592 msg.arg1 = VideoPlayer.getCurrentPosition();
593 mWebCoreHandler.sendMessage(msg);
594 }
595
Andrei Popescu64b86a12009-09-15 20:34:18 +0100596 public Context getContext() {
597 return mWebView.getContext();
598 }
599
600 // The public methods below are all called from WebKit only.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100601 /**
602 * Play a video stream.
603 * @param url is the URL of the video stream.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100604 */
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800605 public void play(String url, int position, int videoLayerID) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100606 if (url == null) {
607 return;
608 }
Ben Murdochff19d192011-01-17 18:08:56 +0000609
610 if (position > 0) {
611 seek(position);
612 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100613 Message message = obtainMessage(PLAY);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800614 message.arg1 = videoLayerID;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100615 message.obj = url;
Andrei Popescu6fa29582009-06-19 14:54:09 +0100616 sendMessage(message);
617 }
618
Andrei Popescu64b86a12009-09-15 20:34:18 +0100619 /**
Andrei Popescu290c34a2009-09-17 15:55:24 +0100620 * Seek into the video stream.
621 * @param time is the position in the video stream.
622 */
623 public void seek(int time) {
624 Message message = obtainMessage(SEEK);
625 message.obj = new Integer(time);
626 sendMessage(message);
627 }
628
629 /**
630 * Pause the playback.
631 */
632 public void pause() {
633 Message message = obtainMessage(PAUSE);
634 sendMessage(message);
635 }
636
637 /**
Andrei Popescu50c86232009-09-30 16:51:25 +0100638 * Tear down this proxy object.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100639 */
Andrei Popescu50c86232009-09-30 16:51:25 +0100640 public void teardown() {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100641 // This is called by the C++ MediaPlayerPrivate dtor.
642 // Cancel any active poster download.
643 if (mPosterDownloader != null) {
644 mPosterDownloader.cancelAndReleaseQueue();
645 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100646 mNativePointer = 0;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100647 }
648
649 /**
650 * Load the poster image.
651 * @param url is the URL of the poster image.
652 */
653 public void loadPoster(String url) {
654 if (url == null) {
Andrei Popescu50c86232009-09-30 16:51:25 +0100655 Message message = obtainMessage(LOAD_DEFAULT_POSTER);
656 sendMessage(message);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100657 return;
658 }
659 // Cancel any active poster download.
660 if (mPosterDownloader != null) {
661 mPosterDownloader.cancelAndReleaseQueue();
662 }
663 // Load the poster asynchronously
664 mPosterDownloader = new PosterDownloader(url, this);
665 mPosterDownloader.start();
Patrick Scott0a5ce012009-07-02 08:56:10 -0400666 }
667
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700668 // These three function are called from UI thread only by WebView.
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800669 public void setBaseLayer(int layer) {
670 VideoPlayer.setBaseLayer(layer);
671 }
672
673 public void pauseAndDispatch() {
674 VideoPlayer.pauseAndDispatch();
675 }
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700676
677 public void enterFullScreenVideo(int layerId, String url) {
678 VideoPlayer.enterFullScreenVideo(layerId, url, this, mWebView);
679 }
680
Andrei Popescu6fa29582009-06-19 14:54:09 +0100681 /**
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100682 * The factory for HTML5VideoViewProxy instances.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100683 * @param webViewCore is the WebViewCore that is requesting the proxy.
684 *
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100685 * @return a new HTML5VideoViewProxy object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100686 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100687 public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) {
688 return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100689 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100690
Ben Murdoch1708ad52010-11-11 15:56:16 +0000691 /* package */ WebView getWebView() {
692 return mWebView;
693 }
694
Andrei Popescu290c34a2009-09-17 15:55:24 +0100695 private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
696 private native void nativeOnEnded(int nativePointer);
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700697 private native void nativeOnPaused(int nativePointer);
Andrei Popescu50c86232009-09-30 16:51:25 +0100698 private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000699 private native void nativeOnTimeupdate(int position, int nativePointer);
Teng-Hui Zhub109c882011-05-04 16:19:49 -0700700 private native void nativeOnStopFullscreen(int nativePointer);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800701 private native static boolean nativeSendSurfaceTexture(SurfaceTexture texture,
702 int baseLayer, int videoLayerId, int textureName,
Teng-Hui Zhu265db322011-03-18 14:56:10 -0700703 int playerState);
Teng-Hui Zhuc0fccd12011-03-29 10:35:11 -0700704
705 @Override
706 public boolean onInfo(MediaPlayer mp, int what, int extra) {
707 if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) {
708 sendMessage(obtainMessage(BUFFERING_START, what, extra));
709 } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
710 sendMessage(obtainMessage(BUFFERING_END, what, extra));
711 }
712 return false;
713 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100714}