package android.webkit; import android.graphics.SurfaceTexture; import android.media.MediaPlayer; import android.util.Log; import android.webkit.HTML5VideoViewProxy; import android.widget.MediaController; import android.opengl.GLES20; import java.io.IOException; import java.util.Map; /** * @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; // 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; // We only need state for handling seekTo private int mCurrentState; // Basically for calling back the OnPrepared in the proxy private HTML5VideoViewProxy mProxy; // Save the seek time when not prepared. This can happen when switching // video besides initial load. private int mSaveSeekTime; // This is used to find the VideoLayer on the native side. private 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; private static HTML5VideoView mInstance = new HTML5VideoView(); // Video control FUNCTIONS: public void start() { if (mCurrentState == STATE_PREPARED) { mPlayer.start(); mReadyToUseSurfTex = true; } } public void pause() { mPlayer.pause(); } public int getDuration() { return mPlayer.getDuration(); } public int getCurrentPosition() { return mPlayer.getCurrentPosition(); } public void seekTo(int pos) { if (mCurrentState == STATE_PREPARED) mPlayer.seekTo(pos); else mSaveSeekTime = pos; } public boolean isPlaying() { return mPlayer.isPlaying(); } public void release() { mPlayer.release(); } public void stopPlayback() { mPlayer.stop(); } private void reset(int videoLayerId) { mPlayer = new MediaPlayer(); mCurrentState = STATE_NOTPREPARED; mProxy = null; mVideoLayerId = videoLayerId; mReadyToUseSurfTex = false; } 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; } private HTML5VideoView() { // This is a singleton across WebViews (i.e. Tabs). // HTML5VideoViewProxy will reset the internal state every time a new // video start. } public void setMediaController(MediaController m) { this.setMediaController(m); } public void setVideoURI(String uri, Map headers) { // When switching players, surface texture will be reused. mPlayer.setTexture(getSurfaceTextureInstance()); // 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(); } } // TODO [FULL SCREEN SUPPORT] // Listeners setup FUNCTIONS: public void setOnCompletionListener(HTML5VideoViewProxy proxy) { mPlayer.setOnCompletionListener(proxy); } public void setOnErrorListener(HTML5VideoViewProxy proxy) { mPlayer.setOnErrorListener(proxy); } public void setOnPreparedListener(HTML5VideoViewProxy proxy) { mProxy = proxy; mPlayer.setOnPreparedListener(this); } // Inline Video specific FUNCTIONS: public SurfaceTexture getSurfaceTexture() { return mSurfaceTexture; } 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; } public int getTextureName() { return mTextureNames[0]; } public int getVideoLayerId() { return mVideoLayerId; } public boolean getReadyToUseSurfTex() { return mReadyToUseSurfTex; } public void setFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener l) { mSurfaceTexture.setOnFrameAvailableListener(l); } @Override public void onPrepared(MediaPlayer mp) { mCurrentState = STATE_PREPARED; seekTo(mSaveSeekTime); if (mProxy != null) mProxy.onPrepared(mp); } // Pause the play and update the play/pause button public void pauseAndDispatch(HTML5VideoViewProxy proxy) { if (isPlaying()) { pause(); if (proxy != null) { proxy.dispatchOnPaused(); } } mReadyToUseSurfTex = false; } }