| Index: android_webview/java/src/org/chromium/android_webview/ExternalVideoSurfaceContainer.java
|
| diff --git a/android_webview/java/src/org/chromium/android_webview/ExternalVideoSurfaceContainer.java b/android_webview/java/src/org/chromium/android_webview/ExternalVideoSurfaceContainer.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b7ade19eccc3b1883e21fe93b4ee400c3486e77e
|
| --- /dev/null
|
| +++ b/android_webview/java/src/org/chromium/android_webview/ExternalVideoSurfaceContainer.java
|
| @@ -0,0 +1,225 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.android_webview;
|
| +
|
| +import android.content.Context;
|
| +import android.graphics.Canvas;
|
| +import android.view.SurfaceHolder;
|
| +import android.view.SurfaceView;
|
| +import android.view.ViewGroup;
|
| +
|
| +import org.chromium.content.browser.ContentViewCore;
|
| +import org.chromium.content.browser.ExternalVideoSurfaceDelegate;
|
| +import org.chromium.content.browser.RenderCoordinates;
|
| +
|
| +import java.lang.ref.WeakReference;
|
| +
|
| +/**
|
| + * This is a container for external video surfaces.
|
| + *
|
| + * Please contact ycheo@chromium.org or wonsik@chromium.org if you have any
|
| + * questions or issues for this class.
|
| + */
|
| +public class ExternalVideoSurfaceContainer implements ExternalVideoSurfaceDelegate,
|
| + SurfaceHolder.Callback {
|
| + private static final int INVALID_PLAYER_ID = -1;
|
| +
|
| + // Because WebView does hole-punching by itself, instead, the hole-punching logic
|
| + // in SurfaceView can clear out some web elements like media control or subtitle.
|
| + // So we need to disable its hole-punching logic.
|
| + private static class NoPunchingSurfaceView extends SurfaceView {
|
| + public NoPunchingSurfaceView(Context context) {
|
| + super(context);
|
| + }
|
| + // SurfaceView.dispatchDraw implementation punches a hole in the view hierarchy.
|
| + // Disable this by making this a no-op.
|
| + @Override
|
| + protected void dispatchDraw(Canvas canvas) {}
|
| + }
|
| +
|
| + // There can be at most 1 external video surface for now.
|
| + // If there are the multiple requests for the surface, then the second video will
|
| + // kick the first one off.
|
| + // To support the mulitple video surfaces seems impractical, because z-order between
|
| + // the multiple SurfaceViews is non-deterministic.
|
| + private static WeakReference<ExternalVideoSurfaceContainer> sActiveContainer =
|
| + new WeakReference<ExternalVideoSurfaceContainer>(null);
|
| +
|
| + private final ContentViewCore mContentViewCore;
|
| + private int mPlayerId = INVALID_PLAYER_ID;
|
| + private SurfaceView mSurfaceView;
|
| +
|
| + // The absolute CSS coordinates of the video element.
|
| + private float mLeft;
|
| + private float mTop;
|
| + private float mRight;
|
| + private float mBottom;
|
| +
|
| + // The physical location/size of the external video surface.
|
| + private float mX;
|
| + private float mY;
|
| + private int mWidth;
|
| + private int mHeight;
|
| +
|
| + public ExternalVideoSurfaceContainer(ContentViewCore contentViewCore) {
|
| + assert contentViewCore != null;
|
| + mContentViewCore = contentViewCore;
|
| + initializeCurrentPositionOfSurfaceView();
|
| + }
|
| +
|
| + /**
|
| + * @see ExternalVideoSurfaceDelegate#requestExternalVideoSurface(int)
|
| + */
|
| + @Override
|
| + public void requestExternalVideoSurface(int playerId) {
|
| + if (mPlayerId == playerId) return;
|
| +
|
| + if (mPlayerId == INVALID_PLAYER_ID) {
|
| + setActiveContainer(this);
|
| + }
|
| +
|
| + mPlayerId = playerId;
|
| + initializeCurrentPositionOfSurfaceView();
|
| +
|
| + createSurfaceView();
|
| + }
|
| +
|
| + /**
|
| + * @see ExternalVideoSurfaceDelegate#releaseExternalVideoSurface(int)
|
| + */
|
| + @Override
|
| + public void releaseExternalVideoSurface(int playerId) {
|
| + if (mPlayerId != playerId) return;
|
| +
|
| + releaseIfActiveContainer(this);
|
| +
|
| + mPlayerId = INVALID_PLAYER_ID;
|
| + }
|
| +
|
| + /**
|
| + * @see ExternalVideoSurfaceDelegate#destroy()
|
| + */
|
| + @Override
|
| + public void destroy() {
|
| + releaseExternalVideoSurface(mPlayerId);
|
| + }
|
| +
|
| + private void initializeCurrentPositionOfSurfaceView() {
|
| + mX = Float.NaN;
|
| + mY = Float.NaN;
|
| + mWidth = 0;
|
| + mHeight = 0;
|
| + }
|
| +
|
| + private static void setActiveContainer(ExternalVideoSurfaceContainer container) {
|
| + ExternalVideoSurfaceContainer activeContainer = sActiveContainer.get();
|
| + if (activeContainer != null) {
|
| + activeContainer.removeSurfaceView();
|
| + }
|
| + sActiveContainer = new WeakReference<ExternalVideoSurfaceContainer>(container);
|
| + }
|
| +
|
| + private static void releaseIfActiveContainer(ExternalVideoSurfaceContainer container) {
|
| + ExternalVideoSurfaceContainer activeContainer = sActiveContainer.get();
|
| + if (activeContainer == container) {
|
| + setActiveContainer(null);
|
| + }
|
| + }
|
| +
|
| + private void createSurfaceView() {
|
| + mSurfaceView = new NoPunchingSurfaceView(mContentViewCore.getContext());
|
| + mSurfaceView.getHolder().addCallback(this);
|
| + // SurfaceHoder.surfaceCreated() will be called after the SurfaceView is attached to
|
| + // the Window and becomes visible.
|
| + mContentViewCore.getContainerView().addView(mSurfaceView);
|
| + }
|
| +
|
| + private void removeSurfaceView() {
|
| + // SurfaceHoder.surfaceDestroyed() will be called in ViewGroup.removeView()
|
| + // as soon as the SurfaceView is detached from the Window.
|
| + mContentViewCore.getContainerView().removeView(mSurfaceView);
|
| + mSurfaceView = null;
|
| + }
|
| +
|
| + /**
|
| + * @see ExternalVideoSurfaceDelegate#onExternalVideoSurfacePositionChanged(
|
| + * int, float, float, float, float)
|
| + */
|
| + @Override
|
| + public void onExternalVideoSurfacePositionChanged(
|
| + int playerId, float left, float top, float right, float bottom) {
|
| + if (mPlayerId != playerId) return;
|
| +
|
| + mLeft = left;
|
| + mTop = top;
|
| + mRight = right;
|
| + mBottom = bottom;
|
| +
|
| + layOutSurfaceView();
|
| + }
|
| +
|
| + /**
|
| + * @see ExternalVideoSurfaceDelegate#onRenderCoordinatesChanged()
|
| + */
|
| + @Override
|
| + public void onRenderCoordinatesChanged() {
|
| + if (mPlayerId == INVALID_PLAYER_ID) return;
|
| +
|
| + layOutSurfaceView();
|
| + }
|
| +
|
| + private void layOutSurfaceView() {
|
| + RenderCoordinates renderCoordinates = mContentViewCore.getRenderCoordinates();
|
| + RenderCoordinates.NormalizedPoint topLeft = renderCoordinates.createNormalizedPoint();
|
| + RenderCoordinates.NormalizedPoint bottomRight = renderCoordinates.createNormalizedPoint();
|
| + topLeft.setAbsoluteCss(mLeft, mTop);
|
| + bottomRight.setAbsoluteCss(mRight, mBottom);
|
| + float top = topLeft.getYPix();
|
| + float left = topLeft.getXPix();
|
| + float bottom = bottomRight.getYPix();
|
| + float right = bottomRight.getXPix();
|
| +
|
| + float x = left + renderCoordinates.getScrollXPix();
|
| + float y = top + renderCoordinates.getScrollYPix();
|
| + int width = Math.round(right - left);
|
| + int height = Math.round(bottom - top);
|
| + if (mX == x && mY == y && mWidth == width && mHeight == height) return;
|
| + mX = x;
|
| + mY = y;
|
| + mWidth = width;
|
| + mHeight = height;
|
| +
|
| + mSurfaceView.setX(x);
|
| + mSurfaceView.setY(y);
|
| + ViewGroup.LayoutParams layoutParams = mSurfaceView.getLayoutParams();
|
| + layoutParams.width = width;
|
| + layoutParams.height = height;
|
| + mSurfaceView.requestLayout();
|
| + }
|
| +
|
| + // SurfaceHolder.Callback methods.
|
| + @Override
|
| + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
| + }
|
| +
|
| + @Override
|
| + // surfaceCreated() callback can be called regardless of requestExternalVideoSurface,
|
| + // if the activity comes back from the background and becomes visible.
|
| + public void surfaceCreated(SurfaceHolder holder) {
|
| + if (mPlayerId != INVALID_PLAYER_ID) {
|
| + mContentViewCore.attachExternalVideoSurface(mPlayerId, holder.getSurface());
|
| + }
|
| + }
|
| +
|
| + // surfaceDestroyed() callback can be called regardless of releaseExternalVideoSurface,
|
| + // if the activity moves to the backgound and becomes invisible.
|
| + @Override
|
| + public void surfaceDestroyed(SurfaceHolder holder) {
|
| + if (mPlayerId != INVALID_PLAYER_ID) {
|
| + mContentViewCore.detachExternalVideoSurface(mPlayerId);
|
| + }
|
| + }
|
| +}
|
| +
|
|
|