Loading core/java/android/webkit/HTML5VideoFullScreen.java 0 → 100644 +286 −0 Original line number Diff line number Diff line package android.webkit; import android.content.Context; import android.media.MediaPlayer; import android.media.Metadata; import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.webkit.HTML5VideoView; import android.webkit.HTML5VideoViewProxy; import android.widget.FrameLayout; import android.widget.MediaController; import android.widget.MediaController.MediaPlayerControl; /** * @hide This is only used by the browser */ public class HTML5VideoFullScreen extends HTML5VideoView implements MediaPlayerControl, MediaPlayer.OnPreparedListener, View.OnTouchListener { private SurfaceView mSurfaceView; // We need the full screen state to decide which surface to render to and // when to create the MediaPlayer accordingly. static final int FULLSCREEN_OFF = 0; static final int FULLSCREEN_SURFACECREATING = 1; static final int FULLSCREEN_SURFACECREATED = 2; private int mFullScreenMode; // The Media Controller only used for full screen mode private MediaController mMediaController; // SurfaceHolder for full screen private SurfaceHolder mSurfaceHolder = null; // Data only for MediaController private boolean mCanSeekBack; private boolean mCanSeekForward; private boolean mCanPause; private int mCurrentBufferPercentage; // The progress view. private static View mProgressView; // The container for the progress view and video view private static FrameLayout mLayout; SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() { public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { if (mPlayer != null && mMediaController != null && mCurrentState == STATE_PREPARED) { if (mMediaController.isShowing()) { // ensure the controller will get repositioned later mMediaController.hide(); } mMediaController.show(); } } public void surfaceCreated(SurfaceHolder holder) { mSurfaceHolder = holder; mFullScreenMode = FULLSCREEN_SURFACECREATED; prepareForFullScreen(); } public void surfaceDestroyed(SurfaceHolder holder) { // after we return from this we can't use the surface any more mSurfaceHolder = null; // The current Video View will be destroy when we play a new video. } }; public SurfaceView getSurfaceView() { return mSurfaceView; } HTML5VideoFullScreen(Context context, int videoLayerId, int position, boolean autoStart) { mSurfaceView = new SurfaceView(context); mFullScreenMode = FULLSCREEN_OFF; init(videoLayerId, position, autoStart); } private void setMediaController(MediaController m) { mMediaController = m; attachMediaController(); } private void attachMediaController() { if (mPlayer != null && mMediaController != null) { mMediaController.setMediaPlayer(this); mMediaController.setAnchorView(mSurfaceView); //Will be enabled when prepared mMediaController.setEnabled(false); } } @Override public void decideDisplayMode() { mPlayer.setDisplay(mSurfaceHolder); } @Override public void prepareForFullScreen() { // So in full screen, we reset the MediaPlayer mPlayer.reset(); setMediaController(new MediaController(mProxy.getContext())); prepareDataAndDisplayMode(mProxy); } private void toggleMediaControlsVisiblity() { if (mMediaController.isShowing()) { mMediaController.hide(); } else { mMediaController.show(); } } @Override public void onPrepared(MediaPlayer mp) { super.onPrepared(mp); mSurfaceView.setOnTouchListener(this); // Get the capabilities of the player for this stream Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL, MediaPlayer.BYPASS_METADATA_FILTER); if (data != null) { mCanPause = !data.has(Metadata.PAUSE_AVAILABLE) || data.getBoolean(Metadata.PAUSE_AVAILABLE); mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE) || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE); mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE) || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE); } else { mCanPause = mCanSeekBack = mCanSeekForward = true; } // mMediaController status depends on the Metadata result, so put it // after reading the MetaData if (mMediaController != null) { mMediaController.setEnabled(true); // If paused , should show the controller for ever! if (getAutostart()) mMediaController.show(); else mMediaController.show(0); } if (mProgressView != null) { mProgressView.setVisibility(View.GONE); mLayout.removeView(mProgressView); mProgressView = null; } } private final WebChromeClient.CustomViewCallback mCallback = new WebChromeClient.CustomViewCallback() { public void onCustomViewHidden() { // It listens to SurfaceHolder.Callback.SurfaceDestroyed event // which happens when the video view is detached from its parent // view. This happens in the WebChromeClient before this method // is invoked. mTimer.cancel(); mTimer = null; pauseAndDispatch(mProxy); mLayout.removeView(getSurfaceView()); if (mProgressView != null) { mLayout.removeView(mProgressView); mProgressView = null; } mLayout = null; // Re enable plugin views. mProxy.getWebView().getViewManager().showAll(); mProxy = null; } }; @Override public void enterFullScreenVideoState(int layerId, HTML5VideoViewProxy proxy, WebView webView) { mFullScreenMode = FULLSCREEN_SURFACECREATING; mCurrentBufferPercentage = 0; mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); mProxy = proxy; mSurfaceView.getHolder().addCallback(mSHCallback); mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); mSurfaceView.setFocusable(true); mSurfaceView.setFocusableInTouchMode(true); mSurfaceView.requestFocus(); // Create a FrameLayout that will contain the VideoView and the // progress view (if any). mLayout = new FrameLayout(mProxy.getContext()); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER); mLayout.addView(getSurfaceView(), layoutParams); mLayout.setVisibility(View.VISIBLE); WebChromeClient client = webView.getWebChromeClient(); client.onShowCustomView(mLayout, mCallback); // Plugins like Flash will draw over the video so hide // them while we're playing. mProxy.getWebView().getViewManager().hideAll(); mProgressView = client.getVideoLoadingProgressView(); if (mProgressView != null) { mLayout.addView(mProgressView, layoutParams); mProgressView.setVisibility(View.VISIBLE); } } /** * @return true when we are in full screen mode, even the surface not fully * created. */ public boolean isFullScreenMode() { return true; } // MediaController FUNCTIONS: @Override public boolean canPause() { return mCanPause; } @Override public boolean canSeekBackward() { return mCanSeekBack; } @Override public boolean canSeekForward() { return mCanSeekForward; } @Override public int getBufferPercentage() { if (mPlayer != null) { return mCurrentBufferPercentage; } return 0; } // Other listeners functions: private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() { public void onBufferingUpdate(MediaPlayer mp, int percent) { mCurrentBufferPercentage = percent; } }; @Override public boolean onTouch(View v, MotionEvent event) { if (mFullScreenMode >= FULLSCREEN_SURFACECREATED && mMediaController != null) { toggleMediaControlsVisiblity(); } return false; } } core/java/android/webkit/HTML5VideoInline.java 0 → 100644 +99 −0 Original line number Diff line number Diff line package android.webkit; import android.graphics.SurfaceTexture; import android.media.MediaPlayer; import android.webkit.HTML5VideoView; import android.webkit.HTML5VideoViewProxy; import android.opengl.GLES20; /** * @hide This is only used by the browser */ public class HTML5VideoInline extends HTML5VideoView{ // Due to the fact that SurfaceTexture consume a lot of memory, we make it // as static. m_textureNames is the texture bound with this SurfaceTexture. private static SurfaceTexture mSurfaceTexture = null; private static int[] mTextureNames; // Only when the video is prepared, we render using SurfaceTexture. // This in fact is used to avoid showing the obsolete content when // switching videos. private static boolean mReadyToUseSurfTex = false; // Video control FUNCTIONS: @Override public void start() { super.start(); if (mCurrentState == STATE_PREPARED) { mReadyToUseSurfTex = true; } } HTML5VideoInline(int videoLayerId, int position, boolean autoStart) { init(videoLayerId, position, autoStart); mReadyToUseSurfTex = false; } @Override public void decideDisplayMode() { mPlayer.setTexture(getSurfaceTextureInstance()); } // Normally called immediately after setVideoURI. But for full screen, // this should be after surface holder created @Override public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { super.prepareDataAndDisplayMode(proxy); setFrameAvailableListener(proxy); } // Pause the play and update the play/pause button @Override public void pauseAndDispatch(HTML5VideoViewProxy proxy) { super.pauseAndDispatch(proxy); mReadyToUseSurfTex = false; } // Inline Video specific FUNCTIONS: @Override public SurfaceTexture getSurfaceTexture() { return mSurfaceTexture; } @Override public void deleteSurfaceTexture() { mSurfaceTexture = null; return; } // SurfaceTexture is a singleton here , too private SurfaceTexture getSurfaceTextureInstance() { // Create the surface texture. if (mSurfaceTexture == null) { mTextureNames = new int[1]; GLES20.glGenTextures(1, mTextureNames, 0); mSurfaceTexture = new SurfaceTexture(mTextureNames[0]); } return mSurfaceTexture; } @Override public int getTextureName() { return mTextureNames[0]; } @Override public boolean getReadyToUseSurfTex() { return mReadyToUseSurfTex; } private void setFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener l) { mSurfaceTexture.setOnFrameAvailableListener(l); } } core/java/android/webkit/HTML5VideoView.java +143 −83 Original line number Diff line number Diff line Loading @@ -4,73 +4,94 @@ package android.webkit; import android.graphics.SurfaceTexture; import android.media.MediaPlayer; import android.util.Log; import android.view.SurfaceView; import android.webkit.HTML5VideoViewProxy; import android.widget.MediaController; import android.opengl.GLES20; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; /** * @hide This is only used by the browser */ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ // Due to the fact that SurfaceTexture consume a lot of memory, we make it // as static. m_textureNames is the texture bound with this SurfaceTexture. private static SurfaceTexture mSurfaceTexture = null; private static int[] mTextureNames; // Only when the video is prepared, we render using SurfaceTexture. // This in fact is used to avoid showing the obsolete content when // switching videos. private static boolean mReadyToUseSurfTex = false; protected static final String LOGTAG = "HTML5VideoView"; protected static final String COOKIE = "Cookie"; protected static final String HIDE_URL_LOGS = "x-hide-urls-from-log"; // For handling the seekTo before prepared, we need to know whether or not // the video is prepared. Therefore, we differentiate the state between // prepared and not prepared. // When the video is not prepared, we will have to save the seekTo time, // and use it when prepared to play. private static final int STATE_NOTPREPARED = 0; private static final int STATE_PREPARED = 1; protected static final int STATE_NOTPREPARED = 0; protected static final int STATE_PREPARED = 1; // We only need state for handling seekTo private int mCurrentState; protected int mCurrentState; // Basically for calling back the OnPrepared in the proxy private HTML5VideoViewProxy mProxy; protected HTML5VideoViewProxy mProxy; // Save the seek time when not prepared. This can happen when switching // video besides initial load. private int mSaveSeekTime; protected int mSaveSeekTime; // This is used to find the VideoLayer on the native side. private int mVideoLayerId; protected int mVideoLayerId; // Every video will have one MediaPlayer. Given the fact we only have one // SurfaceTexture, there is only one MediaPlayer in action. Every time we // switch videos, a new instance of MediaPlayer will be created in reset(). private MediaPlayer mPlayer; // Switching between inline and full screen will also create a new instance. protected MediaPlayer mPlayer; // This will be set up every time we create the Video View object. // Set to true only when switching into full screen while playing protected boolean mAutostart; // We need to save such info. protected String mUri; protected Map<String, String> mHeaders; // The timer for timeupate events. // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate protected static Timer mTimer; private static HTML5VideoView mInstance = new HTML5VideoView(); // The spec says the timer should fire every 250 ms or less. private static final int TIMEUPDATE_PERIOD = 250; // ms // Video control FUNCTIONS: // common Video control FUNCTIONS: public void start() { if (mCurrentState == STATE_PREPARED) { mPlayer.start(); mReadyToUseSurfTex = true; } } public void pause() { if (mCurrentState == STATE_PREPARED && mPlayer.isPlaying()) { mPlayer.pause(); } if (mTimer != null) { mTimer.purge(); } } public int getDuration() { if (mCurrentState == STATE_PREPARED) { return mPlayer.getDuration(); } else { return -1; } } public int getCurrentPosition() { if (mCurrentState == STATE_PREPARED) { return mPlayer.getCurrentPosition(); } return 0; } public void seekTo(int pos) { if (mCurrentState == STATE_PREPARED) Loading @@ -88,54 +109,51 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ } public void stopPlayback() { if (mCurrentState == STATE_PREPARED) { mPlayer.stop(); } } public boolean getAutostart() { return mAutostart; } private void reset(int videoLayerId) { // Every time we start a new Video, we create a VideoView and a MediaPlayer public void init(int videoLayerId, int position, boolean autoStart) { mPlayer = new MediaPlayer(); mCurrentState = STATE_NOTPREPARED; mProxy = null; mVideoLayerId = videoLayerId; mReadyToUseSurfTex = false; mSaveSeekTime = position; mAutostart = autoStart; } public static HTML5VideoView getInstance(int videoLayerId) { // Every time we switch between the videos, a new MediaPlayer will be // created. Make sure we call the m_player.release() when it is done. mInstance.reset(videoLayerId); return mInstance; protected HTML5VideoView() { } private HTML5VideoView() { // This is a singleton across WebViews (i.e. Tabs). // HTML5VideoViewProxy will reset the internal state every time a new // video start. protected static Map<String, String> generateHeaders(String url, HTML5VideoViewProxy proxy) { boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled(); String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate); Map<String, String> headers = new HashMap<String, String>(); if (cookieValue != null) { headers.put(COOKIE, cookieValue); } if (isPrivate) { headers.put(HIDE_URL_LOGS, "true"); } public void setMediaController(MediaController m) { this.setMediaController(m); return headers; } public void setVideoURI(String uri, Map<String, String> headers) { public void setVideoURI(String uri, HTML5VideoViewProxy proxy) { // When switching players, surface texture will be reused. mPlayer.setTexture(getSurfaceTextureInstance()); mUri = uri; mHeaders = generateHeaders(uri, proxy); // When there is exception, we could just bail out silently. // No Video will be played though. Write the stack for debug try { mPlayer.setDataSource(uri, headers); mPlayer.prepareAsync(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } mTimer = new Timer(); } // TODO [FULL SCREEN SUPPORT] // Listeners setup FUNCTIONS: public void setOnCompletionListener(HTML5VideoViewProxy proxy) { mPlayer.setOnCompletionListener(proxy); Loading @@ -150,43 +168,47 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ mPlayer.setOnPreparedListener(this); } // Inline Video specific FUNCTIONS: // Normally called immediately after setVideoURI. But for full screen, // this should be after surface holder created public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { // SurfaceTexture will be created lazily here for inline mode decideDisplayMode(); public SurfaceTexture getSurfaceTexture() { return mSurfaceTexture; } setOnCompletionListener(proxy); setOnPreparedListener(proxy); setOnErrorListener(proxy); public void deleteSurfaceTexture() { mSurfaceTexture = null; return; } // SurfaceTexture is a singleton here , too private SurfaceTexture getSurfaceTextureInstance() { // Create the surface texture. if (mSurfaceTexture == null) { mTextureNames = new int[1]; GLES20.glGenTextures(1, mTextureNames, 0); mSurfaceTexture = new SurfaceTexture(mTextureNames[0]); // When there is exception, we could just bail out silently. // No Video will be played though. Write the stack for debug try { mPlayer.setDataSource(mUri, mHeaders); mPlayer.prepareAsync(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return mSurfaceTexture; } public int getTextureName() { return mTextureNames[0]; } // Common code public int getVideoLayerId() { return mVideoLayerId; } public boolean getReadyToUseSurfTex() { return mReadyToUseSurfTex; private static final class TimeupdateTask extends TimerTask { private HTML5VideoViewProxy mProxy; public TimeupdateTask(HTML5VideoViewProxy proxy) { mProxy = proxy; } public void setFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener l) { mSurfaceTexture.setOnFrameAvailableListener(l); @Override public void run() { mProxy.onTimeupdate(); } } @Override Loading @@ -195,6 +217,9 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ seekTo(mSaveSeekTime); if (mProxy != null) mProxy.onPrepared(mp); mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD); } // Pause the play and update the play/pause button Loading @@ -205,7 +230,42 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ proxy.dispatchOnPaused(); } } mReadyToUseSurfTex = false; } // Below are functions that are different implementation on inline and full- // screen mode. Some are specific to one type, but currently are called // directly from the proxy. public void enterFullScreenVideoState(int layerId, HTML5VideoViewProxy proxy, WebView webView) { } public boolean isFullScreenMode() { return false; } public SurfaceView getSurfaceView() { return null; } public void decideDisplayMode() { } public void prepareForFullScreen() { } public boolean getReadyToUseSurfTex() { return false; } public SurfaceTexture getSurfaceTexture() { return null; } public void deleteSurfaceTexture() { } public int getTextureName() { return 0; } } core/java/android/webkit/HTML5VideoViewProxy.java +51 −77 File changed.Preview size limit exceeded, changes collapsed. Show changes core/java/android/webkit/WebView.java +5 −1 Original line number Diff line number Diff line Loading @@ -7860,7 +7860,11 @@ public class WebView extends AbsoluteLayout case ENTER_FULLSCREEN_VIDEO: int layerId = msg.arg1; Log.v(LOGTAG, "Display the video layer " + layerId + " fullscreen"); String url = (String) msg.obj; if (mHTML5VideoViewProxy != null) { mHTML5VideoViewProxy.enterFullScreenVideo(layerId, url); } break; case SHOW_FULLSCREEN: { Loading Loading
core/java/android/webkit/HTML5VideoFullScreen.java 0 → 100644 +286 −0 Original line number Diff line number Diff line package android.webkit; import android.content.Context; import android.media.MediaPlayer; import android.media.Metadata; import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.webkit.HTML5VideoView; import android.webkit.HTML5VideoViewProxy; import android.widget.FrameLayout; import android.widget.MediaController; import android.widget.MediaController.MediaPlayerControl; /** * @hide This is only used by the browser */ public class HTML5VideoFullScreen extends HTML5VideoView implements MediaPlayerControl, MediaPlayer.OnPreparedListener, View.OnTouchListener { private SurfaceView mSurfaceView; // We need the full screen state to decide which surface to render to and // when to create the MediaPlayer accordingly. static final int FULLSCREEN_OFF = 0; static final int FULLSCREEN_SURFACECREATING = 1; static final int FULLSCREEN_SURFACECREATED = 2; private int mFullScreenMode; // The Media Controller only used for full screen mode private MediaController mMediaController; // SurfaceHolder for full screen private SurfaceHolder mSurfaceHolder = null; // Data only for MediaController private boolean mCanSeekBack; private boolean mCanSeekForward; private boolean mCanPause; private int mCurrentBufferPercentage; // The progress view. private static View mProgressView; // The container for the progress view and video view private static FrameLayout mLayout; SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() { public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { if (mPlayer != null && mMediaController != null && mCurrentState == STATE_PREPARED) { if (mMediaController.isShowing()) { // ensure the controller will get repositioned later mMediaController.hide(); } mMediaController.show(); } } public void surfaceCreated(SurfaceHolder holder) { mSurfaceHolder = holder; mFullScreenMode = FULLSCREEN_SURFACECREATED; prepareForFullScreen(); } public void surfaceDestroyed(SurfaceHolder holder) { // after we return from this we can't use the surface any more mSurfaceHolder = null; // The current Video View will be destroy when we play a new video. } }; public SurfaceView getSurfaceView() { return mSurfaceView; } HTML5VideoFullScreen(Context context, int videoLayerId, int position, boolean autoStart) { mSurfaceView = new SurfaceView(context); mFullScreenMode = FULLSCREEN_OFF; init(videoLayerId, position, autoStart); } private void setMediaController(MediaController m) { mMediaController = m; attachMediaController(); } private void attachMediaController() { if (mPlayer != null && mMediaController != null) { mMediaController.setMediaPlayer(this); mMediaController.setAnchorView(mSurfaceView); //Will be enabled when prepared mMediaController.setEnabled(false); } } @Override public void decideDisplayMode() { mPlayer.setDisplay(mSurfaceHolder); } @Override public void prepareForFullScreen() { // So in full screen, we reset the MediaPlayer mPlayer.reset(); setMediaController(new MediaController(mProxy.getContext())); prepareDataAndDisplayMode(mProxy); } private void toggleMediaControlsVisiblity() { if (mMediaController.isShowing()) { mMediaController.hide(); } else { mMediaController.show(); } } @Override public void onPrepared(MediaPlayer mp) { super.onPrepared(mp); mSurfaceView.setOnTouchListener(this); // Get the capabilities of the player for this stream Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL, MediaPlayer.BYPASS_METADATA_FILTER); if (data != null) { mCanPause = !data.has(Metadata.PAUSE_AVAILABLE) || data.getBoolean(Metadata.PAUSE_AVAILABLE); mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE) || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE); mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE) || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE); } else { mCanPause = mCanSeekBack = mCanSeekForward = true; } // mMediaController status depends on the Metadata result, so put it // after reading the MetaData if (mMediaController != null) { mMediaController.setEnabled(true); // If paused , should show the controller for ever! if (getAutostart()) mMediaController.show(); else mMediaController.show(0); } if (mProgressView != null) { mProgressView.setVisibility(View.GONE); mLayout.removeView(mProgressView); mProgressView = null; } } private final WebChromeClient.CustomViewCallback mCallback = new WebChromeClient.CustomViewCallback() { public void onCustomViewHidden() { // It listens to SurfaceHolder.Callback.SurfaceDestroyed event // which happens when the video view is detached from its parent // view. This happens in the WebChromeClient before this method // is invoked. mTimer.cancel(); mTimer = null; pauseAndDispatch(mProxy); mLayout.removeView(getSurfaceView()); if (mProgressView != null) { mLayout.removeView(mProgressView); mProgressView = null; } mLayout = null; // Re enable plugin views. mProxy.getWebView().getViewManager().showAll(); mProxy = null; } }; @Override public void enterFullScreenVideoState(int layerId, HTML5VideoViewProxy proxy, WebView webView) { mFullScreenMode = FULLSCREEN_SURFACECREATING; mCurrentBufferPercentage = 0; mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); mProxy = proxy; mSurfaceView.getHolder().addCallback(mSHCallback); mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); mSurfaceView.setFocusable(true); mSurfaceView.setFocusableInTouchMode(true); mSurfaceView.requestFocus(); // Create a FrameLayout that will contain the VideoView and the // progress view (if any). mLayout = new FrameLayout(mProxy.getContext()); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER); mLayout.addView(getSurfaceView(), layoutParams); mLayout.setVisibility(View.VISIBLE); WebChromeClient client = webView.getWebChromeClient(); client.onShowCustomView(mLayout, mCallback); // Plugins like Flash will draw over the video so hide // them while we're playing. mProxy.getWebView().getViewManager().hideAll(); mProgressView = client.getVideoLoadingProgressView(); if (mProgressView != null) { mLayout.addView(mProgressView, layoutParams); mProgressView.setVisibility(View.VISIBLE); } } /** * @return true when we are in full screen mode, even the surface not fully * created. */ public boolean isFullScreenMode() { return true; } // MediaController FUNCTIONS: @Override public boolean canPause() { return mCanPause; } @Override public boolean canSeekBackward() { return mCanSeekBack; } @Override public boolean canSeekForward() { return mCanSeekForward; } @Override public int getBufferPercentage() { if (mPlayer != null) { return mCurrentBufferPercentage; } return 0; } // Other listeners functions: private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() { public void onBufferingUpdate(MediaPlayer mp, int percent) { mCurrentBufferPercentage = percent; } }; @Override public boolean onTouch(View v, MotionEvent event) { if (mFullScreenMode >= FULLSCREEN_SURFACECREATED && mMediaController != null) { toggleMediaControlsVisiblity(); } return false; } }
core/java/android/webkit/HTML5VideoInline.java 0 → 100644 +99 −0 Original line number Diff line number Diff line package android.webkit; import android.graphics.SurfaceTexture; import android.media.MediaPlayer; import android.webkit.HTML5VideoView; import android.webkit.HTML5VideoViewProxy; import android.opengl.GLES20; /** * @hide This is only used by the browser */ public class HTML5VideoInline extends HTML5VideoView{ // Due to the fact that SurfaceTexture consume a lot of memory, we make it // as static. m_textureNames is the texture bound with this SurfaceTexture. private static SurfaceTexture mSurfaceTexture = null; private static int[] mTextureNames; // Only when the video is prepared, we render using SurfaceTexture. // This in fact is used to avoid showing the obsolete content when // switching videos. private static boolean mReadyToUseSurfTex = false; // Video control FUNCTIONS: @Override public void start() { super.start(); if (mCurrentState == STATE_PREPARED) { mReadyToUseSurfTex = true; } } HTML5VideoInline(int videoLayerId, int position, boolean autoStart) { init(videoLayerId, position, autoStart); mReadyToUseSurfTex = false; } @Override public void decideDisplayMode() { mPlayer.setTexture(getSurfaceTextureInstance()); } // Normally called immediately after setVideoURI. But for full screen, // this should be after surface holder created @Override public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { super.prepareDataAndDisplayMode(proxy); setFrameAvailableListener(proxy); } // Pause the play and update the play/pause button @Override public void pauseAndDispatch(HTML5VideoViewProxy proxy) { super.pauseAndDispatch(proxy); mReadyToUseSurfTex = false; } // Inline Video specific FUNCTIONS: @Override public SurfaceTexture getSurfaceTexture() { return mSurfaceTexture; } @Override public void deleteSurfaceTexture() { mSurfaceTexture = null; return; } // SurfaceTexture is a singleton here , too private SurfaceTexture getSurfaceTextureInstance() { // Create the surface texture. if (mSurfaceTexture == null) { mTextureNames = new int[1]; GLES20.glGenTextures(1, mTextureNames, 0); mSurfaceTexture = new SurfaceTexture(mTextureNames[0]); } return mSurfaceTexture; } @Override public int getTextureName() { return mTextureNames[0]; } @Override public boolean getReadyToUseSurfTex() { return mReadyToUseSurfTex; } private void setFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener l) { mSurfaceTexture.setOnFrameAvailableListener(l); } }
core/java/android/webkit/HTML5VideoView.java +143 −83 Original line number Diff line number Diff line Loading @@ -4,73 +4,94 @@ package android.webkit; import android.graphics.SurfaceTexture; import android.media.MediaPlayer; import android.util.Log; import android.view.SurfaceView; import android.webkit.HTML5VideoViewProxy; import android.widget.MediaController; import android.opengl.GLES20; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; /** * @hide This is only used by the browser */ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ // Due to the fact that SurfaceTexture consume a lot of memory, we make it // as static. m_textureNames is the texture bound with this SurfaceTexture. private static SurfaceTexture mSurfaceTexture = null; private static int[] mTextureNames; // Only when the video is prepared, we render using SurfaceTexture. // This in fact is used to avoid showing the obsolete content when // switching videos. private static boolean mReadyToUseSurfTex = false; protected static final String LOGTAG = "HTML5VideoView"; protected static final String COOKIE = "Cookie"; protected static final String HIDE_URL_LOGS = "x-hide-urls-from-log"; // For handling the seekTo before prepared, we need to know whether or not // the video is prepared. Therefore, we differentiate the state between // prepared and not prepared. // When the video is not prepared, we will have to save the seekTo time, // and use it when prepared to play. private static final int STATE_NOTPREPARED = 0; private static final int STATE_PREPARED = 1; protected static final int STATE_NOTPREPARED = 0; protected static final int STATE_PREPARED = 1; // We only need state for handling seekTo private int mCurrentState; protected int mCurrentState; // Basically for calling back the OnPrepared in the proxy private HTML5VideoViewProxy mProxy; protected HTML5VideoViewProxy mProxy; // Save the seek time when not prepared. This can happen when switching // video besides initial load. private int mSaveSeekTime; protected int mSaveSeekTime; // This is used to find the VideoLayer on the native side. private int mVideoLayerId; protected int mVideoLayerId; // Every video will have one MediaPlayer. Given the fact we only have one // SurfaceTexture, there is only one MediaPlayer in action. Every time we // switch videos, a new instance of MediaPlayer will be created in reset(). private MediaPlayer mPlayer; // Switching between inline and full screen will also create a new instance. protected MediaPlayer mPlayer; // This will be set up every time we create the Video View object. // Set to true only when switching into full screen while playing protected boolean mAutostart; // We need to save such info. protected String mUri; protected Map<String, String> mHeaders; // The timer for timeupate events. // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate protected static Timer mTimer; private static HTML5VideoView mInstance = new HTML5VideoView(); // The spec says the timer should fire every 250 ms or less. private static final int TIMEUPDATE_PERIOD = 250; // ms // Video control FUNCTIONS: // common Video control FUNCTIONS: public void start() { if (mCurrentState == STATE_PREPARED) { mPlayer.start(); mReadyToUseSurfTex = true; } } public void pause() { if (mCurrentState == STATE_PREPARED && mPlayer.isPlaying()) { mPlayer.pause(); } if (mTimer != null) { mTimer.purge(); } } public int getDuration() { if (mCurrentState == STATE_PREPARED) { return mPlayer.getDuration(); } else { return -1; } } public int getCurrentPosition() { if (mCurrentState == STATE_PREPARED) { return mPlayer.getCurrentPosition(); } return 0; } public void seekTo(int pos) { if (mCurrentState == STATE_PREPARED) Loading @@ -88,54 +109,51 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ } public void stopPlayback() { if (mCurrentState == STATE_PREPARED) { mPlayer.stop(); } } public boolean getAutostart() { return mAutostart; } private void reset(int videoLayerId) { // Every time we start a new Video, we create a VideoView and a MediaPlayer public void init(int videoLayerId, int position, boolean autoStart) { mPlayer = new MediaPlayer(); mCurrentState = STATE_NOTPREPARED; mProxy = null; mVideoLayerId = videoLayerId; mReadyToUseSurfTex = false; mSaveSeekTime = position; mAutostart = autoStart; } public static HTML5VideoView getInstance(int videoLayerId) { // Every time we switch between the videos, a new MediaPlayer will be // created. Make sure we call the m_player.release() when it is done. mInstance.reset(videoLayerId); return mInstance; protected HTML5VideoView() { } private HTML5VideoView() { // This is a singleton across WebViews (i.e. Tabs). // HTML5VideoViewProxy will reset the internal state every time a new // video start. protected static Map<String, String> generateHeaders(String url, HTML5VideoViewProxy proxy) { boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled(); String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate); Map<String, String> headers = new HashMap<String, String>(); if (cookieValue != null) { headers.put(COOKIE, cookieValue); } if (isPrivate) { headers.put(HIDE_URL_LOGS, "true"); } public void setMediaController(MediaController m) { this.setMediaController(m); return headers; } public void setVideoURI(String uri, Map<String, String> headers) { public void setVideoURI(String uri, HTML5VideoViewProxy proxy) { // When switching players, surface texture will be reused. mPlayer.setTexture(getSurfaceTextureInstance()); mUri = uri; mHeaders = generateHeaders(uri, proxy); // When there is exception, we could just bail out silently. // No Video will be played though. Write the stack for debug try { mPlayer.setDataSource(uri, headers); mPlayer.prepareAsync(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } mTimer = new Timer(); } // TODO [FULL SCREEN SUPPORT] // Listeners setup FUNCTIONS: public void setOnCompletionListener(HTML5VideoViewProxy proxy) { mPlayer.setOnCompletionListener(proxy); Loading @@ -150,43 +168,47 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ mPlayer.setOnPreparedListener(this); } // Inline Video specific FUNCTIONS: // Normally called immediately after setVideoURI. But for full screen, // this should be after surface holder created public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { // SurfaceTexture will be created lazily here for inline mode decideDisplayMode(); public SurfaceTexture getSurfaceTexture() { return mSurfaceTexture; } setOnCompletionListener(proxy); setOnPreparedListener(proxy); setOnErrorListener(proxy); public void deleteSurfaceTexture() { mSurfaceTexture = null; return; } // SurfaceTexture is a singleton here , too private SurfaceTexture getSurfaceTextureInstance() { // Create the surface texture. if (mSurfaceTexture == null) { mTextureNames = new int[1]; GLES20.glGenTextures(1, mTextureNames, 0); mSurfaceTexture = new SurfaceTexture(mTextureNames[0]); // When there is exception, we could just bail out silently. // No Video will be played though. Write the stack for debug try { mPlayer.setDataSource(mUri, mHeaders); mPlayer.prepareAsync(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return mSurfaceTexture; } public int getTextureName() { return mTextureNames[0]; } // Common code public int getVideoLayerId() { return mVideoLayerId; } public boolean getReadyToUseSurfTex() { return mReadyToUseSurfTex; private static final class TimeupdateTask extends TimerTask { private HTML5VideoViewProxy mProxy; public TimeupdateTask(HTML5VideoViewProxy proxy) { mProxy = proxy; } public void setFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener l) { mSurfaceTexture.setOnFrameAvailableListener(l); @Override public void run() { mProxy.onTimeupdate(); } } @Override Loading @@ -195,6 +217,9 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ seekTo(mSaveSeekTime); if (mProxy != null) mProxy.onPrepared(mp); mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD); } // Pause the play and update the play/pause button Loading @@ -205,7 +230,42 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ proxy.dispatchOnPaused(); } } mReadyToUseSurfTex = false; } // Below are functions that are different implementation on inline and full- // screen mode. Some are specific to one type, but currently are called // directly from the proxy. public void enterFullScreenVideoState(int layerId, HTML5VideoViewProxy proxy, WebView webView) { } public boolean isFullScreenMode() { return false; } public SurfaceView getSurfaceView() { return null; } public void decideDisplayMode() { } public void prepareForFullScreen() { } public boolean getReadyToUseSurfTex() { return false; } public SurfaceTexture getSurfaceTexture() { return null; } public void deleteSurfaceTexture() { } public int getTextureName() { return 0; } }
core/java/android/webkit/HTML5VideoViewProxy.java +51 −77 File changed.Preview size limit exceeded, changes collapsed. Show changes
core/java/android/webkit/WebView.java +5 −1 Original line number Diff line number Diff line Loading @@ -7860,7 +7860,11 @@ public class WebView extends AbsoluteLayout case ENTER_FULLSCREEN_VIDEO: int layerId = msg.arg1; Log.v(LOGTAG, "Display the video layer " + layerId + " fullscreen"); String url = (String) msg.obj; if (mHTML5VideoViewProxy != null) { mHTML5VideoViewProxy.enterFullScreenVideo(layerId, url); } break; case SHOW_FULLSCREEN: { Loading