Java tutorial
/* * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.xnxs.mediaplayer.widget.media; import android.annotation.TargetApi; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.res.Resources; import android.content.res.TypedArray; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.os.Build; import android.support.annotation.NonNull; import android.support.v4.view.GestureDetectorCompat; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; import com.xnxs.mediaplayer.R; import com.xnxs.mediaplayer.configure.Settings; import java.io.IOException; import java.util.Locale; import java.util.Map; import tv.danmaku.ijk.media.player.AndroidMediaPlayer; import tv.danmaku.ijk.media.player.IMediaPlayer; import tv.danmaku.ijk.media.player.IjkMediaPlayer; import tv.danmaku.ijk.media.player.misc.IMediaDataSource; public class VRVideoView extends FrameLayout implements MediaPlayerControl { private String TAG = "IjkVideoView"; // 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_STOP = 5; private static final int STATE_PLAYBACK_COMPLETED = 6; // 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 IRenderView.ISurfaceHolder mSurfaceHolder = null; private IMediaPlayer mMediaPlayer = null; // private int mAudioSession; private int mVideoWidth; private int mVideoHeight; private int mSurfaceWidth; private int mSurfaceHeight; private int mVideoRotationDegree; private IMediaController mMediaController; private IMediaPlayer.OnCompletionListener mOnCompletionListener; private IMediaPlayer.OnPreparedListener mOnPreparedListener; private int mCurrentBufferPercentage; private IMediaPlayer.OnErrorListener mOnErrorListener; private IMediaPlayer.OnInfoListener mOnInfoListener; private int mSeekWhenPrepared; // recording the seek position while preparing private boolean mCanPause = true; private boolean mCanSeekBack = true; private boolean mCanSeekForward = true; private boolean isNeedCreateRender = false; private long mLastProgress = 0; private int showPausePlayDelayTime = 0; //? ?? /** Subtitle rendering widget overlaid on top of the video. */ // private RenderingWidget mSubtitleWidget; private IMediaDataSource mIMediaDataSource; /** * Listener for changes to subtitle data, used to redraw when needed. */ // private RenderingWidget.OnChangedListener mSubtitlesChangedListener; private Context mAppContext; private Settings mSettings; private IRenderView mRenderView; private int mVideoSarNum; private int mVideoSarDen; private int mDisPlayType = MediaPlayerVRControl.VIDEO_TYPE_UNKNOWN; //UI ? private boolean mIsLocked; private AudioManager mAudioManager; private int mAudioMax; private boolean onScrollEnable = false; private MediaPlayerVRControl mediaPlayerVRControl; public VRVideoView(Context context) { this(context, null); } public VRVideoView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public VRVideoView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initVideoView(context, attrs); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public VRVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initVideoView(context, attrs); } // REMOVED: onMeasure // REMOVED: onInitializeAccessibilityEvent // REMOVED: onInitializeAccessibilityNodeInfo // REMOVED: resolveAdjustedSize private void initVideoView(Context context, AttributeSet attrs) { TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.VRVideoView); mCurrentRender = mTypedArray.getInt(R.styleable.VRVideoView_render_type, RENDER_NONE); mAppContext = context; mSettings = new Settings(mAppContext); 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; mIsLocked = false; } public void setRenderView(IRenderView renderView) { if (mRenderView != null) { if (mMediaPlayer != null) mMediaPlayer.setDisplay(null); View renderUIView = mRenderView.getView(); mRenderView.removeRenderCallback(mSHCallback); mRenderView = null; removeView(renderUIView); } if (renderView == null) return; mRenderView = renderView; renderView.setAspectRatio(mCurrentAspectRatio); if (mVideoWidth > 0 && mVideoHeight > 0) renderView.setVideoSize(mVideoWidth, mVideoHeight); if (mVideoSarNum > 0 && mVideoSarDen > 0) renderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen); View renderUIView = mRenderView.getView(); LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER); renderUIView.setLayoutParams(lp); addView(renderUIView); mRenderView.addRenderCallback(mSHCallback); mRenderView.setVideoRotation(mVideoRotationDegree); } public void setRender(int render) { switch (render) { case RENDER_NONE: setRenderView(null); break; case RENDER_TEXTURE_VIEW: { TextureRenderView renderView = new TextureRenderView(mAppContext); if (mMediaPlayer != null) { renderView.getSurfaceHolder().bindToMediaPlayer(mMediaPlayer); renderView.setVideoSize(mMediaPlayer.getVideoWidth(), mMediaPlayer.getVideoHeight()); renderView.setVideoSampleAspectRatio(mMediaPlayer.getVideoSarNum(), mMediaPlayer.getVideoSarDen()); renderView.setAspectRatio(mCurrentAspectRatio); } setRenderView(renderView); break; } case RENDER_SURFACE_VIEW: { SurfaceRenderView renderView = new SurfaceRenderView(mAppContext); setRenderView(renderView); break; } case RENDER_GL_SURFACE_VIEW: { GLSurfaceRenderView renderView = new GLSurfaceRenderView(mAppContext); mediaPlayerVRControl = renderView; attachMediaVrController(); setRenderView(renderView); break; } default: Log.e(TAG, String.format(Locale.getDefault(), "invalid render %d\n", render)); break; } } public void setLastProgress(long lastProgress) { mLastProgress = lastProgress; } /** * Sets video path. * * @param path the path of the video. */ public void setVideoPath(@NonNull 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; if (mMediaPlayer == null) openVideo(); requestLayout(); invalidate(); } // REMOVED: addSubtitleSource // REMOVED: mPendingSubtitleTracks public void stopPlayback() { if (mMediaPlayer != null) { if (mMediaPlayer.isPlaying()) { mMediaPlayer.stop(); } mCurrentState = STATE_STOP; mTargetState = STATE_STOP; } } @TargetApi(Build.VERSION_CODES.M) private void openVideo() { if ((mUri == null && mIMediaDataSource == null) || mSurfaceHolder == 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 = getAudioManager(); am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); try { mMediaPlayer = createPlayer(Settings.PV_PLAYER__IjkMediaPlayer); // TODO: create SubtitleController in MediaPlayer, but we need // a context for the subtitle renderers final Context context = getContext(); // REMOVED: SubtitleController // REMOVED: mAudioSession mMediaPlayer.setOnPreparedListener(mPreparedListener); mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); mMediaPlayer.setOnCompletionListener(mCompletionListener); mMediaPlayer.setOnErrorListener(mErrorListener); mMediaPlayer.setOnInfoListener(mInfoListener); mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); mCurrentBufferPercentage = 0; if (mUri != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders); } else { mMediaPlayer.setDataSource(mUri.toString()); } } else { mMediaPlayer.setDataSource(mIMediaDataSource); } bindSurfaceHolder(mMediaPlayer, mSurfaceHolder); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setScreenOnWhilePlaying(true); 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(IMediaController controller) { if (mMediaController != null) { mMediaController.hide(); } mMediaController = controller; attachMediaController(); attachMediaVrController(); } public void setDataSource(IMediaDataSource iMediaDataSource) { mIMediaDataSource = iMediaDataSource; } //?? private void attachMediaController() { if (mMediaPlayer != null && mMediaController != null) { mMediaController.setMediaPlayer(this); mMediaController.setEnabled(isInPlaybackState()); mMediaController.setShowPausePlayDelayTime(showPausePlayDelayTime); } } private void attachMediaVrController() { if (mediaPlayerVRControl != null && mMediaController != null) { mMediaController.setVRControl(mediaPlayerVRControl); } } 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) { if (mRenderView != null) { mRenderView.setVideoSize(mVideoWidth, mVideoHeight); mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen); } // REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight); requestLayout(); } } }; IMediaPlayer.OnPreparedListener mPreparedListener = new IMediaPlayer.OnPreparedListener() { public void onPrepared(IMediaPlayer mp) { 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(); int 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 (mRenderView != null) { mRenderView.setVideoSize(mVideoWidth, mVideoHeight); mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen); if (!mRenderView.shouldWaitForResize() || 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: mVideoRotationDegree = arg2; Log.d(TAG, "MEDIA_INFO_VIDEO_ROTATION_CHANGED: " + arg2); if (mRenderView != null) mRenderView.setVideoRotation(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; } }; /** * 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 void bindSurfaceHolder(IMediaPlayer mp, IRenderView.ISurfaceHolder holder) { if (mp == null) return; if (holder == null) { mp.setDisplay(null); return; } holder.bindToMediaPlayer(mp); } IRenderView.IRenderCallback mSHCallback = new IRenderView.IRenderCallback() { @Override public void onSurfaceChanged(@NonNull IRenderView.ISurfaceHolder holder, int format, int w, int h) { if (holder.getRenderView() != mRenderView) { Log.e(TAG, "onSurfaceChanged: unmatched render callback\n"); return; } mSurfaceWidth = w; mSurfaceHeight = h; boolean isValidState = (mTargetState == STATE_PLAYING); boolean hasValidSize = !mRenderView.shouldWaitForResize() || (mVideoWidth == w && mVideoHeight == h); if (mMediaPlayer != null && isValidState && hasValidSize) { if (mSeekWhenPrepared != 0) { seekTo(mSeekWhenPrepared); } start(); } } @Override public void onSurfaceCreated(@NonNull IRenderView.ISurfaceHolder holder, int width, int height) { if (holder.getRenderView() != mRenderView) { Log.e(TAG, "onSurfaceCreated: unmatched render callback\n"); return; } mSurfaceHolder = holder; if (mMediaPlayer != null) bindSurfaceHolder(mMediaPlayer, holder); else openVideo(); } @Override public void onSurfaceDestroyed(@NonNull IRenderView.ISurfaceHolder holder) { if (holder.getRenderView() != mRenderView) { Log.e(TAG, "onSurfaceDestroyed: unmatched render callback\n"); return; } // after we return from this we can't use the surface any more mSurfaceHolder = null; isNeedCreateRender = true; // REMOVED: if (mMediaController != null) mMediaController.hide(); // REMOVED: release(true); releaseWithoutStop(); } }; 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 = getAudioManager(); am.abandonAudioFocus(null); } } //Touch Events private float mTouchInvalidHeight = 0; private int mVolume = -1; private long mPlaySeekTime = -1; private long mSeekStartTime = -1; private boolean isPlaySeekEnable = false; private long mSeekSlideTimeLength = 4 * 60 * 1000; //seek 4 private float mBrightness; private boolean mBrightnessAudioVolumeEnable = false; //??? private Window mWindow; private GestureDetectorCompat mDetector; @Override public boolean onTouchEvent(MotionEvent event) { if (isInPlaybackState() && mMediaController != null && !mBrightnessAudioVolumeEnable) { toggleMediaControlsVisiblity(); } if (mIsLocked || !mBrightnessAudioVolumeEnable) { return false; } if (mDetector.onTouchEvent(event)) { return true; } // ?? switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_UP: endGesture(); break; } return super.onTouchEvent(event); } /** * By Lin Date:2016/8/12 15:42 * * @? */ private void endGesture() { mVolume = -1; mBrightness = -1f; mSeekStartTime = -1; if (isPlaySeekEnable) { seekTo((int) mPlaySeekTime); } mPlaySeekTime = 0; isPlaySeekEnable = false; onScrollEnable = false; } /** * By Lin Date:2016/8/12 15:42 * * @? */ public void enableBrightnessAudioVolume(@NonNull Window window) { this.mBrightnessAudioVolumeEnable = true; this.mWindow = window; if (mDetector == null) { mDetector = new GestureDetectorCompat(mAppContext, onGestureListener); mDetector.setOnDoubleTapListener(onGestureListener); } } /** * By Lin Date:2016/8/12 15:41 * * @? */ private GestureDetector.SimpleOnGestureListener onGestureListener = new GestureDetector.SimpleOnGestureListener() { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { float mOldX = e1.getX(), mOldY = e1.getY(); int x = (int) e2.getRawX(); int y = (int) e2.getRawY(); if (Math.abs(distanceX) < Math.abs(distanceY) * 0.36) { // tan(x/y) ?30 if (mOldX > getWidth() * 3.0 / 5.0) {// ? ? 3/4 onVolumeSlide((mOldY - y) / getHeight()); } else if (mOldX < getWidth() * 2.0 / 5.0) {// ? 1/4 onBrightnessSlide((mOldY - y) / getHeight()); } } else if (Math.abs(distanceY) < Math.abs(distanceX) * 0.10) { onSeekSlide((x - mOldX) / getWidth()); } return super.onScroll(e1, e2, distanceX, distanceY); } @Override public boolean onDown(MotionEvent e) { return (e.getY() < getHeight() - mTouchInvalidHeight) && (e.getY() > mTouchInvalidHeight); } @Override public boolean onSingleTapConfirmed(MotionEvent e) { toggleMediaControlsVisiblity(); return super.onSingleTapConfirmed(e); } }; /** * By Lin Date:2016/8/12 15:41 * * @?? */ private void onVolumeSlide(float percent) { if (mVolume == -1 && onScrollEnable) { return; } if (mVolume == -1) { mVolume = getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC); if (mVolume < 0) mVolume = 0; // } int index = (int) (percent * mAudioMax) + mVolume; if (index > mAudioMax) index = mAudioMax; else if (index < 0) index = 0; // ? getAudioManager().setStreamVolume(AudioManager.STREAM_MUSIC, index, 0); //UI if (mMediaController != null) { mMediaController.showVolumeSet(index * 100 / mAudioMax); } onScrollEnable = true; } /** * By Lin Date:2016/8/12 15:41 * * @? */ private void onBrightnessSlide(float percent) { if (mBrightness == -1f && onScrollEnable) { return; } if (mBrightness < 0.00f) { mBrightness = mWindow.getAttributes().screenBrightness; if (mBrightness < 0.00f) mBrightness = 0.60f; if (mBrightness < 0.01f) mBrightness = 0.01f; // } WindowManager.LayoutParams lpa = mWindow.getAttributes(); lpa.screenBrightness = mBrightness + percent; if (lpa.screenBrightness > 1.0f) lpa.screenBrightness = 1.0f; else if (lpa.screenBrightness < 0.01f) lpa.screenBrightness = 0.01f; mWindow.setAttributes(lpa); //UI if (mMediaController != null) { mMediaController.showBrightnessSet(Math.round(lpa.screenBrightness * 100)); } onScrollEnable = true; } /** * By Lin Date:2016/8/12 16:54 * * @ seek */ private void onSeekSlide(float percent) { if (!isInPlaybackState()) { return; } if (mSeekStartTime == -1 && onScrollEnable) { return; } if (mSeekStartTime < 0) { mSeekStartTime = getCurrentPosition(); } isPlaySeekEnable = (percent >= 0 && canSeekForward()) || canSeekBackward(); int duration = getDuration(); if (isPlaySeekEnable) { mPlaySeekTime = (long) (percent * (float) mSeekSlideTimeLength + mSeekStartTime); //seek? if (mPlaySeekTime > duration) { mPlaySeekTime = duration - 2 * 1000; } if (mPlaySeekTime < 0) { mPlaySeekTime = 0; } //UI if (mMediaController != null) { mMediaController.showSeekSet(percent >= 0, (int) mPlaySeekTime); } } onScrollEnable = true; } /** * By Lin Date:2016/8/12 15:45 * * @? */ private void disableBrightnessAudioVolume() { this.mBrightnessAudioVolumeEnable = false; } /** * By Lin Date:2016/8/12 15:46 * * @?? */ private void setTouchInvalidHeight(float touchInvalidHeight) { this.mTouchInvalidHeight = touchInvalidHeight; } @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()) { mCurrentState = STATE_PLAYING; mMediaPlayer.start(); } mTargetState = STATE_PLAYING; } @Override public void pause() { if (isInPlaybackState()) { if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); mCurrentState = STATE_PAUSED; } mTargetState = STATE_PAUSED; } } public void onResume() { resume(); if (mSurfaceHolder != null) { mSurfaceHolder.onResume(); } } public void onPause() { pause(); if (mSurfaceHolder != null) { mSurfaceHolder.onPause(); } } public void onStop() { stopPlayback(); } public void onDestroy() { release(true); } public void suspend() { release(false); } public void resume() { // openVideo(); if (mTargetState == STATE_PAUSED || mTargetState == STATE_STOP) { if (!mMediaPlayer.isPlaying()) { mMediaPlayer.start(); mCurrentState = STATE_PLAYING; } if (isNeedCreateRender) { isNeedCreateRender = false; setRender(mCurrentRender); } mTargetState = STATE_PREPARING; } } @Override public int getDuration() { if (isInPlaybackState()) { return (int) mMediaPlayer.getDuration(); } return -1; } @Override public int getCurrentPosition() { if (isInPlaybackState()) { return (int) mMediaPlayer.getCurrentPosition(); } return 0; } @Override public void seekTo(int msec) { if (isInPlaybackState()) { mMediaPlayer.seekTo(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: Aspect Ratio //------------------------- private static final int[] s_allAspectRatio = { IRenderView.AR_ASPECT_FIT_PARENT, IRenderView.AR_ASPECT_FILL_PARENT, IRenderView.AR_ASPECT_WRAP_CONTENT, // IRenderView.AR_MATCH_PARENT, IRenderView.AR_16_9_FIT_PARENT, IRenderView.AR_4_3_FIT_PARENT }; private int mCurrentAspectRatioIndex = 0; private int mCurrentAspectRatio = s_allAspectRatio[1]; public int toggleAspectRatio() { mCurrentAspectRatioIndex++; mCurrentAspectRatioIndex %= s_allAspectRatio.length; mCurrentAspectRatio = s_allAspectRatio[mCurrentAspectRatioIndex]; if (mRenderView != null) mRenderView.setAspectRatio(mCurrentAspectRatio); return mCurrentAspectRatio; } //------------------------- // Extend: Render //------------------------- public static final int RENDER_NONE = 0; public static final int RENDER_SURFACE_VIEW = 1; public static final int RENDER_TEXTURE_VIEW = 2; public static final int RENDER_GL_SURFACE_VIEW = 3; private int mCurrentRender = RENDER_NONE; private void initRenders() { setRender(mCurrentRender); } public IMediaPlayer createPlayer(int playerType) { IMediaPlayer mediaPlayer = null; switch (playerType) { case Settings.PV_PLAYER__IjkExoMediaPlayer: { //?? AndroidMediaPlayer androidMediaPlayer = new AndroidMediaPlayer(); mediaPlayer = androidMediaPlayer; } break; case Settings.PV_PLAYER__AndroidMediaPlayer: { AndroidMediaPlayer androidMediaPlayer = new AndroidMediaPlayer(); mediaPlayer = androidMediaPlayer; } break; case Settings.PV_PLAYER__IjkMediaPlayer: default: { IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer(); ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 0); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 60); // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "seek-at-start", mLastProgress); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48); // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer"); ? //mLastProgress mediaPlayer = ijkMediaPlayer; showPausePlayDelayTime = 10; } break; } return mediaPlayer; } private AudioManager getAudioManager() { if (mAudioManager == null) { mAudioManager = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE); mAudioMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); } return mAudioManager; } private String buildResolution(int width, int height, int sarNum, int sarDen) { StringBuilder sb = new StringBuilder(); sb.append(width); sb.append(" x "); sb.append(height); if (sarNum > 1 || sarDen > 1) { sb.append("["); sb.append(sarNum); sb.append(":"); sb.append(sarDen); sb.append("]"); } return sb.toString(); } private String buildTimeMilli(long duration) { long total_seconds = duration / 1000; long hours = total_seconds / 3600; long minutes = (total_seconds % 3600) / 60; long seconds = total_seconds % 60; if (duration <= 0) { return "--:--"; } if (hours >= 100) { return String.format(Locale.US, "%d:%02d:%02d", hours, minutes, seconds); } else if (hours > 0) { return String.format(Locale.US, "%02d:%02d:%02d", hours, minutes, seconds); } else { return String.format(Locale.US, "%02d:%02d", minutes, seconds); } } }