package com.media.ffmpeg; import java.io.IOException; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.graphics.PixelFormat; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnBufferingUpdateListener; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; import android.media.MediaPlayer.OnSeekCompleteListener; import android.net.Uri; import android.opengl.GLSurfaceView; import android.util.AttributeSet; //import android.util.Log; import android.view.KeyEvent; import android.view.SurfaceHolder; import android.view.View; import android.widget.MediaController; import android.widget.RelativeLayout.LayoutParams; public class TSVideoView extends GLSurfaceView implements MediaController.MediaPlayerControl { private static final String TAG = "TSVideoView"; public static final int STATE_ERROR = -1; public static final int STATE_IDLE = 0; public static final int STATE_PREPARING = 1; public static final int STATE_PREPARED = 2; public static final int STATE_PLAYING = 3; public static final int STATE_PAUSED = 4; public static final int STATE_PLAYBACK_COMPLETED = 5; public static final int STATE_STOPBACK = 6; public static final int STATE_ENFORCEMENT = 7; private int mCurrentState = STATE_IDLE; private int mTargetState = STATE_IDLE; private final int FORWARD_TIME = 15000 ; private final int REWIND_TIME = 15000 ; // All the stuff we need for playing and showing a video private SurfaceHolder mSurfaceHolder = null; private FFMpegPlayer mMediaPlayer = null; private Context mContext; private int mVideoWidth; private int mVideoHeight; private int mSurfaceWidth; private int mSurfaceHeight; private OnCompletionListener mOnCompletionListener; private OnPreparedListener mOnPreparedListener; private int mCurrentBufferPercentage; private OnErrorListener mOnErrorListener; private OnBufferingUpdateListener mOnBufferingUpdateListener; private OnSeekCompleteListener mOnSeekCompleteListener; private TSVideoViewStateChangeListener mStateChangeListener; private boolean fullScreen; private int mSeekWhenPrepared; // recording the seek position while // preparing private boolean mCanPause; private boolean mCanSeekBack; private boolean mCanSeekForward; private boolean firstPlay = true;// 当前VideoView所代表的播放器首次进行播放 private String mLastUrl; private String mVersion; private Uri mUri; private int mDuration; /** * 记录消毁前的时间点 * */ protected int lastSeekWhenPrepared = 0 ; /** * 强制等待,无法播放 * */ private boolean enforcementWait = false ; /** * 强制暂停 * */ private boolean enforcementPause = false ; public TSVideoView(Context context) { super(context); this.mContext = context; initVideoView(); } public TSVideoView(Context context, AttributeSet attrs) { super(context, attrs); this.mContext = context; initVideoView(); } private void initVideoView() { mVideoWidth = 0; mVideoHeight = 0; setEGLConfigChooser(8, 8, 8, 8, 16, 0); setRenderer(new MyRenderer()); this.onPause(); getHolder().addCallback(mSHCallback); getHolder().setFormat(PixelFormat.TRANSLUCENT); setFocusable(true); setFocusableInTouchMode(true); requestFocus(); mCurrentState = STATE_IDLE; StateChange(mCurrentState); mTargetState = STATE_IDLE; } public void setVideoPath(String videoPath) { setVideoURI(Uri.parse(videoPath)); } public void setVideoURI(Uri uri) { mUri = uri; // Log.i("chenyg", "TSVideoView: uri=" + mUri.toString()); mSeekWhenPrepared = 0; openVideo(); requestLayout(); invalidate(); } /** * mediaControl 底层方法必须实现 */ @Override public boolean canPause() { return mCanPause; } @Override public boolean canSeekBackward() { return mCanSeekBack; } @Override public boolean canSeekForward() { return mCanSeekForward; } @Override public int getBufferPercentage() { if (mMediaPlayer != null) { return mCurrentBufferPercentage; } return 0; } @Override public int getCurrentPosition() { if (isInPlaybackState()) { return mMediaPlayer.getCurrentPosition(); } return 0; } @Override public int getDuration() { if (isInPlaybackState()) { if (mDuration > 0) { return mDuration; } mDuration = mMediaPlayer.getDuration(); return mDuration; } mDuration = -1; return mDuration; } public View getVideoView() { return this; } public String getSkipLastURL() { return mLastUrl; } public String getVersion() { return mVersion; } public int getViewWidth() { return getLayoutParams().width; } public int getViewHeight() { return getLayoutParams().height; } @Override public void start() { if(!enforcementWait && !enforcementPause){ if (isInPlaybackState()) { // setVisibility(View.VISIBLE); mMediaPlayer.start(); mCurrentState = STATE_PLAYING; StateChange(mCurrentState); } } mTargetState = STATE_PLAYING; StateChange(STATE_ENFORCEMENT); } @Override public void pause() { if (isInPlaybackState()) { if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); mCurrentState = STATE_PAUSED; StateChange(mCurrentState); } } mTargetState = STATE_PAUSED; } @Override public void seekTo(int mesc) { if(mesc < 1000){ mesc = 1000 ; } if (isInPlaybackState()) { mMediaPlayer.seekTo(mesc); mSeekWhenPrepared = 0; lastSeekWhenPrepared = 0; } else { mSeekWhenPrepared = mesc; lastSeekWhenPrepared = 0; } } /** * 固定快进 * */ public void forward() { seekTo(getCurrentPosition() + FORWARD_TIME) ; } /** * 固定快退 * */ public void rewind() { seekTo(getCurrentPosition() - REWIND_TIME) ; } public void stopPlayback(boolean isRemoveCallBack) { StateChange(STATE_STOPBACK);//为统计加入 if (mMediaPlayer != null) { mMediaPlayer.stop(); if (isRemoveCallBack) { getHolder().removeCallback(mSHCallback); } mMediaPlayer.release(); mMediaPlayer = null; mCurrentState = STATE_IDLE; StateChange(mCurrentState); mTargetState = STATE_IDLE; setVisibility(INVISIBLE); } // Log.i(TAG, "stopPlayback()"); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = getDefaultSize(mVideoWidth, widthMeasureSpec); int height = getDefaultSize(mVideoHeight, heightMeasureSpec); if (!fullScreen) { if (mVideoWidth > 0 && mVideoHeight > 0) { if (mVideoWidth * height > width * mVideoHeight) { height = width * mVideoHeight / mVideoWidth; } else if (mVideoWidth * height < width * mVideoHeight) { width = height * mVideoWidth / mVideoHeight; } else { } } } setMeasuredDimension(width, height); } private void invalateScreenSize() { fullScreen = !fullScreen; LayoutParams lp = (LayoutParams) this.getLayoutParams(); this.setLayoutParams(lp); } public int resolveAdjustedSize(int desiredSize, int measureSpec) { int result = desiredSize; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: /* * Parent says we can be as big as we want. Just don't be larger * than max size imposed on ourselves. */ result = desiredSize; break; case MeasureSpec.AT_MOST: /* * Parent says we can be as big as we want, up to specSize. Don't be * larger than specSize, and don't be larger than the max size * imposed on ourselves. */ result = Math.min(desiredSize, specSize); break; case MeasureSpec.EXACTLY: // No choice. Do what we are told. result = specSize; break; } return result; } SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() { public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // Log.i(TAG, "surfaceChanged()"); mSurfaceWidth = w; mSurfaceHeight = h; boolean isValidState = (mTargetState == STATE_PLAYING); boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h); if (mMediaPlayer != null && isValidState && hasValidSize) { if (mSeekWhenPrepared != 0) { seekTo(mSeekWhenPrepared); } start(); } } public void surfaceCreated(SurfaceHolder holder) { // Log.i(TAG, "surfaceCreated"); if (mSurfaceHolder == null) { mSurfaceHolder = holder; openVideo(); } } public void surfaceDestroyed(SurfaceHolder holder) { // Log.i(TAG, "surfaceDestroyed()"); mSurfaceHolder = null; lastSeekWhenPrepared = getCurrentPosition() ; release(true); } }; public boolean isInPlaybackState() { return (mMediaPlayer != null && mCurrentState != STATE_ERROR && mCurrentState != STATE_IDLE && mCurrentState != STATE_PREPARING); } private void openVideo() { // Log.i(TAG, "openVideo"); if (mUri == null || mSurfaceHolder == null) { setVisibility(VISIBLE); return; } release(false); try { mMediaPlayer = new FFMpegPlayer(); mMediaPlayer.setHardwareDecode(FFMpegPlayer.SOFTWARE_DECODE);//sunyuanzeng onResume(); mMediaPlayer.setOnPreparedListener(mPreparedListener); mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); mDuration = -1; mMediaPlayer.setOnCompletionListener(mCompletionListener); mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); mMediaPlayer.setOnErrorListener(mErrorListener); mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener); mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); mMediaPlayer.setRenderControler(mGLRenderControler); mCurrentBufferPercentage = 0; mMediaPlayer.setDataSource(mContext, mUri); //mMediaPlayer.setDataSource(mContext, Uri.parse("http://cache.video.qiyi.com/vd/334053/cfce4ed87b3a422ab4f400dbe36a9b01/")); mMediaPlayer.setDisplay(mSurfaceHolder); // mMediaPlayer.setDecoderSurface(mSurfaceHolder.getSurface()); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setScreenOnWhilePlaying(true); mMediaPlayer.prepareAsync(); mCurrentState = STATE_PREPARING; } catch (IllegalArgumentException ex) { // Log.w(TAG, "Unable to open content: " + mUri, ex); mCurrentState = STATE_ERROR; StateChange(mCurrentState); mTargetState = STATE_ERROR; mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); return; } catch (IOException e) { // Log.w(TAG, "Unable to open content: " + mUri, e); mCurrentState = STATE_ERROR; StateChange(mCurrentState); mTargetState = STATE_ERROR; mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); } } // 设置播放器控件的大小 private void setVideoViewScale(int width, int height) { LayoutParams lp = (LayoutParams) this.getLayoutParams(); lp.height = height; lp.width = width; setLayoutParams(lp); } public boolean toggleScreen() { invalateScreenSize(); return fullScreen; } private void release(boolean cleartargetstate) { // Log.i(TAG, "release()"); if (mMediaPlayer != null) { mMediaPlayer.stop(); mMediaPlayer.release(); mMediaPlayer = null; mCurrentState = STATE_IDLE; StateChange(mCurrentState); if (cleartargetstate) { mTargetState = STATE_IDLE; } } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK && keyCode != KeyEvent.KEYCODE_VOLUME_UP && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_MENU && keyCode != KeyEvent.KEYCODE_CALL && keyCode != KeyEvent.KEYCODE_ENDCALL; if (isInPlaybackState() && isKeyCodeSupported) { if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { if (mMediaPlayer.isPlaying()) { pause(); } else { start(); } return true; } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP && mMediaPlayer.isPlaying()) { pause(); } else { toggleMediaControlsVisiblity(); } } return super.onKeyDown(keyCode, event); } private void toggleMediaControlsVisiblity() { } @Override public boolean isPlaying() { return isInPlaybackState() && mMediaPlayer.isPlaying(); } public boolean isPaused() { return mCurrentState != STATE_PAUSED; } MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener = new MediaPlayer.OnVideoSizeChangedListener() { public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { mVideoWidth = mp.getVideoWidth(); mVideoHeight = mp.getVideoHeight(); if (mVideoWidth != 0 && mVideoHeight != 0) { getHolder().setFixedSize(mVideoWidth, mVideoHeight); } } }; MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() { public void onPrepared(MediaPlayer mp) { mCurrentState = STATE_PREPARED; StateChange(mCurrentState); mCanPause = mCanSeekBack = mCanSeekForward = true; if (mOnPreparedListener != null) { mOnPreparedListener.onPrepared(mMediaPlayer); } mLastUrl = ((FFMpegPlayer) mp).getLastUrl(); mVersion = ((FFMpegPlayer) mp).getVersion(); int seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be if (seekToPosition != 0) { seekTo(seekToPosition); } mVideoWidth = mp.getVideoWidth(); mVideoHeight = mp.getVideoHeight(); if (mVideoWidth != 0 && mVideoHeight != 0) { if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) { if (mTargetState == STATE_PLAYING) { start(); } } else { getHolder().setFixedSize(mVideoWidth, mVideoHeight); } } else { if (mTargetState == STATE_PLAYING) { start(); } } } }; private MediaPlayer.OnCompletionListener mCompletionListener = new MediaPlayer.OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mCurrentState = STATE_PLAYBACK_COMPLETED; StateChange(mCurrentState); mTargetState = STATE_PLAYBACK_COMPLETED; mCurrentState = STATE_STOPBACK; StateChange(mCurrentState);//未统计加入 if (mOnCompletionListener != null) { mOnCompletionListener.onCompletion(mMediaPlayer); } /** * 播放完成,停止并释放资源 */ TSVideoView.this.pause(); TSVideoView.this.release(true); } }; private MediaPlayer.OnErrorListener mErrorListener = new MediaPlayer.OnErrorListener() { public boolean onError(MediaPlayer mp, int framework_err, int impl_err) { // Log.d(TAG, "Error: " + framework_err + "," + impl_err); mCurrentState = STATE_ERROR; StateChange(mCurrentState); mTargetState = STATE_ERROR; /* If an error handler has been supplied, use it and finish. */ if (mOnErrorListener != null) { if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) { return true; } } if (getWindowToken() != null) { } return true; } }; private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() { public void onBufferingUpdate(MediaPlayer mp, int percent) { mCurrentBufferPercentage = percent; if (mOnBufferingUpdateListener != null) mOnBufferingUpdateListener.onBufferingUpdate(mp, percent); } }; private MediaPlayer.OnSeekCompleteListener mSeekCompleteListener = new MediaPlayer.OnSeekCompleteListener() { public void onSeekComplete(MediaPlayer mp) { if (mOnSeekCompleteListener != null) { mOnSeekCompleteListener.onSeekComplete(mMediaPlayer); } // resume(); } }; public void setOnBufferingUpdateListener(OnBufferingUpdateListener l) { mOnBufferingUpdateListener = l; } public void setOnPreparedListener(OnPreparedListener mOnPreparedListener) { this.mOnPreparedListener = mOnPreparedListener; } public void setOnCompletionListener(OnCompletionListener mOnCompletionListener) { this.mOnCompletionListener = mOnCompletionListener; } public void setOnErrorListener(OnErrorListener mOnErrorListener) { this.mOnErrorListener = mOnErrorListener; } private FFMpegPlayer.GLRenderControler mGLRenderControler = new FFMpegPlayer.GLRenderControler() { public void setGLStartRenderMode() { setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); } public void setGLStopRenderMode() { setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); } }; public void setOnSeekCompleteListener(OnSeekCompleteListener mSeekCompleteListener) { this.mOnSeekCompleteListener = mSeekCompleteListener; } class MyRenderer implements GLSurfaceView.Renderer { public void onSurfaceCreated(GL10 gl, EGLConfig c) { // Log.i(TAG, "myRenderer"); } public void onSurfaceChanged(GL10 gl, int w, int h) { mSurfaceHeight = h; mSurfaceWidth = w; if (mMediaPlayer != null) { if ((lastW != w) || (lastH != h)) { gl.glViewport(0, 0, w, h); mMediaPlayer.native_gl_resize(w, h); lastW = w; lastH = h; // gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); } } } public void onDrawFrame(GL10 gl) { try { if (mMediaPlayer != null && mMediaPlayer.isPlaying()) { mMediaPlayer.native_gl_render(); } } catch (Exception e) { e.printStackTrace(); } } public long time = 0; public int lastW = 0; public int lastH = 0; public short framerate = 0; public long fpsTime = 0; public long frameTime = 0; public float avgFPS = 0; } public int getLastSeekWhenPrepared() { return lastSeekWhenPrepared; } public int getAudioSessionId() { return 0; } private void StateChange(int mCurrentState){ if(mStateChangeListener != null){ mStateChangeListener.onChange(mCurrentState) ; } } public TSVideoViewStateChangeListener getStateChangeListener() { return mStateChangeListener; } public void setStateChangeListener(TSVideoViewStateChangeListener listener) { this.mStateChangeListener = listener; } public interface TSVideoViewStateChangeListener{ public void onChange(int mCurrentState); } public boolean isEnforcementWait() { return enforcementWait; } public void setEnforcementWait(boolean enforcementWait) { this.enforcementWait = enforcementWait; } public boolean isEnforcementPause() { return enforcementPause; } public void setEnforcementPause(boolean enforcementPause) { this.enforcementPause = enforcementPause; } }