Chromium Code Reviews| 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..94faf0f12cced1721a88bae0ac95c93fffcaf97d |
| --- /dev/null |
| +++ b/android_webview/java/src/org/chromium/android_webview/ExternalVideoSurfaceContainer.java |
| @@ -0,0 +1,160 @@ |
| +// Copyright 2013 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.graphics.RectF; |
| +import android.view.Surface; |
| +import android.view.SurfaceHolder; |
| +import android.view.SurfaceView; |
| +import android.view.ViewGroup; |
| + |
| +import org.chromium.content.browser.ContentViewCore; |
| +import org.chromium.content.browser.RenderCoordinates; |
| + |
| +/** |
| + * This is a container for external video surfaces. |
| + */ |
| +public class ExternalVideoSurfaceContainer implements SurfaceHolder.Callback { |
| + private static final int INVALID_PLAYER_ID = -1; |
| + |
| + // There can be at most 1 external video container. Note that this does not mean that there can |
| + // be at most 1 SurfaceView inside the container. |
| + private static class LazyHolder { |
| + private static final ExternalVideoSurfaceContainer sContainer = |
| + new ExternalVideoSurfaceContainer(); |
| + } |
| + |
| + // 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); |
| + } |
| + @Override |
| + protected void dispatchDraw(Canvas canvas) {} // Do nothing |
|
boliu
2014/01/24 21:56:32
What does this do exactly? From the public docs, o
ycheo (away)
2014/01/28 13:08:53
Done.
|
| + } |
| + |
| + // There can be at most 1 external video surface for now. |
| + // TODO(wonsik): extend it to multiple surfaces. |
|
boliu
2014/01/24 21:56:32
Ok. Should mention this in the comment.
boliu
2014/01/25 01:27:39
Misread the code. I think right now the second vid
ycheo (away)
2014/01/28 13:08:53
Yes, the second video kicks the first one off.
|
| + private NoPunchingSurfaceView mSurfaceView; |
| + |
| + private ContentViewCore mContentViewCore = null; |
|
boliu
2014/01/24 21:56:32
This reference is very scary because it will never
ycheo (away)
2014/01/28 13:08:53
Done.
|
| + private int mPlayerId = INVALID_PLAYER_ID; |
| + |
| + private ExternalVideoSurfaceContainer() {} |
| + |
| + /** |
| + * Returns the ExternalVideoSurfaceContainer instance. |
| + */ |
| + public static ExternalVideoSurfaceContainer getInstance() { |
| + return LazyHolder.sContainer; |
| + } |
| + |
| + /** |
| + * Request to associate an external video surface with the specific media player or |
| + * notify the geometry change on the video surface. |
| + * @param contentViewCore The ContentViewCore object associated with the player. |
| + * @param playerId The ID of the media player. |
| + * @param isRequest True if this call is for requesting to associate the external video surface |
| + * with the given media player. |
| + * @param rect The boundary rectangle of the video surface. |
| + */ |
| + public void notifyExternalVideoSurface( |
| + ContentViewCore contentViewCore, int playerId, boolean isRequest, RectF rect) { |
| + assert contentViewCore != null && playerId != INVALID_PLAYER_ID; |
| + if (isRequest) { |
| + requestExternalVideoSurface(contentViewCore, playerId); |
| + // fall-through |
| + } else if (rect.isEmpty()) { |
| + releaseExternalVideoSurface(contentViewCore, playerId); |
| + return; |
| + } |
| + notifyGeometryChange(playerId, rect); |
| + } |
| + |
| + private void removeSurfaceView() { |
| + if (mSurfaceView != null) { |
| + mSurfaceView.getHolder().removeCallback(this); |
| + mContentViewCore.getContainerView().removeView(mSurfaceView); |
| + mSurfaceView = null; |
| + } |
| + } |
| + |
| + private void createSurfaceView() { |
| + mSurfaceView = new NoPunchingSurfaceView(mContentViewCore.getContext()); |
| + mSurfaceView.getHolder().addCallback(this); |
| + mContentViewCore.getContainerView().addView(mSurfaceView); |
| + } |
| + |
| + private void requestExternalVideoSurface(ContentViewCore contentViewCore, int playerId) { |
| + // Detach the surface from the previous player if attached previously. |
| + if ((mContentViewCore != contentViewCore || mPlayerId != playerId) |
| + && mContentViewCore != null && mPlayerId != INVALID_PLAYER_ID) { |
| + mContentViewCore.detachExternalVideoSurface(mPlayerId); |
| + } |
| + if (mContentViewCore != contentViewCore) { |
| + removeSurfaceView(); |
| + mContentViewCore = contentViewCore; |
| + createSurfaceView(); |
| + } |
| + // Attach the surface to the player. |
| + Surface surface = mSurfaceView.getHolder().getSurface(); |
| + if (surface != null && surface.isValid()) { |
| + mContentViewCore.attachExternalVideoSurface(playerId, surface); |
| + } |
| + mPlayerId = playerId; |
| + } |
| + |
| + private void releaseExternalVideoSurface(ContentViewCore contentViewCore, int playerId) { |
| + if (mContentViewCore != contentViewCore || mPlayerId != playerId) return; |
| + |
| + mContentViewCore.detachExternalVideoSurface(mPlayerId); |
| + removeSurfaceView(); |
| + mContentViewCore = null; |
| + mPlayerId = INVALID_PLAYER_ID; |
| + } |
| + |
| + private void notifyGeometryChange(int playerId, RectF rect) { |
| + if (mContentViewCore == null || mPlayerId != playerId) return; |
| + |
| + RenderCoordinates renderCoordinates = mContentViewCore.getRenderCoordinates(); |
| + RenderCoordinates.NormalizedPoint topLeft = renderCoordinates.createNormalizedPoint(); |
| + RenderCoordinates.NormalizedPoint bottomRight = renderCoordinates.createNormalizedPoint(); |
| + topLeft.setAbsoluteCss(rect.left, rect.top); |
| + bottomRight.setAbsoluteCss(rect.right, rect.bottom); |
| + |
| + float left = topLeft.getXPix(); |
| + float top = topLeft.getYPix(); |
| + float right = bottomRight.getXPix(); |
| + float bottom = bottomRight.getYPix(); |
| + mSurfaceView.setX(left + renderCoordinates.getScrollXPix()); |
| + mSurfaceView.setY(top + renderCoordinates.getScrollYPix()); |
| + ViewGroup.LayoutParams layoutParams = mSurfaceView.getLayoutParams(); |
| + layoutParams.width = (int) (right - left + 0.5); |
| + layoutParams.height = (int) (bottom - top + 0.5); |
| + mSurfaceView.requestLayout(); |
| + } |
| + |
| + @Override |
| + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { |
| + } |
| + |
| + @Override |
| + public void surfaceCreated(SurfaceHolder holder) { |
| + if (mContentViewCore != null && mPlayerId != INVALID_PLAYER_ID) { |
| + mContentViewCore.attachExternalVideoSurface(mPlayerId, holder.getSurface()); |
| + } |
| + } |
| + |
| + @Override |
| + public void surfaceDestroyed(SurfaceHolder holder) { |
| + if (mContentViewCore != null && mPlayerId != INVALID_PLAYER_ID) { |
| + mContentViewCore.detachExternalVideoSurface(mPlayerId); |
| + } |
| + } |
| +} |