package com.wheat7.vrplayer.vr; import android.annotation.TargetApi; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.res.Resources; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.*; import com.wheat7.vrplayer.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Map; import tv.danmaku.ijk.media.player.AbstractMediaPlayer; import tv.danmaku.ijk.media.player.IMediaPlayer; import tv.danmaku.ijk.media.player.IjkMediaPlayer; /** * Created by wheat7 on 28/07/2017. */ public class PlayerView extends FrameLayout implements VRMediaController.MediaPlayerControl { private String TAG = "PlayerView"; // settable by the client private Uri mUri; private Map<String, String> mHeaders; // all possible internal states private static final int STATE_ERROR = -1; private static final int STATE_IDLE = 0; private static final int STATE_PREPARING = 1; private static final int STATE_PREPARED = 2; private static final int STATE_PLAYING = 3; private static final int STATE_PAUSED = 4; private static final int STATE_PLAYBACK_COMPLETED = 5; // mCurrentState is a VideoView object's current state. // mTargetState is the state that a method caller intends to reach. // For instance, regardless the VideoView object's current state, // calling pause() intends to bring the object to a target state // of STATE_PAUSED. private int mCurrentState = STATE_IDLE; private int mTargetState = STATE_IDLE; // All the stuff we need for playing and showing a video //private SurfaceHolder mSurfaceHolder = null; private AbstractMediaPlayer mMediaPlayer = null; // private int mAudioSession; private int mVideoWidth; private int mVideoHeight; private int mSurfaceWidth; private int mSurfaceHeight; private VRMediaController mMediaController; private IMediaPlayer.OnCompletionListener mOnCompletionListener; private IMediaPlayer.OnPreparedListener mOnPreparedListener; private int mCurrentBufferPercentage; private IMediaPlayer.OnErrorListener mOnErrorListener; private IMediaPlayer.OnInfoListener mOnInfoListener; private long mSeekWhenPrepared; // recording the seek position while preparing private boolean mCanPause = true; private boolean mCanSeekBack = true; private boolean mCanSeekForward = true; /** Subtitle rendering widget overlaid on top of the video. */ // private RenderingWidget mSubtitleWidget; /** * Listener for changes to subtitle data, used to redraw when needed. */ // private RenderingWidget.OnChangedListener mSubtitlesChangedListener; private Context mAppContext; private int mVideoSarNum; private int mVideoSarDen; private long mPrepareStartTime = 0; private long mPrepareEndTime = 0; private long mSeekStartTime = 0; private long mSeekEndTime = 0; //private SurfaceView mSurfaceView; public PlayerView(Context context) { super(context); initVideoView(context); } public PlayerView(Context context, AttributeSet attrs) { super(context, attrs); initVideoView(context); } public PlayerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initVideoView(context); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public PlayerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initVideoView(context); } // REMOVED: onMeasure // REMOVED: onInitializeAccessibilityEvent // REMOVED: onInitializeAccessibilityNodeInfo // REMOVED: resolveAdjustedSize private void initVideoView(Context context) { mAppContext = context.getApplicationContext(); //initBackground(); //initRenders(); mVideoWidth = 0; mVideoHeight = 0; // REMOVED: getHolder().addCallback(mSHCallback); // REMOVED: getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); setFocusable(true); setFocusableInTouchMode(true); requestFocus(); // REMOVED: mPendingSubtitleTracks = new Vector<Pair<InputStream, MediaFormat>>(); mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; } /** * Sets video path. * * @param path the path of the video. */ public void setVideoPath(String path) { setVideoURI(Uri.parse(path)); } /** * Sets video URI. * * @param uri the URI of the video. */ public void setVideoURI(Uri uri) { setVideoURI(uri, null); } /** * Sets video URI using specific headers. * * @param uri the URI of the video. * @param headers the headers for the URI request. * Note that the cross domain redirection is allowed by default, but that can be * changed with key/value pairs through the headers parameter with * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value * to disallow or allow cross domain redirection. */ private void setVideoURI(Uri uri, Map<String, String> headers) { mUri = uri; mHeaders = headers; mSeekWhenPrepared = 0; openVideo(); requestLayout(); invalidate(); } // REMOVED: addSubtitleSource // REMOVED: mPendingSubtitleTracks public void stopPlayback() { if (mMediaPlayer != null) { mMediaPlayer.stop(); mMediaPlayer.release(); mMediaPlayer = null; mMediaPlayer = null; mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE); am.abandonAudioFocus(null); } } @TargetApi(Build.VERSION_CODES.M) private void openVideo() { if (mUri == null) { // not ready for playback just yet, will try again later return; } // we shouldn't clear the target state, because somebody might have // called start() previously release(false); AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE); am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); try { mMediaPlayer = new IjkMediaPlayer(); enableHardwareDecoding(); // TODO: create SubtitleController in MediaPlayer, but we need // a context for the subtitle renderers final Context context = getContext(); // REMOVED: SubtitleController // REMOVED: mAudioSession //add if (mSurfaceHolder != null) { mMediaPlayer.setDisplay(mSurfaceHolder); } mMediaPlayer.setOnPreparedListener(mPreparedListener); mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); mMediaPlayer.setOnCompletionListener(mCompletionListener); mMediaPlayer.setOnErrorListener(mErrorListener); mMediaPlayer.setOnInfoListener(mInfoListener); mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener); // mMediaPlayer.setDisplay(mSurfaceHolder); mCurrentBufferPercentage = 0; // String scheme = mUri.getScheme(); // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && // mSettings.getUsingMediaDataSource() && // (TextUtils.isEmpty(scheme) || scheme.equalsIgnoreCase("file"))) { // IMediaDataSource dataSource = new FileMediaDataSource(new File(mUri.toString())); // mMediaPlayer.setDataSource(dataSource); // } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { // mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders); // } else { mMediaPlayer.setDataSource(mUri.toString()); // } mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setScreenOnWhilePlaying(true); mPrepareStartTime = System.currentTimeMillis(); mMediaPlayer.prepareAsync(); // REMOVED: mPendingSubtitleTracks // we don't set the target state here either, but preserve the // target state that was there before. mCurrentState = STATE_PREPARING; attachMediaController(); } catch (IOException ex) { Log.w(TAG, "Unable to open content: " + mUri, ex); mCurrentState = STATE_ERROR; mTargetState = STATE_ERROR; mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); } catch (IllegalArgumentException ex) { Log.w(TAG, "Unable to open content: " + mUri, ex); mCurrentState = STATE_ERROR; mTargetState = STATE_ERROR; mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); } finally { // REMOVED: mPendingSubtitleTracks.clear(); } } public void setMediaController(VRMediaController controller) { if (mMediaController != null) { mMediaController.hide(); } mMediaController = controller; attachMediaController(); } private void attachMediaController() { if (mMediaPlayer != null && mMediaController != null) { mMediaController.setMediaPlayer(this); View anchorView = this.getParent() instanceof View ? (View) this.getParent() : this; mMediaController.setAnchorView(anchorView); mMediaController.setEnabled(isInPlaybackState()); } } IMediaPlayer.OnVideoSizeChangedListener mSizeChangedListener = new IMediaPlayer.OnVideoSizeChangedListener() { public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sarNum, int sarDen) { mVideoWidth = mp.getVideoWidth(); mVideoHeight = mp.getVideoHeight(); mVideoSarNum = mp.getVideoSarNum(); mVideoSarDen = mp.getVideoSarDen(); if (mVideoWidth != 0 && mVideoHeight != 0) { // REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight); requestLayout(); } } }; IMediaPlayer.OnPreparedListener mPreparedListener = new IMediaPlayer.OnPreparedListener() { public void onPrepared(IMediaPlayer mp) { mPrepareEndTime = System.currentTimeMillis(); mCurrentState = STATE_PREPARED; // Get the capabilities of the player for this stream // REMOVED: Metadata if (mOnPreparedListener != null) { mOnPreparedListener.onPrepared(mMediaPlayer); } if (mMediaController != null) { mMediaController.setEnabled(true); } mVideoWidth = mp.getVideoWidth(); mVideoHeight = mp.getVideoHeight(); long seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call if (seekToPosition != 0) { seekTo(seekToPosition); } if (mVideoWidth != 0 && mVideoHeight != 0) { //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight); // REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight); if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) { // We didn't actually change the size (it was already at the size // we need), so we won't get a "surface changed" callback, so // start the video here instead of in the callback. if (mTargetState == STATE_PLAYING) { start(); if (mMediaController != null) { mMediaController.show(); } } else if (!isPlaying() && (seekToPosition != 0 || getCurrentPosition() > 0)) { if (mMediaController != null) { // Show the media controls when we're paused into a video and make 'em stick. mMediaController.show(0); } } } } else { // We don't know the video size yet, but should start anyway. // The video size might be reported to us later. if (mTargetState == STATE_PLAYING) { start(); } } } }; private IMediaPlayer.OnCompletionListener mCompletionListener = new IMediaPlayer.OnCompletionListener() { public void onCompletion(IMediaPlayer mp) { mCurrentState = STATE_PLAYBACK_COMPLETED; mTargetState = STATE_PLAYBACK_COMPLETED; if (mMediaController != null) { mMediaController.hide(); } if (mOnCompletionListener != null) { mOnCompletionListener.onCompletion(mMediaPlayer); } } }; private IMediaPlayer.OnInfoListener mInfoListener = new IMediaPlayer.OnInfoListener() { public boolean onInfo(IMediaPlayer mp, int arg1, int arg2) { if (mOnInfoListener != null) { mOnInfoListener.onInfo(mp, arg1, arg2); } switch (arg1) { case IMediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING: Log.d(TAG, "MEDIA_INFO_VIDEO_TRACK_LAGGING:"); break; case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START: Log.d(TAG, "MEDIA_INFO_VIDEO_RENDERING_START:"); break; case IMediaPlayer.MEDIA_INFO_BUFFERING_START: Log.d(TAG, "MEDIA_INFO_BUFFERING_START:"); break; case IMediaPlayer.MEDIA_INFO_BUFFERING_END: Log.d(TAG, "MEDIA_INFO_BUFFERING_END:"); break; case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH: Log.d(TAG, "MEDIA_INFO_NETWORK_BANDWIDTH: " + arg2); break; case IMediaPlayer.MEDIA_INFO_BAD_INTERLEAVING: Log.d(TAG, "MEDIA_INFO_BAD_INTERLEAVING:"); break; case IMediaPlayer.MEDIA_INFO_NOT_SEEKABLE: Log.d(TAG, "MEDIA_INFO_NOT_SEEKABLE:"); break; case IMediaPlayer.MEDIA_INFO_METADATA_UPDATE: Log.d(TAG, "MEDIA_INFO_METADATA_UPDATE:"); break; case IMediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE: Log.d(TAG, "MEDIA_INFO_UNSUPPORTED_SUBTITLE:"); break; case IMediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT: Log.d(TAG, "MEDIA_INFO_SUBTITLE_TIMED_OUT:"); break; case IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED: Log.d(TAG, "MEDIA_INFO_VIDEO_ROTATION_CHANGED: " + arg2); break; case IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START: Log.d(TAG, "MEDIA_INFO_AUDIO_RENDERING_START:"); break; } return true; } }; private IMediaPlayer.OnErrorListener mErrorListener = new IMediaPlayer.OnErrorListener() { public boolean onError(IMediaPlayer mp, int framework_err, int impl_err) { Log.d(TAG, "Error: " + framework_err + "," + impl_err); mCurrentState = STATE_ERROR; mTargetState = STATE_ERROR; if (mMediaController != null) { mMediaController.hide(); } /* If an error handler has been supplied, use it and finish. */ if (mOnErrorListener != null) { if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) { return true; } } /* Otherwise, pop up an error dialog so the user knows that * something bad has happened. Only try and pop up the dialog * if we're attached to a window. When we're going away and no * longer have a window, don't bother showing the user an error. */ if (getWindowToken() != null) { Resources r = mAppContext.getResources(); int messageId; if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) { messageId = R.string.VideoView_error_text_invalid_progressive_playback; } else { messageId = R.string.VideoView_error_text_unknown; } new AlertDialog.Builder(getContext()) .setMessage(messageId) .setPositiveButton(R.string.VideoView_error_button, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { /* If we get here, there is no onError listener, so * at least inform them that the video is over. */ if (mOnCompletionListener != null) { mOnCompletionListener.onCompletion(mMediaPlayer); } } }) .setCancelable(false) .show(); } return true; } }; private IMediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new IMediaPlayer.OnBufferingUpdateListener() { public void onBufferingUpdate(IMediaPlayer mp, int percent) { mCurrentBufferPercentage = percent; } }; private IMediaPlayer.OnSeekCompleteListener mSeekCompleteListener = new IMediaPlayer.OnSeekCompleteListener() { @Override public void onSeekComplete(IMediaPlayer mp) { mSeekEndTime = System.currentTimeMillis(); } }; /** * Register a callback to be invoked when the media file * is loaded and ready to go. * * @param l The callback that will be run */ public void setOnPreparedListener(IMediaPlayer.OnPreparedListener l) { mOnPreparedListener = l; } /** * Register a callback to be invoked when the end of a media file * has been reached during playback. * * @param l The callback that will be run */ public void setOnCompletionListener(IMediaPlayer.OnCompletionListener l) { mOnCompletionListener = l; } /** * Register a callback to be invoked when an error occurs * during playback or setup. If no listener is specified, * or if the listener returned false, VideoView will inform * the user of any errors. * * @param l The callback that will be run */ public void setOnErrorListener(IMediaPlayer.OnErrorListener l) { mOnErrorListener = l; } /** * Register a callback to be invoked when an informational event * occurs during playback or setup. * * @param l The callback that will be run */ public void setOnInfoListener(IMediaPlayer.OnInfoListener l) { mOnInfoListener = l; } // REMOVED: mSHCallback // private class SurfaceCallBack implements SurfaceHolder.Callback { // @Override // public void surfaceCreated(SurfaceHolder holder) { // mSurfaceHolder = holder; // openVideo(); // //mMediaPlayer.setDisplay(mSurfaceView.getHolder()); // } // // @Override // public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // mSurfaceWidth = width; // mSurfaceHeight = height; // boolean isValidState = (mTargetState == STATE_PLAYING); // boolean hasValidSize = (mVideoWidth == width && mVideoHeight == height); // if (mMediaPlayer != null && isValidState && hasValidSize) { // if (mSeekWhenPrepared != 0) { // seekTo(mSeekWhenPrepared); // } // start(); // } // } // // @Override // public void surfaceDestroyed(SurfaceHolder holder) { // // } // } public void releaseWithoutStop() { if (mMediaPlayer != null) mMediaPlayer.setDisplay(null); } /* * release the media player in any state */ public void release(boolean cleartargetstate) { if (mMediaPlayer != null) { mMediaPlayer.reset(); mMediaPlayer.release(); mMediaPlayer = null; // REMOVED: mPendingSubtitleTracks.clear(); mCurrentState = STATE_IDLE; if (cleartargetstate) { mTargetState = STATE_IDLE; } AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE); am.abandonAudioFocus(null); } } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: if (isInPlaybackState() && mMediaController != null) { toggleMediaControlsVisiblity(); } } return false; } @Override public boolean onTrackballEvent(MotionEvent ev) { if (isInPlaybackState() && mMediaController != null) { toggleMediaControlsVisiblity(); } return false; } @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_VOLUME_MUTE && keyCode != KeyEvent.KEYCODE_MENU && keyCode != KeyEvent.KEYCODE_CALL && keyCode != KeyEvent.KEYCODE_ENDCALL; if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) { if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { if (mMediaPlayer.isPlaying()) { pause(); mMediaController.show(); } else { start(); mMediaController.hide(); } return true; } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { if (!mMediaPlayer.isPlaying()) { start(); mMediaController.hide(); } return true; } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { if (mMediaPlayer.isPlaying()) { pause(); mMediaController.show(); } return true; } else { toggleMediaControlsVisiblity(); } } return super.onKeyDown(keyCode, event); } private void toggleMediaControlsVisiblity() { if (mMediaController.isShowing()) { mMediaController.hide(); } else { mMediaController.show(); } } @Override public void start() { if (isInPlaybackState()) { mMediaPlayer.start(); mCurrentState = STATE_PLAYING; } mTargetState = STATE_PLAYING; } @Override public void pause() { if (isInPlaybackState()) { if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); mCurrentState = STATE_PAUSED; } } mTargetState = STATE_PAUSED; } public void suspend() { release(false); } public void resume() { openVideo(); } @Override public long getDuration() { if (isInPlaybackState()) { return (int) mMediaPlayer.getDuration(); } return -1; } @Override public long getCurrentPosition() { if (isInPlaybackState()) { return (int) mMediaPlayer.getCurrentPosition(); } return 0; } @Override public void seekTo(long msec) { if (isInPlaybackState()) { mSeekStartTime = System.currentTimeMillis(); mMediaPlayer.seekTo((int) msec); mSeekWhenPrepared = 0; } else { mSeekWhenPrepared = msec; } } @Override public boolean isPlaying() { return isInPlaybackState() && mMediaPlayer.isPlaying(); } @Override public int getBufferPercentage() { if (mMediaPlayer != null) { return mCurrentBufferPercentage; } return 0; } private boolean isInPlaybackState() { return (mMediaPlayer != null && mCurrentState != STATE_ERROR && mCurrentState != STATE_IDLE && mCurrentState != STATE_PREPARING); } // @Override // public boolean canPause() { // return mCanPause; // } // // @Override // public boolean canSeekBackward() { // return mCanSeekBack; // } // // @Override // public boolean canSeekForward() { // return mCanSeekForward; // } // // @Override // public int getAudioSessionId() { // return 0; // } // REMOVED: getAudioSessionId(); // REMOVED: onAttachedToWindow(); // REMOVED: onDetachedFromWindow(); // REMOVED: onLayout(); // REMOVED: draw(); // REMOVED: measureAndLayoutSubtitleWidget(); // REMOVED: setSubtitleWidget(); // REMOVED: getSubtitleLooper(); /* ---------------------------------------Extend —————————————————————————————————————————————— */ //getPlayer public AbstractMediaPlayer getPlayer() { return mMediaPlayer; } // enableHardwareDecoding private void enableHardwareDecoding() { if (mMediaPlayer instanceof IjkMediaPlayer) { IjkMediaPlayer player = (IjkMediaPlayer) mMediaPlayer; player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1); player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1); player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32); player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 60); player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-fps", 0); player.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48); } } public boolean isSupportMediaCodecHardDecoder() { boolean isHardcode = false; //读取系统配置文件/system/etc/media_codecc.xml File file = new File("/system/etc/media_codecsPlayerView.xml"); InputStream inFile = null; try { inFile = new FileInputStream(file); } catch (Exception e) { // TODO: handle exception } if (inFile != null) { XmlPullParserFactory pullFactory; try { pullFactory = XmlPullParserFactory.newInstance(); XmlPullParser xmlPullParser = pullFactory.newPullParser(); xmlPullParser.setInput(inFile, "UTF-8"); int eventType = xmlPullParser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { String tagName = xmlPullParser.getName(); switch (eventType) { case XmlPullParser.START_TAG: if ("MediaCodec".equals(tagName)) { String componentName = xmlPullParser.getAttributeValue(0); if (componentName.startsWith("OMX.")) { if (!componentName.startsWith("OMX.google.")) { isHardcode = true; } } } } eventType = xmlPullParser.next(); } } catch (Exception e) { // TODO: handle exception } } return isHardcode; } private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; //setSurfaceView private void setSurfaceView(SurfaceView surfaceView) { mSurfaceView = surfaceView; mSurfaceView.getHolder().addCallback(new SurfaceCallBack()); } public class SurfaceCallBack implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder holder) { mSurfaceHolder = holder; openVideo(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { { mSurfaceWidth = width; mSurfaceHeight = height; boolean isValidState = (mTargetState == STATE_PLAYING); boolean hasValidSize = (mVideoWidth == width && mVideoHeight == height); if (mMediaPlayer != null && isValidState && hasValidSize) { if (mSeekWhenPrepared != 0) { seekTo(mSeekWhenPrepared); } start(); } } } @Override public void surfaceDestroyed(SurfaceHolder holder) { // after we return from this we can't use the surface any more mSurfaceHolder = null; if (mMediaController != null) mMediaController.hide(); release(true); } } }