HTML5VideoViewProxy.java revision 96fae5e088916e1da8d4afaf40758ceec6e7187a
1/* 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; 20import android.graphics.Bitmap; 21import android.graphics.BitmapFactory; 22import android.graphics.SurfaceTexture; 23import android.media.MediaPlayer; 24import 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; 30import android.os.Handler; 31import android.os.Looper; 32import android.os.Message; 33import android.util.Log; 34 35import java.io.ByteArrayOutputStream; 36import java.io.IOException; 37import java.net.MalformedURLException; 38import java.net.URL; 39import java.util.HashMap; 40import java.util.Map; 41 42/** 43 * <p>Proxy for HTML5 video views. 44 */ 45class HTML5VideoViewProxy extends Handler 46 implements MediaPlayer.OnPreparedListener, 47 MediaPlayer.OnCompletionListener, 48 MediaPlayer.OnErrorListener, 49 MediaPlayer.OnInfoListener, 50 SurfaceTexture.OnFrameAvailableListener { 51 // Logging tag. 52 private static final String LOGTAG = "HTML5VideoViewProxy"; 53 54 // Message Ids for WebCore thread -> UI thread communication. 55 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; 60 private static final int BUFFERING_START = 105; 61 private static final int BUFFERING_END = 106; 62 63 // Message Ids to be handled on the WebCore thread 64 private static final int PREPARED = 200; 65 private static final int ENDED = 201; 66 private static final int POSTER_FETCHED = 202; 67 private static final int PAUSED = 203; 68 private static final int STOPFULLSCREEN = 204; 69 private static final int RESTORESTATE = 205; 70 71 // Timer thread -> UI thread 72 private static final int TIMEUPDATE = 300; 73 74 // The C++ MediaPlayerPrivateAndroid object. 75 int mNativePointer; 76 // The handler for WebCore thread messages; 77 private Handler mWebCoreHandler; 78 // The WebView instance that created this view. 79 private WebView mWebView; 80 // The poster image to be shown when the video is not playing. 81 // This ref prevents the bitmap from being GC'ed. 82 private Bitmap mPoster; 83 // The poster downloader. 84 private PosterDownloader mPosterDownloader; 85 // The seek position. 86 private int mSeekPosition; 87 // A helper class to control the playback. This executes on the UI thread! 88 private static final class VideoPlayer { 89 // The proxy that is currently playing (if any). 90 private static HTML5VideoViewProxy mCurrentProxy; 91 // The VideoView instance. This is a singleton for now, at least until 92 // http://b/issue?id=1973663 is fixed. 93 private static HTML5VideoView mHTML5VideoView; 94 95 private static boolean isVideoSelfEnded = false; 96 // By using the baseLayer and the current video Layer ID, we can 97 // identify the exact layer on the UI thread to use the SurfaceTexture. 98 private static int mBaseLayer = 0; 99 100 private static void setPlayerBuffering(boolean playerBuffering) { 101 mHTML5VideoView.setPlayerBuffering(playerBuffering); 102 } 103 104 // Every time webView setBaseLayer, this will be called. 105 // When we found the Video layer, then we set the Surface Texture to it. 106 // Otherwise, we may want to delete the Surface Texture to save memory. 107 public static void setBaseLayer(int layer) { 108 // Don't do this for full screen mode. 109 if (mHTML5VideoView != null 110 && !mHTML5VideoView.isFullScreenMode() 111 && !mHTML5VideoView.surfaceTextureDeleted()) { 112 mBaseLayer = layer; 113 114 int currentVideoLayerId = mHTML5VideoView.getVideoLayerId(); 115 SurfaceTexture surfTexture = mHTML5VideoView.getSurfaceTexture(currentVideoLayerId); 116 int textureName = mHTML5VideoView.getTextureName(); 117 118 if (layer != 0 && surfTexture != null && currentVideoLayerId != -1) { 119 int playerState = mHTML5VideoView.getCurrentState(); 120 if (mHTML5VideoView.getPlayerBuffering()) 121 playerState = HTML5VideoView.STATE_NOTPREPARED; 122 boolean foundInTree = nativeSendSurfaceTexture(surfTexture, 123 layer, currentVideoLayerId, textureName, 124 playerState); 125 if (playerState >= HTML5VideoView.STATE_PREPARED 126 && !foundInTree) { 127 mHTML5VideoView.pauseAndDispatch(mCurrentProxy); 128 mHTML5VideoView.deleteSurfaceTexture(); 129 } 130 } 131 } 132 } 133 134 // When a WebView is paused, we also want to pause the video in it. 135 public static void pauseAndDispatch() { 136 if (mHTML5VideoView != null) { 137 mHTML5VideoView.pauseAndDispatch(mCurrentProxy); 138 // When switching out, clean the video content on the old page 139 // by telling the layer not readyToUseSurfTex. 140 setBaseLayer(mBaseLayer); 141 } 142 } 143 144 public static void enterFullScreenVideo(int layerId, String url, 145 HTML5VideoViewProxy proxy, WebView webView) { 146 // Save the inline video info and inherit it in the full screen 147 int savePosition = 0; 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 } 154 mHTML5VideoView.release(); 155 } 156 mHTML5VideoView = new HTML5VideoFullScreen(proxy.getContext(), 157 layerId, savePosition); 158 mCurrentProxy = proxy; 159 160 mHTML5VideoView.setVideoURI(url, mCurrentProxy); 161 162 mHTML5VideoView.enterFullScreenVideoState(layerId, proxy, webView); 163 } 164 165 public static void exitFullScreenVideo(HTML5VideoViewProxy proxy, 166 WebView webView) { 167 if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) { 168 WebChromeClient client = webView.getWebChromeClient(); 169 if (client != null) { 170 client.onHideCustomView(); 171 } 172 } 173 } 174 175 // This is on the UI thread. 176 // When native tell Java to play, we need to check whether or not it is 177 // still the same video by using videoLayerId and treat it differently. 178 public static void play(String url, int time, HTML5VideoViewProxy proxy, 179 WebChromeClient client, int videoLayerId) { 180 int currentVideoLayerId = -1; 181 boolean backFromFullScreenMode = false; 182 if (mHTML5VideoView != null) { 183 currentVideoLayerId = mHTML5VideoView.getVideoLayerId(); 184 backFromFullScreenMode = mHTML5VideoView.fullScreenExited(); 185 186 // When playing video back to back in full screen mode, 187 // javascript will switch the src and call play. 188 // In this case, we can just reuse the same full screen view, 189 // and play the video after prepared. 190 if (mHTML5VideoView.isFullScreenMode() 191 && !backFromFullScreenMode 192 && currentVideoLayerId != videoLayerId 193 && mCurrentProxy != proxy) { 194 mCurrentProxy = proxy; 195 mHTML5VideoView.setStartWhenPrepared(true); 196 mHTML5VideoView.setVideoURI(url, proxy); 197 mHTML5VideoView.reprepareData(proxy); 198 return; 199 } 200 } 201 202 if (backFromFullScreenMode 203 || currentVideoLayerId != videoLayerId 204 || mHTML5VideoView.surfaceTextureDeleted()) { 205 // Here, we handle the case when switching to a new video, 206 // either inside a WebView or across WebViews 207 // For switching videos within a WebView or across the WebView, 208 // we need to pause the old one and re-create a new media player 209 // inside the HTML5VideoView. 210 if (mHTML5VideoView != null) { 211 if (!backFromFullScreenMode) { 212 mHTML5VideoView.pauseAndDispatch(mCurrentProxy); 213 } 214 // release the media player to avoid finalize error 215 mHTML5VideoView.release(); 216 } 217 mCurrentProxy = proxy; 218 mHTML5VideoView = new HTML5VideoInline(videoLayerId, time); 219 220 mHTML5VideoView.setVideoURI(url, mCurrentProxy); 221 mHTML5VideoView.prepareDataAndDisplayMode(proxy); 222 } else if (mCurrentProxy == proxy) { 223 // Here, we handle the case when we keep playing with one video 224 if (!mHTML5VideoView.isPlaying()) { 225 mHTML5VideoView.seekTo(time); 226 mHTML5VideoView.start(); 227 } 228 } else if (mCurrentProxy != null) { 229 // Some other video is already playing. Notify the caller that 230 // its playback ended. 231 proxy.dispatchOnEnded(); 232 } 233 } 234 235 public static boolean isPlaying(HTML5VideoViewProxy proxy) { 236 return (mCurrentProxy == proxy && mHTML5VideoView != null 237 && mHTML5VideoView.isPlaying()); 238 } 239 240 public static int getCurrentPosition() { 241 int currentPosMs = 0; 242 if (mHTML5VideoView != null) { 243 currentPosMs = mHTML5VideoView.getCurrentPosition(); 244 } 245 return currentPosMs; 246 } 247 248 public static void seek(int time, HTML5VideoViewProxy proxy) { 249 if (mCurrentProxy == proxy && time >= 0 && mHTML5VideoView != null) { 250 mHTML5VideoView.seekTo(time); 251 } 252 } 253 254 public static void pause(HTML5VideoViewProxy proxy) { 255 if (mCurrentProxy == proxy && mHTML5VideoView != null) { 256 mHTML5VideoView.pause(); 257 } 258 } 259 260 public static void onPrepared() { 261 if (!mHTML5VideoView.isFullScreenMode()) { 262 mHTML5VideoView.start(); 263 } 264 if (mBaseLayer != 0) { 265 setBaseLayer(mBaseLayer); 266 } 267 } 268 269 public static void end() { 270 if (mCurrentProxy != null) { 271 if (isVideoSelfEnded) 272 mCurrentProxy.dispatchOnEnded(); 273 else 274 mCurrentProxy.dispatchOnPaused(); 275 } 276 isVideoSelfEnded = false; 277 } 278 } 279 280 // A bunch event listeners for our VideoView 281 // MediaPlayer.OnPreparedListener 282 public void onPrepared(MediaPlayer mp) { 283 VideoPlayer.onPrepared(); 284 Message msg = Message.obtain(mWebCoreHandler, PREPARED); 285 Map<String, Object> map = new HashMap<String, Object>(); 286 map.put("dur", new Integer(mp.getDuration())); 287 map.put("width", new Integer(mp.getVideoWidth())); 288 map.put("height", new Integer(mp.getVideoHeight())); 289 msg.obj = map; 290 mWebCoreHandler.sendMessage(msg); 291 } 292 293 // MediaPlayer.OnCompletionListener; 294 public void onCompletion(MediaPlayer mp) { 295 // The video ended by itself, so we need to 296 // send a message to the UI thread to dismiss 297 // the video view and to return to the WebView. 298 // arg1 == 1 means the video ends by itself. 299 sendMessage(obtainMessage(ENDED, 1, 0)); 300 } 301 302 // MediaPlayer.OnErrorListener 303 public boolean onError(MediaPlayer mp, int what, int extra) { 304 sendMessage(obtainMessage(ERROR)); 305 return false; 306 } 307 308 public void dispatchOnEnded() { 309 Message msg = Message.obtain(mWebCoreHandler, ENDED); 310 mWebCoreHandler.sendMessage(msg); 311 } 312 313 public void dispatchOnPaused() { 314 Message msg = Message.obtain(mWebCoreHandler, PAUSED); 315 mWebCoreHandler.sendMessage(msg); 316 } 317 318 public void dispatchOnStopFullScreen() { 319 Message msg = Message.obtain(mWebCoreHandler, STOPFULLSCREEN); 320 mWebCoreHandler.sendMessage(msg); 321 } 322 323 public void dispatchOnRestoreState() { 324 Message msg = Message.obtain(mWebCoreHandler, RESTORESTATE); 325 mWebCoreHandler.sendMessage(msg); 326 } 327 328 public void onTimeupdate() { 329 sendMessage(obtainMessage(TIMEUPDATE)); 330 } 331 332 // When there is a frame ready from surface texture, we should tell WebView 333 // to refresh. 334 @Override 335 public void onFrameAvailable(SurfaceTexture surfaceTexture) { 336 // TODO: This should support partial invalidation too. 337 mWebView.invalidate(); 338 } 339 340 // Handler for the messages from WebCore or Timer thread to the UI thread. 341 @Override 342 public void handleMessage(Message msg) { 343 // This executes on the UI thread. 344 switch (msg.what) { 345 case PLAY: { 346 String url = (String) msg.obj; 347 WebChromeClient client = mWebView.getWebChromeClient(); 348 int videoLayerID = msg.arg1; 349 if (client != null) { 350 VideoPlayer.play(url, mSeekPosition, this, client, videoLayerID); 351 } 352 break; 353 } 354 case SEEK: { 355 Integer time = (Integer) msg.obj; 356 mSeekPosition = time; 357 VideoPlayer.seek(mSeekPosition, this); 358 break; 359 } 360 case PAUSE: { 361 VideoPlayer.pause(this); 362 break; 363 } 364 case ENDED: 365 if (msg.arg1 == 1) 366 VideoPlayer.isVideoSelfEnded = true; 367 VideoPlayer.end(); 368 break; 369 case ERROR: { 370 WebChromeClient client = mWebView.getWebChromeClient(); 371 if (client != null) { 372 client.onHideCustomView(); 373 } 374 break; 375 } 376 case LOAD_DEFAULT_POSTER: { 377 WebChromeClient client = mWebView.getWebChromeClient(); 378 if (client != null) { 379 doSetPoster(client.getDefaultVideoPoster()); 380 } 381 break; 382 } 383 case TIMEUPDATE: { 384 if (VideoPlayer.isPlaying(this)) { 385 sendTimeupdate(); 386 } 387 break; 388 } 389 case BUFFERING_START: { 390 VideoPlayer.setPlayerBuffering(true); 391 break; 392 } 393 case BUFFERING_END: { 394 VideoPlayer.setPlayerBuffering(false); 395 break; 396 } 397 } 398 } 399 400 // Everything below this comment executes on the WebCore thread, except for 401 // the EventHandler methods, which are called on the network thread. 402 403 // A helper class that knows how to download posters 404 private static final class PosterDownloader implements EventHandler { 405 // The request queue. This is static as we have one queue for all posters. 406 private static RequestQueue mRequestQueue; 407 private static int mQueueRefCount = 0; 408 // The poster URL 409 private URL mUrl; 410 // The proxy we're doing this for. 411 private final HTML5VideoViewProxy mProxy; 412 // The poster bytes. We only touch this on the network thread. 413 private ByteArrayOutputStream mPosterBytes; 414 // The request handle. We only touch this on the WebCore thread. 415 private RequestHandle mRequestHandle; 416 // The response status code. 417 private int mStatusCode; 418 // The response headers. 419 private Headers mHeaders; 420 // The handler to handle messages on the WebCore thread. 421 private Handler mHandler; 422 423 public PosterDownloader(String url, HTML5VideoViewProxy proxy) { 424 try { 425 mUrl = new URL(url); 426 } catch (MalformedURLException e) { 427 mUrl = null; 428 } 429 mProxy = proxy; 430 mHandler = new Handler(); 431 } 432 // Start the download. Called on WebCore thread. 433 public void start() { 434 retainQueue(); 435 436 if (mUrl == null) { 437 return; 438 } 439 440 // Only support downloading posters over http/https. 441 // FIXME: Add support for other schemes. WebKit seems able to load 442 // posters over other schemes e.g. file://, but gets the dimensions wrong. 443 String protocol = mUrl.getProtocol(); 444 if ("http".equals(protocol) || "https".equals(protocol)) { 445 mRequestHandle = mRequestQueue.queueRequest(mUrl.toString(), "GET", null, 446 this, null, 0); 447 } 448 } 449 // Cancel the download if active and release the queue. Called on WebCore thread. 450 public void cancelAndReleaseQueue() { 451 if (mRequestHandle != null) { 452 mRequestHandle.cancel(); 453 mRequestHandle = null; 454 } 455 releaseQueue(); 456 } 457 // EventHandler methods. Executed on the network thread. 458 public void status(int major_version, 459 int minor_version, 460 int code, 461 String reason_phrase) { 462 mStatusCode = code; 463 } 464 465 public void headers(Headers headers) { 466 mHeaders = headers; 467 } 468 469 public void data(byte[] data, int len) { 470 if (mPosterBytes == null) { 471 mPosterBytes = new ByteArrayOutputStream(); 472 } 473 mPosterBytes.write(data, 0, len); 474 } 475 476 public void endData() { 477 if (mStatusCode == 200) { 478 if (mPosterBytes.size() > 0) { 479 Bitmap poster = BitmapFactory.decodeByteArray( 480 mPosterBytes.toByteArray(), 0, mPosterBytes.size()); 481 mProxy.doSetPoster(poster); 482 } 483 cleanup(); 484 } else if (mStatusCode >= 300 && mStatusCode < 400) { 485 // We have a redirect. 486 try { 487 mUrl = new URL(mHeaders.getLocation()); 488 } catch (MalformedURLException e) { 489 mUrl = null; 490 } 491 if (mUrl != null) { 492 mHandler.post(new Runnable() { 493 public void run() { 494 if (mRequestHandle != null) { 495 mRequestHandle.setupRedirect(mUrl.toString(), mStatusCode, 496 new HashMap<String, String>()); 497 } 498 } 499 }); 500 } 501 } 502 } 503 504 public void certificate(SslCertificate certificate) { 505 // Don't care. 506 } 507 508 public void error(int id, String description) { 509 cleanup(); 510 } 511 512 public boolean handleSslErrorRequest(SslError error) { 513 // Don't care. If this happens, data() will never be called so 514 // mPosterBytes will never be created, so no need to call cleanup. 515 return false; 516 } 517 // Tears down the poster bytes stream. Called on network thread. 518 private void cleanup() { 519 if (mPosterBytes != null) { 520 try { 521 mPosterBytes.close(); 522 } catch (IOException ignored) { 523 // Ignored. 524 } finally { 525 mPosterBytes = null; 526 } 527 } 528 } 529 530 // Queue management methods. Called on WebCore thread. 531 private void retainQueue() { 532 if (mRequestQueue == null) { 533 mRequestQueue = new RequestQueue(mProxy.getContext()); 534 } 535 mQueueRefCount++; 536 } 537 538 private void releaseQueue() { 539 if (mQueueRefCount == 0) { 540 return; 541 } 542 if (--mQueueRefCount == 0) { 543 mRequestQueue.shutdown(); 544 mRequestQueue = null; 545 } 546 } 547 } 548 549 /** 550 * Private constructor. 551 * @param webView is the WebView that hosts the video. 552 * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object. 553 */ 554 private HTML5VideoViewProxy(WebView webView, int nativePtr) { 555 // This handler is for the main (UI) thread. 556 super(Looper.getMainLooper()); 557 // Save the WebView object. 558 mWebView = webView; 559 // Pass Proxy into webview, such that every time we have a setBaseLayer 560 // call, we tell this Proxy to call the native to update the layer tree 561 // for the Video Layer's surface texture info 562 mWebView.setHTML5VideoViewProxy(this); 563 // Save the native ptr 564 mNativePointer = nativePtr; 565 // create the message handler for this thread 566 createWebCoreHandler(); 567 } 568 569 private void createWebCoreHandler() { 570 mWebCoreHandler = new Handler() { 571 @Override 572 public void handleMessage(Message msg) { 573 switch (msg.what) { 574 case PREPARED: { 575 Map<String, Object> map = (Map<String, Object>) msg.obj; 576 Integer duration = (Integer) map.get("dur"); 577 Integer width = (Integer) map.get("width"); 578 Integer height = (Integer) map.get("height"); 579 nativeOnPrepared(duration.intValue(), width.intValue(), 580 height.intValue(), mNativePointer); 581 break; 582 } 583 case ENDED: 584 mSeekPosition = 0; 585 nativeOnEnded(mNativePointer); 586 break; 587 case PAUSED: 588 nativeOnPaused(mNativePointer); 589 break; 590 case POSTER_FETCHED: 591 Bitmap poster = (Bitmap) msg.obj; 592 nativeOnPosterFetched(poster, mNativePointer); 593 break; 594 case TIMEUPDATE: 595 nativeOnTimeupdate(msg.arg1, mNativePointer); 596 break; 597 case STOPFULLSCREEN: 598 nativeOnStopFullscreen(mNativePointer); 599 break; 600 case RESTORESTATE: 601 nativeOnRestoreState(mNativePointer); 602 break; 603 } 604 } 605 }; 606 } 607 608 private void doSetPoster(Bitmap poster) { 609 if (poster == null) { 610 return; 611 } 612 // Save a ref to the bitmap and send it over to the WebCore thread. 613 mPoster = poster; 614 Message msg = Message.obtain(mWebCoreHandler, POSTER_FETCHED); 615 msg.obj = poster; 616 mWebCoreHandler.sendMessage(msg); 617 } 618 619 private void sendTimeupdate() { 620 Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE); 621 msg.arg1 = VideoPlayer.getCurrentPosition(); 622 mWebCoreHandler.sendMessage(msg); 623 } 624 625 public Context getContext() { 626 return mWebView.getContext(); 627 } 628 629 // The public methods below are all called from WebKit only. 630 /** 631 * Play a video stream. 632 * @param url is the URL of the video stream. 633 */ 634 public void play(String url, int position, int videoLayerID) { 635 if (url == null) { 636 return; 637 } 638 639 if (position > 0) { 640 seek(position); 641 } 642 Message message = obtainMessage(PLAY); 643 message.arg1 = videoLayerID; 644 message.obj = url; 645 sendMessage(message); 646 } 647 648 /** 649 * Seek into the video stream. 650 * @param time is the position in the video stream. 651 */ 652 public void seek(int time) { 653 Message message = obtainMessage(SEEK); 654 message.obj = new Integer(time); 655 sendMessage(message); 656 } 657 658 /** 659 * Pause the playback. 660 */ 661 public void pause() { 662 Message message = obtainMessage(PAUSE); 663 sendMessage(message); 664 } 665 666 /** 667 * Tear down this proxy object. 668 */ 669 public void teardown() { 670 // This is called by the C++ MediaPlayerPrivate dtor. 671 // Cancel any active poster download. 672 if (mPosterDownloader != null) { 673 mPosterDownloader.cancelAndReleaseQueue(); 674 } 675 mNativePointer = 0; 676 } 677 678 /** 679 * Load the poster image. 680 * @param url is the URL of the poster image. 681 */ 682 public void loadPoster(String url) { 683 if (url == null) { 684 Message message = obtainMessage(LOAD_DEFAULT_POSTER); 685 sendMessage(message); 686 return; 687 } 688 // Cancel any active poster download. 689 if (mPosterDownloader != null) { 690 mPosterDownloader.cancelAndReleaseQueue(); 691 } 692 // Load the poster asynchronously 693 mPosterDownloader = new PosterDownloader(url, this); 694 mPosterDownloader.start(); 695 } 696 697 // These three function are called from UI thread only by WebView. 698 public void setBaseLayer(int layer) { 699 VideoPlayer.setBaseLayer(layer); 700 } 701 702 public void pauseAndDispatch() { 703 VideoPlayer.pauseAndDispatch(); 704 } 705 706 public void enterFullScreenVideo(int layerId, String url) { 707 VideoPlayer.enterFullScreenVideo(layerId, url, this, mWebView); 708 } 709 710 public void exitFullScreenVideo() { 711 VideoPlayer.exitFullScreenVideo(this, mWebView); 712 } 713 714 /** 715 * The factory for HTML5VideoViewProxy instances. 716 * @param webViewCore is the WebViewCore that is requesting the proxy. 717 * 718 * @return a new HTML5VideoViewProxy object. 719 */ 720 public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) { 721 return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr); 722 } 723 724 /* package */ WebView getWebView() { 725 return mWebView; 726 } 727 728 private native void nativeOnPrepared(int duration, int width, int height, int nativePointer); 729 private native void nativeOnEnded(int nativePointer); 730 private native void nativeOnPaused(int nativePointer); 731 private native void nativeOnPosterFetched(Bitmap poster, int nativePointer); 732 private native void nativeOnTimeupdate(int position, int nativePointer); 733 private native void nativeOnStopFullscreen(int nativePointer); 734 private native void nativeOnRestoreState(int nativePointer); 735 private native static boolean nativeSendSurfaceTexture(SurfaceTexture texture, 736 int baseLayer, int videoLayerId, int textureName, 737 int playerState); 738 739 @Override 740 public boolean onInfo(MediaPlayer mp, int what, int extra) { 741 if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) { 742 sendMessage(obtainMessage(BUFFERING_START, what, extra)); 743 } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) { 744 sendMessage(obtainMessage(BUFFERING_END, what, extra)); 745 } 746 return false; 747 } 748} 749