首先工程内集成这几个玩意
然后在model的gradle里边加点东西
apply plugin: 'com.android.application' android { compileSdkVersion 25 defaultConfig { applicationId "com.example.weekthreetest" minSdkVersion 14 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets { main { jniLibs.srcDirs = ['libs'] } } } repositories { mavenCentral() flatDir { dirs 'libs' //this way we can find the .aar file in libs folder } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) implementation 'com.android.support:appcompat-v7:25.+' implementation 'com.android.support.constraint:constraint-layout:1.0.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:0.5' androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2' compile(name: 'ijkplayer-java-release', ext: 'aar') }
从这里开始到下边的---------------------------------------------------------------------------------------------
/* * Copyright (C) 2015 Zhang Rui* * 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.example.weekthreetest.widget.media; import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.ActionBar; import android.util.AttributeSet; import android.view.View; import android.widget.MediaController; import java.util.ArrayList; public class AndroidMediaController extends MediaController implements IMediaController { private ActionBar mActionBar; public AndroidMediaController(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public AndroidMediaController(Context context, boolean useFastForward) { super(context, useFastForward); initView(context); } public AndroidMediaController(Context context) { super(context); initView(context); } private void initView(Context context) { } public void setSupportActionBar(@Nullable ActionBar actionBar) { mActionBar = actionBar; if (isShowing()) { actionBar.show(); } else { actionBar.hide(); } } @Override public void show() { super.show(); if (mActionBar != null){ mActionBar.show(); } } @Override public void hide() { super.hide(); if (mActionBar != null){ mActionBar.hide(); } for (View view : mShowOnceArray){ view.setVisibility(View.GONE); } mShowOnceArray.clear(); } //---------- // Extends //---------- private ArrayList mShowOnceArray = new ArrayList (); @Override public void showOnce(@NonNull View view) { mShowOnceArray.add(view); view.setVisibility(View.VISIBLE); show(); } }
/* * Copyright (C) 2015 Zhang Rui* * 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.example.weekthreetest.widget.media; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import tv.danmaku.ijk.media.player.misc.IMediaDataSource; public class FileMediaDataSource implements IMediaDataSource { private RandomAccessFile mFile; private long mFileSize; public FileMediaDataSource(File file) throws IOException { mFile = new RandomAccessFile(file, "r"); mFileSize = mFile.length(); } @Override public int readAt(long position, byte[] buffer, int offset, int size) throws IOException { if (mFile.getFilePointer() != position){ mFile.seek(position); } if (size == 0){ return 0; } return mFile.read(buffer, 0, size); } @Override public long getSize() throws IOException { return mFileSize; } @Override public void close() throws IOException { mFileSize = 0; mFile.close(); mFile = null; } }
/* * Copyright (C) 2015 Zhang Rui* * 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.example.weekthreetest.widget.media; import android.annotation.TargetApi; 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.support.annotation.NonNull; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; import android.widget.MediaController; import java.io.IOException; import java.util.ArrayList; import java.util.List; 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.TextureMediaPlayer; public class IjkVideoView extends FrameLayout implements MediaController.MediaPlayerControl { private String TAG = "IjkVideoView"; // settable by the client private Uri mUri; private Map mHeaders; // all possible internal states 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 int getCurrentState() { return mCurrentState; } // 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 long mSeekWhenPrepared; // recording the seek position while preparing private boolean mCanPause = true; private boolean mCanSeekBack; private boolean mCanSeekForward; /** 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 IRenderView mRenderView; private int mVideoSarNum; private int mVideoSarDen; private boolean usingAndroidPlayer=false; private boolean usingMediaCodec=false; private boolean usingMediaCodecAutoRotate=false; private boolean usingOpenSLES=false; private String pixelFormat="";//Auto Select=,RGB 565=fcc-rv16,RGB 888X=fcc-rv32,YV12=fcc-yv12,默认为RGB 888X private boolean enableBackgroundPlay=false; private boolean enableSurfaceView=true; private boolean enableTextureView=false; private boolean enableNoView=false; public IjkVideoView(Context context) { super(context); initVideoView(context); } public IjkVideoView(Context context, AttributeSet attrs) { super(context, attrs); initVideoView(context); } public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initVideoView(context); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public IjkVideoView(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 >(); mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; } 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(getContext()); 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(getContext()); setRenderView(renderView); break; } default: Log.e(TAG, String.format(Locale.getDefault(), "invalid render %d\n", render)); break; } } /** * 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, Mapheaders) { 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; mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE); am.abandonAudioFocus(null); } } private void openVideo() { if (mUri == 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 = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE); am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); try { if (usingAndroidPlayer) { mMediaPlayer = new AndroidMediaPlayer(); } else { IjkMediaPlayer ijkMediaPlayer = null; if (mUri != null) { ijkMediaPlayer = new IjkMediaPlayer(); ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG); if (usingMediaCodec) { ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1); if (usingMediaCodecAutoRotate) { ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1); } else { ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0); } } else { ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0); } if (usingOpenSLES) { ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1); } else { ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0); } if (TextUtils.isEmpty(pixelFormat)) { ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32); } else { ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", pixelFormat); } ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", 10000000); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect", 1); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48); } mMediaPlayer = ijkMediaPlayer; } if (enableBackgroundPlay) { mMediaPlayer = new TextureMediaPlayer(mMediaPlayer); } // 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 (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) { mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders); } else { mMediaPlayer.setDataSource(mUri.toString()); } 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); return; } 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); return; } finally { // REMOVED: mPendingSubtitleTracks.clear(); } } public void setMediaController(IMediaController 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) { 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(); long seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call if (seekToPosition != 0) { seekTo((int) 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_ROTATION_CHANGED: mVideoRotationDegree = arg2; Log.d(TAG, "MEDIA_INFO_VIDEO_ROTATION_CHANGED: " + arg2); if (mRenderView != null){ mRenderView.setVideoRotation(arg2); } break; default: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(); String message="Unknown error"; if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) { message="Invalid progressive playback"; } new android.app.AlertDialog.Builder(getContext()) .setMessage(message) .setPositiveButton("error", 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((int) 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; // 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 = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE); am.abandonAudioFocus(null); } } @Override public boolean onTouchEvent(MotionEvent ev) { 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 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[mCurrentAspectRatioIndex]; 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; private List mAllRenders = new ArrayList (); private int mCurrentRenderIndex = 0; private int mCurrentRender = RENDER_NONE; private void initRenders() { mAllRenders.clear(); if (enableSurfaceView){ mAllRenders.add(RENDER_SURFACE_VIEW); } if (enableTextureView && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH){ mAllRenders.add(RENDER_TEXTURE_VIEW); } if (enableNoView){ mAllRenders.add(RENDER_NONE); } if (mAllRenders.isEmpty()){ mAllRenders.add(RENDER_SURFACE_VIEW); } mCurrentRender = mAllRenders.get(mCurrentRenderIndex); setRender(mCurrentRender); } public int toggleRender() { mCurrentRenderIndex++; mCurrentRenderIndex %= mAllRenders.size(); mCurrentRender = mAllRenders.get(mCurrentRenderIndex); setRender(mCurrentRender); return mCurrentRender; } //------------------------- // Extend: Background //------------------------- private void initBackground() { if (enableBackgroundPlay) { // MediaPlayerService.intentToStart(getContext()); // mMediaPlayer = MediaPlayerService.getMediaPlayer(); } } public void setAspectRatio(int aspectRatio) { for (int i = 0; i < s_allAspectRatio.length; i++) { if (s_allAspectRatio[i]==aspectRatio) { mCurrentAspectRatioIndex=i; if (mRenderView != null){ mRenderView.setAspectRatio(mCurrentAspectRatio); } break; } } } }
/* * Copyright (C) 2015 Zhang Rui* * 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.example.weekthreetest.widget.media; import android.view.View; import android.widget.MediaController; public interface IMediaController { void hide(); boolean isShowing(); void setAnchorView(View view); void setEnabled(boolean enabled); void setMediaPlayer(MediaController.MediaPlayerControl player); void show(int timeout); void show(); //---------- // Extends //---------- void showOnce(View view); }
/* * Copyright (C) 2015 Zhang Rui* * 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.example.weekthreetest.widget.media; import android.graphics.SurfaceTexture; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.Surface; import android.view.SurfaceHolder; import android.view.View; import tv.danmaku.ijk.media.player.IMediaPlayer; public interface IRenderView { int AR_ASPECT_FIT_PARENT = 0; // without clip int AR_ASPECT_FILL_PARENT = 1; // may clip int AR_ASPECT_WRAP_CONTENT = 2; int AR_MATCH_PARENT = 3; int AR_16_9_FIT_PARENT = 4; int AR_4_3_FIT_PARENT = 5; View getView(); boolean shouldWaitForResize(); void setVideoSize(int videoWidth, int videoHeight); void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen); void setVideoRotation(int degree); void setAspectRatio(int aspectRatio); void addRenderCallback(@NonNull IRenderCallback callback); void removeRenderCallback(@NonNull IRenderCallback callback); interface ISurfaceHolder { void bindToMediaPlayer(IMediaPlayer mp); @NonNull IRenderView getRenderView(); @Nullable SurfaceHolder getSurfaceHolder(); @Nullable Surface openSurface(); @Nullable SurfaceTexture getSurfaceTexture(); } interface IRenderCallback { /** * @param holder * @param width could be 0 * @param height could be 0 */ void onSurfaceCreated(@NonNull ISurfaceHolder holder, int width, int height); /** * @param holder * @param format could be 0 * @param width * @param height */ void onSurfaceChanged(@NonNull ISurfaceHolder holder, int format, int width, int height); void onSurfaceDestroyed(@NonNull ISurfaceHolder holder); } }
/* * Copyright (C) 2015 Zhang Rui* * 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.example.weekthreetest.widget.media; import android.content.Context; import android.support.annotation.NonNull; import android.view.View; import java.lang.ref.WeakReference; public final class MeasureHelper { private WeakReference mWeakView; private int mVideoWidth; private int mVideoHeight; private int mVideoSarNum; private int mVideoSarDen; private int mVideoRotationDegree; private int mMeasuredWidth; private int mMeasuredHeight; private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT; public MeasureHelper(View view) { mWeakView = new WeakReference (view); } public View getView() { if (mWeakView == null) return null; return mWeakView.get(); } public void setVideoSize(int videoWidth, int videoHeight) { mVideoWidth = videoWidth; mVideoHeight = videoHeight; } public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) { mVideoSarNum = videoSarNum; mVideoSarDen = videoSarDen; } public void setVideoRotation(int videoRotationDegree) { mVideoRotationDegree = videoRotationDegree; } /** * Must be called by View.onMeasure(int, int) * * @param widthMeasureSpec * @param heightMeasureSpec */ public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) { //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", " // + MeasureSpec.toString(heightMeasureSpec) + ")"); if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) { int tempSpec = widthMeasureSpec; widthMeasureSpec = heightMeasureSpec; heightMeasureSpec = tempSpec; } int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec); int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec); if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) { width = widthMeasureSpec; height = heightMeasureSpec; } else if (mVideoWidth > 0 && mVideoHeight > 0) { int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec); if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) { float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize; float displayAspectRatio; switch (mCurrentAspectRatio) { case IRenderView.AR_16_9_FIT_PARENT: displayAspectRatio = 16.0f / 9.0f; if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) displayAspectRatio = 1.0f / displayAspectRatio; break; case IRenderView.AR_4_3_FIT_PARENT: displayAspectRatio = 4.0f / 3.0f; if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) displayAspectRatio = 1.0f / displayAspectRatio; break; case IRenderView.AR_ASPECT_FIT_PARENT: case IRenderView.AR_ASPECT_FILL_PARENT: case IRenderView.AR_ASPECT_WRAP_CONTENT: default: displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight; if (mVideoSarNum > 0 && mVideoSarDen > 0) displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen; break; } boolean shouldBeWider = displayAspectRatio > specAspectRatio; switch (mCurrentAspectRatio) { case IRenderView.AR_ASPECT_FIT_PARENT: case IRenderView.AR_16_9_FIT_PARENT: case IRenderView.AR_4_3_FIT_PARENT: if (shouldBeWider) { // too wide, fix width width = widthSpecSize; height = (int) (width / displayAspectRatio); } else { // too high, fix height height = heightSpecSize; width = (int) (height * displayAspectRatio); } break; case IRenderView.AR_ASPECT_FILL_PARENT: if (shouldBeWider) { // not high enough, fix height height = heightSpecSize; width = (int) (height * displayAspectRatio); } else { // not wide enough, fix width width = widthSpecSize; height = (int) (width / displayAspectRatio); } break; case IRenderView.AR_ASPECT_WRAP_CONTENT: default: if (shouldBeWider) { // too wide, fix width width = Math.min(mVideoWidth, widthSpecSize); height = (int) (width / displayAspectRatio); } else { // too high, fix height height = Math.min(mVideoHeight, heightSpecSize); width = (int) (height * displayAspectRatio); } break; } } else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) { // the size is fixed width = widthSpecSize; height = heightSpecSize; // for compatibility, we adjust size based on aspect ratio if (mVideoWidth * height < width * mVideoHeight) { //Log.i("@@@", "image too wide, correcting"); width = height * mVideoWidth / mVideoHeight; } else if (mVideoWidth * height > width * mVideoHeight) { //Log.i("@@@", "image too tall, correcting"); height = width * mVideoHeight / mVideoWidth; } } else if (widthSpecMode == View.MeasureSpec.EXACTLY) { // only the width is fixed, adjust the height to match aspect ratio if possible width = widthSpecSize; height = width * mVideoHeight / mVideoWidth; if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) { // couldn't match aspect ratio within the constraints height = heightSpecSize; } } else if (heightSpecMode == View.MeasureSpec.EXACTLY) { // only the height is fixed, adjust the width to match aspect ratio if possible height = heightSpecSize; width = height * mVideoWidth / mVideoHeight; if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) { // couldn't match aspect ratio within the constraints width = widthSpecSize; } } else { // neither the width nor the height are fixed, try to use actual video size width = mVideoWidth; height = mVideoHeight; if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) { // too tall, decrease both width and height height = heightSpecSize; width = height * mVideoWidth / mVideoHeight; } if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) { // too wide, decrease both width and height width = widthSpecSize; height = width * mVideoHeight / mVideoWidth; } } } else { // no size yet, just adopt the given spec sizes } mMeasuredWidth = width; mMeasuredHeight = height; } public int getMeasuredWidth() { return mMeasuredWidth; } public int getMeasuredHeight() { return mMeasuredHeight; } public void setAspectRatio(int aspectRatio) { mCurrentAspectRatio = aspectRatio; } @NonNull public static String getAspectRatioText(Context context, int aspectRatio) { String text; switch (aspectRatio) { case IRenderView.AR_ASPECT_FIT_PARENT: text = "Aspect / Fit parent"; break; case IRenderView.AR_ASPECT_FILL_PARENT: text = "Aspect / Fill parent"; break; case IRenderView.AR_ASPECT_WRAP_CONTENT: text = "Aspect / Wrap conten"; break; case IRenderView.AR_MATCH_PARENT: text = "Free / Fill parent"; break; case IRenderView.AR_16_9_FIT_PARENT: text = "16:9 / Fit parent"; break; case IRenderView.AR_4_3_FIT_PARENT: text = "4:3 / Fit parent"; break; default: text = "NA"; break; } return text; } }
/* * Copyright (C) 2015 Zhang Rui* * 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.example.weekthreetest.widget.media; import tv.danmaku.ijk.media.player.IMediaPlayer; import tv.danmaku.ijk.media.player.IjkMediaPlayer; import tv.danmaku.ijk.media.player.MediaPlayerProxy; import tv.danmaku.ijk.media.player.TextureMediaPlayer; public class MediaPlayerCompat { public static String getName(IMediaPlayer mp) { if (mp == null) { return "null"; } else if (mp instanceof TextureMediaPlayer) { StringBuilder sb = new StringBuilder("TextureMediaPlayer <"); IMediaPlayer internalMediaPlayer = ((TextureMediaPlayer) mp).getInternalMediaPlayer(); if (internalMediaPlayer == null) { sb.append("null>"); } else { sb.append(internalMediaPlayer.getClass().getSimpleName()); sb.append(">"); } return sb.toString(); } else { return mp.getClass().getSimpleName(); } } public static IjkMediaPlayer getIjkMediaPlayer(IMediaPlayer mp) { IjkMediaPlayer ijkMediaPlayer = null; if (mp == null) { return null; } if (mp instanceof IjkMediaPlayer) { ijkMediaPlayer = (IjkMediaPlayer) mp; } else if (mp instanceof MediaPlayerProxy && ((MediaPlayerProxy) mp).getInternalMediaPlayer() instanceof IjkMediaPlayer) { ijkMediaPlayer = (IjkMediaPlayer) ((MediaPlayerProxy) mp).getInternalMediaPlayer(); } return ijkMediaPlayer; } public static void selectTrack(IMediaPlayer mp, int stream) { IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp); if (ijkMediaPlayer == null) return; ijkMediaPlayer.selectTrack(stream); } public static void deselectTrack(IMediaPlayer mp, int stream) { IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp); if (ijkMediaPlayer == null) return; ijkMediaPlayer.deselectTrack(stream); } public static int getSelectedTrack(IMediaPlayer mp, int trackType) { IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp); if (ijkMediaPlayer == null) return -1; return ijkMediaPlayer.getSelectedTrack(trackType); } }
/* * Copyright (C) 2015 Zhang Rui* * 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.example.weekthreetest.widget.media; import android.annotation.TargetApi; import android.content.Context; import android.graphics.SurfaceTexture; import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import java.lang.ref.WeakReference; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import tv.danmaku.ijk.media.player.IMediaPlayer; import tv.danmaku.ijk.media.player.ISurfaceTextureHolder; public class SurfaceRenderView extends SurfaceView implements IRenderView { private MeasureHelper mMeasureHelper; public SurfaceRenderView(Context context) { super(context); initView(context); } public SurfaceRenderView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initView(context); } private void initView(Context context) { mMeasureHelper = new MeasureHelper(this); mSurfaceCallback = new SurfaceCallback(this); getHolder().addCallback(mSurfaceCallback); //noinspection deprecation getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL); } @Override public View getView() { return this; } @Override public boolean shouldWaitForResize() { return true; } //-------------------- // Layout & Measure //-------------------- @Override public void setVideoSize(int videoWidth, int videoHeight) { if (videoWidth > 0 && videoHeight > 0) { mMeasureHelper.setVideoSize(videoWidth, videoHeight); getHolder().setFixedSize(videoWidth, videoHeight); requestLayout(); } } @Override public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) { if (videoSarNum > 0 && videoSarDen > 0) { mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen); requestLayout(); } } @Override public void setVideoRotation(int degree) { Log.e("", "SurfaceView doesn't support rotation (" + degree + ")!\n"); } @Override public void setAspectRatio(int aspectRatio) { mMeasureHelper.setAspectRatio(aspectRatio); requestLayout(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight()); } //-------------------- // SurfaceViewHolder //-------------------- private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder { private SurfaceRenderView mSurfaceView; private SurfaceHolder mSurfaceHolder; public InternalSurfaceHolder(@NonNull SurfaceRenderView surfaceView, @Nullable SurfaceHolder surfaceHolder) { mSurfaceView = surfaceView; mSurfaceHolder = surfaceHolder; } public void bindToMediaPlayer(IMediaPlayer mp) { if (mp != null) { if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) && (mp instanceof ISurfaceTextureHolder)) { ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp; textureHolder.setSurfaceTexture(null); } mp.setDisplay(mSurfaceHolder); } } @NonNull @Override public IRenderView getRenderView() { return mSurfaceView; } @Nullable @Override public SurfaceHolder getSurfaceHolder() { return mSurfaceHolder; } @Nullable @Override public SurfaceTexture getSurfaceTexture() { return null; } @Nullable @Override public Surface openSurface() { if (mSurfaceHolder == null) return null; return mSurfaceHolder.getSurface(); } } //------------------------- // SurfaceHolder.Callback //------------------------- @Override public void addRenderCallback(IRenderCallback callback) { mSurfaceCallback.addRenderCallback(callback); } @Override public void removeRenderCallback(IRenderCallback callback) { mSurfaceCallback.removeRenderCallback(callback); } private SurfaceCallback mSurfaceCallback; private static final class SurfaceCallback implements SurfaceHolder.Callback { private SurfaceHolder mSurfaceHolder; private boolean mIsFormatChanged; private int mFormat; private int mWidth; private int mHeight; private WeakReference mWeakSurfaceView; private Map mRenderCallbackMap = new ConcurrentHashMap (); public SurfaceCallback(@NonNull SurfaceRenderView surfaceView) { mWeakSurfaceView = new WeakReference (surfaceView); } public void addRenderCallback(@NonNull IRenderCallback callback) { mRenderCallbackMap.put(callback, callback); ISurfaceHolder surfaceHolder = null; if (mSurfaceHolder != null) { if (surfaceHolder == null) surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight); } if (mIsFormatChanged) { if (surfaceHolder == null) surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); callback.onSurfaceChanged(surfaceHolder, mFormat, mWidth, mHeight); } } public void removeRenderCallback(@NonNull IRenderCallback callback) { mRenderCallbackMap.remove(callback); } @Override public void surfaceCreated(SurfaceHolder holder) { mSurfaceHolder = holder; mIsFormatChanged = false; mFormat = 0; mWidth = 0; mHeight = 0; ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { renderCallback.onSurfaceCreated(surfaceHolder, 0, 0); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { mSurfaceHolder = null; mIsFormatChanged = false; mFormat = 0; mWidth = 0; mHeight = 0; ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { renderCallback.onSurfaceDestroyed(surfaceHolder); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mSurfaceHolder = holder; mIsFormatChanged = true; mFormat = format; mWidth = width; mHeight = height; // mMeasureHelper.setVideoSize(width, height); ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { renderCallback.onSurfaceChanged(surfaceHolder, format, width, height); } } } //-------------------- // Accessibility //-------------------- @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); event.setClassName(SurfaceRenderView.class.getName()); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { info.setClassName(SurfaceRenderView.class.getName()); } } }
/* * Copyright (C) 2015 Zhang Rui* * 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.example.weekthreetest.widget.media; import android.annotation.TargetApi; import android.content.Context; import android.graphics.SurfaceTexture; import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.TextureView; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import java.lang.ref.WeakReference; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import tv.danmaku.ijk.media.player.IMediaPlayer; import tv.danmaku.ijk.media.player.ISurfaceTextureHolder; import tv.danmaku.ijk.media.player.ISurfaceTextureHost; @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public class TextureRenderView extends TextureView implements IRenderView { private static final String TAG = "TextureRenderView"; private MeasureHelper mMeasureHelper; public TextureRenderView(Context context) { super(context); initView(context); } public TextureRenderView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initView(context); } private void initView(Context context) { mMeasureHelper = new MeasureHelper(this); mSurfaceCallback = new SurfaceCallback(this); setSurfaceTextureListener(mSurfaceCallback); } @Override public View getView() { return this; } @Override public boolean shouldWaitForResize() { return false; } @Override protected void onDetachedFromWindow() { mSurfaceCallback.willDetachFromWindow(); super.onDetachedFromWindow(); mSurfaceCallback.didDetachFromWindow(); } //-------------------- // Layout & Measure //-------------------- @Override public void setVideoSize(int videoWidth, int videoHeight) { if (videoWidth > 0 && videoHeight > 0) { mMeasureHelper.setVideoSize(videoWidth, videoHeight); requestLayout(); } } @Override public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) { if (videoSarNum > 0 && videoSarDen > 0) { mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen); requestLayout(); } } @Override public void setVideoRotation(int degree) { mMeasureHelper.setVideoRotation(degree); setRotation(degree); } @Override public void setAspectRatio(int aspectRatio) { mMeasureHelper.setAspectRatio(aspectRatio); requestLayout(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight()); } //-------------------- // TextureViewHolder //-------------------- public IRenderView.ISurfaceHolder getSurfaceHolder() { return new InternalSurfaceHolder(this, mSurfaceCallback.mSurfaceTexture, mSurfaceCallback); } private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder { private TextureRenderView mTextureView; private SurfaceTexture mSurfaceTexture; private ISurfaceTextureHost mSurfaceTextureHost; public InternalSurfaceHolder(@NonNull TextureRenderView textureView, @Nullable SurfaceTexture surfaceTexture, @NonNull ISurfaceTextureHost surfaceTextureHost) { mTextureView = textureView; mSurfaceTexture = surfaceTexture; mSurfaceTextureHost = surfaceTextureHost; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public void bindToMediaPlayer(IMediaPlayer mp) { if (mp == null) return; if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) && (mp instanceof ISurfaceTextureHolder)) { ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp; mTextureView.mSurfaceCallback.setOwnSurfaceTexture(false); SurfaceTexture surfaceTexture = textureHolder.getSurfaceTexture(); if (surfaceTexture != null) { mTextureView.setSurfaceTexture(surfaceTexture); } else { textureHolder.setSurfaceTexture(mSurfaceTexture); textureHolder.setSurfaceTextureHost(mTextureView.mSurfaceCallback); } } else { mp.setSurface(openSurface()); } } @NonNull @Override public IRenderView getRenderView() { return mTextureView; } @Nullable @Override public SurfaceHolder getSurfaceHolder() { return null; } @Nullable @Override public SurfaceTexture getSurfaceTexture() { return mSurfaceTexture; } @Nullable @Override public Surface openSurface() { if (mSurfaceTexture == null) return null; return new Surface(mSurfaceTexture); } } //------------------------- // SurfaceHolder.Callback //------------------------- @Override public void addRenderCallback(IRenderCallback callback) { mSurfaceCallback.addRenderCallback(callback); } @Override public void removeRenderCallback(IRenderCallback callback) { mSurfaceCallback.removeRenderCallback(callback); } private SurfaceCallback mSurfaceCallback; private static final class SurfaceCallback implements SurfaceTextureListener, ISurfaceTextureHost { private SurfaceTexture mSurfaceTexture; private boolean mIsFormatChanged; private int mWidth; private int mHeight; private boolean mOwnSurfaceTexture = true; private boolean mWillDetachFromWindow = false; private boolean mDidDetachFromWindow = false; private WeakReference mWeakRenderView; private Map mRenderCallbackMap = new ConcurrentHashMap (); public SurfaceCallback(@NonNull TextureRenderView renderView) { mWeakRenderView = new WeakReference (renderView); } public void setOwnSurfaceTexture(boolean ownSurfaceTexture) { mOwnSurfaceTexture = ownSurfaceTexture; } public void addRenderCallback(@NonNull IRenderCallback callback) { mRenderCallbackMap.put(callback, callback); ISurfaceHolder surfaceHolder = null; if (mSurfaceTexture != null) { if (surfaceHolder == null) surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this); callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight); } if (mIsFormatChanged) { if (surfaceHolder == null) surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this); callback.onSurfaceChanged(surfaceHolder, 0, mWidth, mHeight); } } public void removeRenderCallback(@NonNull IRenderCallback callback) { mRenderCallbackMap.remove(callback); } @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mSurfaceTexture = surface; mIsFormatChanged = false; mWidth = 0; mHeight = 0; ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this); for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { renderCallback.onSurfaceCreated(surfaceHolder, 0, 0); } } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { mSurfaceTexture = surface; mIsFormatChanged = true; mWidth = width; mHeight = height; ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this); for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { renderCallback.onSurfaceChanged(surfaceHolder, 0, width, height); } } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { mSurfaceTexture = surface; mIsFormatChanged = false; mWidth = 0; mHeight = 0; ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this); for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { renderCallback.onSurfaceDestroyed(surfaceHolder); } Log.d(TAG, "onSurfaceTextureDestroyed: destroy: " + mOwnSurfaceTexture); return mOwnSurfaceTexture; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } //------------------------- // ISurfaceTextureHost //------------------------- @Override public void releaseSurfaceTexture(SurfaceTexture surfaceTexture) { if (surfaceTexture == null) { Log.d(TAG, "releaseSurfaceTexture: null"); } else if (mDidDetachFromWindow) { if (surfaceTexture != mSurfaceTexture) { Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release different SurfaceTexture"); surfaceTexture.release(); } else if (!mOwnSurfaceTexture) { Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release detached SurfaceTexture"); surfaceTexture.release(); } else { Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): already released by TextureView"); } } else if (mWillDetachFromWindow) { if (surfaceTexture != mSurfaceTexture) { Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): release different SurfaceTexture"); surfaceTexture.release(); } else if (!mOwnSurfaceTexture) { Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): re-attach SurfaceTexture to TextureView"); setOwnSurfaceTexture(true); } else { Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): will released by TextureView"); } } else { if (surfaceTexture != mSurfaceTexture) { Log.d(TAG, "releaseSurfaceTexture: alive: release different SurfaceTexture"); surfaceTexture.release(); } else if (!mOwnSurfaceTexture) { Log.d(TAG, "releaseSurfaceTexture: alive: re-attach SurfaceTexture to TextureView"); setOwnSurfaceTexture(true); } else { Log.d(TAG, "releaseSurfaceTexture: alive: will released by TextureView"); } } } public void willDetachFromWindow() { Log.d(TAG, "willDetachFromWindow()"); mWillDetachFromWindow = true; } public void didDetachFromWindow() { Log.d(TAG, "didDetachFromWindow()"); mDidDetachFromWindow = true; } } //-------------------- // Accessibility //-------------------- @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); event.setClassName(TextureRenderView.class.getName()); } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(TextureRenderView.class.getName()); } }
下边是重点来了
package com.example.weekthreetest.common; import android.app.Activity; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Resources; import android.media.AudioManager; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.OrientationEventListener; import android.view.Surface; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; import com.example.weekthreetest.R; import com.example.weekthreetest.widget.media.IRenderView; import com.example.weekthreetest.widget.media.IjkVideoView; import tv.danmaku.ijk.media.player.IMediaPlayer; import tv.danmaku.ijk.media.player.IjkMediaPlayer; import tv.danmaku.ijk.media.player.pragma.DebugLog; public class PlayerManager { /** * 可能会剪裁,保持原视频的大小,显示在中心,当原视频的大小超过view的大小超过部分裁剪处理 */ public static final String SCALETYPE_FITPARENT="fitParent"; /** * 可能会剪裁,等比例放大视频,直到填满View为止,超过View的部分作裁剪处理 */ public static final String SCALETYPE_FILLPARENT="fillParent"; /** * 将视频的内容完整居中显示,如果视频大于view,则按比例缩视频直到完全显示在view中 */ public static final String SCALETYPE_WRAPCONTENT="wrapContent"; /** * 不剪裁,非等比例拉伸画面填满整个View */ public static final String SCALETYPE_FITXY="fitXY"; /** * 不剪裁,非等比例拉伸画面到16:9,并完全显示在View中 */ public static final String SCALETYPE_16_9="16:9"; /** * 不剪裁,非等比例拉伸画面到4:3,并完全显示在View中 */ public static final String SCALETYPE_4_3="4:3"; /** * 状态常量 */ private final int STATUS_ERROR=-1; private final int STATUS_IDLE=0; private final int STATUS_LOADING=1; private final int STATUS_PLAYING=2; private final int STATUS_PAUSE=3; private final int STATUS_COMPLETED=4; private final Activity activity; private final IjkVideoView videoView; private final AudioManager audioManager; public GestureDetector gestureDetector; private boolean playerSupport; private boolean isLive = false;//是否为直播 private boolean fullScreenOnly; private boolean portrait; private final int mMaxVolume; private int screenWidthPixels; private int currentPosition; private int status=STATUS_IDLE; private long pauseTime; private String url; private float brightness=-1; private int volume=-1; private long newPosition = -1; private long defaultRetryTime=5000; private OrientationEventListener orientationEventListener; private PlayerStateListener playerStateListener; public void setPlayerStateListener(PlayerStateListener playerStateListener) { this.playerStateListener = playerStateListener; } private OnErrorListener onErrorListener=new OnErrorListener() { @Override public void onError(int what, int extra) { } }; private OnCompleteListener onCompleteListener=new OnCompleteListener() { @Override public void onComplete() { } }; private OnInfoListener onInfoListener=new OnInfoListener(){ @Override public void onInfo(int what, int extra) { } }; private OnControlPanelVisibilityChangeListener onControlPanelVisibilityChangeListener=new OnControlPanelVisibilityChangeListener() { @Override public void change(boolean isShowing) { } }; /** * try to play when error(only for live video) * @param defaultRetryTime millisecond,0 will stop retry,default is 5000 millisecond */ public void setDefaultRetryTime(long defaultRetryTime) { this.defaultRetryTime = defaultRetryTime; } public PlayerManager(final Activity activity) { try { IjkMediaPlayer.loadLibrariesOnce(null); IjkMediaPlayer.native_profileBegin("libijkplayer.so"); playerSupport=true; } catch (Throwable e) { Log.e("GiraffePlayer", "loadLibraries error", e); } this.activity=activity; screenWidthPixels = activity.getResources().getDisplayMetrics().widthPixels; videoView = (IjkVideoView) activity.findViewById(R.id.video_view); videoView.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() { @Override public void onCompletion(IMediaPlayer mp) { statusChange(STATUS_COMPLETED); onCompleteListener.onComplete(); } }); videoView.setOnErrorListener(new IMediaPlayer.OnErrorListener() { @Override public boolean onError(IMediaPlayer mp, int what, int extra) { statusChange(STATUS_ERROR); onErrorListener.onError(what,extra); return true; } }); videoView.setOnInfoListener(new IMediaPlayer.OnInfoListener() { @Override public boolean onInfo(IMediaPlayer mp, int what, int extra) { switch (what) { case IMediaPlayer.MEDIA_INFO_BUFFERING_START: statusChange(STATUS_LOADING); break; case IMediaPlayer.MEDIA_INFO_BUFFERING_END: statusChange(STATUS_PLAYING); break; case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH: //显示下载速度 // Toast.show("download rate:" + extra); break; case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START: statusChange(STATUS_PLAYING); break; default:break; } onInfoListener.onInfo(what,extra); return false; } }); audioManager = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE); mMaxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); gestureDetector = new GestureDetector(activity, new PlayerGestureListener()); if (fullScreenOnly) { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } portrait=getScreenOrientation()== ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; if (!playerSupport) { DebugLog.e("","播放器不支持此设备"); } } private void statusChange(int newStatus) { status = newStatus; if (!isLive && newStatus==STATUS_COMPLETED) { DebugLog.d("","statusChange STATUS_COMPLETED..."); if (playerStateListener != null){ playerStateListener.onComplete(); } }else if (newStatus == STATUS_ERROR) { DebugLog.d("","statusChange STATUS_ERROR..."); if (playerStateListener != null){ playerStateListener.onError(); } } else if(newStatus==STATUS_LOADING){ // $.id(R.id.app_video_loading).visible(); if (playerStateListener != null){ playerStateListener.onLoading(); } DebugLog.d("","statusChange STATUS_LOADING..."); } else if (newStatus == STATUS_PLAYING) { DebugLog.d("","statusChange STATUS_PLAYING..."); if (playerStateListener != null){ playerStateListener.onPlay(); } } } public void onPause() { pauseTime= System.currentTimeMillis(); if (status==STATUS_PLAYING) { videoView.pause(); if (!isLive) { currentPosition = videoView.getCurrentPosition(); } } } public void onResume() { pauseTime=0; if (status==STATUS_PLAYING) { if (isLive) { videoView.seekTo(0); } else { if (currentPosition>0) { videoView.seekTo(currentPosition); } } videoView.start(); } } public void onDestroy() { orientationEventListener.disable(); videoView.stopPlayback(); } public void play(String url) { this.url = url; if (playerSupport) { videoView.setVideoPath(url); videoView.start(); } } private String generateTime(long time) { int totalSeconds = (int) (time / 1000); int seconds = totalSeconds % 60; int minutes = (totalSeconds / 60) % 60; int hours = totalSeconds / 3600; return hours > 0 ? String.format("%02d:%02d:%02d", hours, minutes, seconds) : String.format("%02d:%02d", minutes, seconds); } private int getScreenOrientation() { int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); DisplayMetrics dm = new DisplayMetrics(); activity.getWindowManager().getDefaultDisplay().getMetrics(dm); int width = dm.widthPixels; int height = dm.heightPixels; int orientation; // if the device's natural orientation is portrait: if ((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) && height > width || (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) && width > height) { switch (rotation) { case Surface.ROTATION_0: orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; break; case Surface.ROTATION_90: orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; break; case Surface.ROTATION_180: orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; break; case Surface.ROTATION_270: orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; break; default: orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; break; } } // if the device's natural orientation is landscape or if the device // is square: else { switch (rotation) { case Surface.ROTATION_0: orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; break; case Surface.ROTATION_90: orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; break; case Surface.ROTATION_180: orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; break; case Surface.ROTATION_270: orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; break; default: orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; break; } } return orientation; } /** * 滑动改变声音大小 * * @param percent */ private void onVolumeSlide(float percent) { if (volume == -1) { volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); if (volume < 0) volume = 0; } int index = (int) (percent * mMaxVolume) + volume; if (index > mMaxVolume) { index = mMaxVolume; } else if (index < 0){ index = 0; } // 变更声音 audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0); // 变更进度条 int i = (int) (index * 1.0 / mMaxVolume * 100); String s = i + "%"; if (i == 0) { s = "off"; } DebugLog.d("","onVolumeSlide:"+s); } private void onProgressSlide(float percent) { long position = videoView.getCurrentPosition(); long duration = videoView.getDuration(); long deltaMax = Math.min(100 * 1000, duration - position); long delta = (long) (deltaMax * percent); newPosition = delta + position; if (newPosition > duration) { newPosition = duration; } else if (newPosition <= 0) { newPosition=0; delta=-position; } int showDelta = (int) delta / 1000; if (showDelta != 0) { String text = showDelta > 0 ? ("+" + showDelta) : "" + showDelta; DebugLog.d("","onProgressSlide:" + text); } } /** * 滑动改变亮度 * * @param percent */ private void onBrightnessSlide(float percent) { if (brightness < 0) { brightness = activity.getWindow().getAttributes().screenBrightness; if (brightness <= 0.00f){ brightness = 0.50f; }else if (brightness < 0.01f){ brightness = 0.01f; } } DebugLog.d("","brightness:"+brightness+",percent:"+ percent); WindowManager.LayoutParams lpa = activity.getWindow().getAttributes(); lpa.screenBrightness = brightness + percent; if (lpa.screenBrightness > 1.0f){ lpa.screenBrightness = 1.0f; }else if (lpa.screenBrightness < 0.01f){ lpa.screenBrightness = 0.01f; } activity.getWindow().setAttributes(lpa); } public void setFullScreenOnly(boolean fullScreenOnly) { this.fullScreenOnly = fullScreenOnly; tryFullScreen(fullScreenOnly); if (fullScreenOnly) { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } else { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR); } } private void tryFullScreen(boolean fullScreen) { if (activity instanceof AppCompatActivity) { ActionBar supportActionBar = ((AppCompatActivity) activity).getSupportActionBar(); if (supportActionBar != null) { if (fullScreen) { supportActionBar.hide(); } else { supportActionBar.show(); } } } setFullScreen(fullScreen); } private void setFullScreen(boolean fullScreen) { if (activity != null) { WindowManager.LayoutParams attrs = activity.getWindow().getAttributes(); if (fullScreen) { attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; activity.getWindow().setAttributes(attrs); activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); } else { attrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN); activity.getWindow().setAttributes(attrs); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); } } } /** * * fitParent:可能会剪裁,保持原视频的大小,显示在中心,当原视频的大小超过view的大小超过部分裁剪处理 * fillParent:可能会剪裁,等比例放大视频,直到填满View为止,超过View的部分作裁剪处理 * wrapContent:将视频的内容完整居中显示,如果视频大于view,则按比例缩视频直到完全显示在view中 * fitXY:不剪裁,非等比例拉伸画面填满整个View * 16:9:不剪裁,非等比例拉伸画面到16:9,并完全显示在View中 * 4:3:不剪裁,非等比例拉伸画面到4:3,并完全显示在View中 ** @param scaleType */ public void setScaleType(String scaleType) { if (SCALETYPE_FITPARENT.equals(scaleType)) { videoView.setAspectRatio(IRenderView.AR_ASPECT_FIT_PARENT); }else if (SCALETYPE_FILLPARENT.equals(scaleType)) { videoView.setAspectRatio(IRenderView.AR_ASPECT_FILL_PARENT); }else if (SCALETYPE_WRAPCONTENT.equals(scaleType)) { videoView.setAspectRatio(IRenderView.AR_ASPECT_WRAP_CONTENT); }else if (SCALETYPE_FITXY.equals(scaleType)) { videoView.setAspectRatio(IRenderView.AR_MATCH_PARENT); }else if (SCALETYPE_16_9.equals(scaleType)) { videoView.setAspectRatio(IRenderView.AR_16_9_FIT_PARENT); }else if (SCALETYPE_4_3.equals(scaleType)) { videoView.setAspectRatio(IRenderView.AR_4_3_FIT_PARENT); } } public void start() { videoView.start(); } public void pause() { videoView.pause(); } public boolean onBackPressed() { if (!fullScreenOnly && getScreenOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); return true; } return false; } class Query { private final Activity activity; private View view; public Query(Activity activity) { this.activity=activity; } public Query id(int id) { view = activity.findViewById(id); return this; } public Query image(int resId) { if (view instanceof ImageView) { ((ImageView) view).setImageResource(resId); } return this; } public Query visible() { if (view != null) { view.setVisibility(View.VISIBLE); } return this; } public Query gone() { if (view != null) { view.setVisibility(View.GONE); } return this; } public Query invisible() { if (view != null) { view.setVisibility(View.INVISIBLE); } return this; } public Query clicked(View.OnClickListener handler) { if (view != null) { view.setOnClickListener(handler); } return this; } public Query text(CharSequence text) { if (view!=null && view instanceof TextView) { ((TextView) view).setText(text); } return this; } public Query visibility(int visible) { if (view != null) { view.setVisibility(visible); } return this; } private void size(boolean width, int n, boolean dip){ if(view != null){ ViewGroup.LayoutParams lp = view.getLayoutParams(); if(n > 0 && dip){ n = dip2pixel(activity, n); } if(width){ lp.width = n; }else{ lp.height = n; } view.setLayoutParams(lp); } } public void height(int height, boolean dip) { size(false,height,dip); } public int dip2pixel(Context context, float n){ int value = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, n, context.getResources().getDisplayMetrics()); return value; } public float pixel2dip(Context context, float n){ Resources resources = context.getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); float dp = n / (metrics.densityDpi / 160f); return dp; } } public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener { private boolean firstTouch; private boolean volumeControl; private boolean toSeek; /** * 双击 */ @Override public boolean onDoubleTap(MotionEvent e) { videoView.toggleAspectRatio(); return true; } @Override public boolean onDown(MotionEvent e) { firstTouch = true; return super.onDown(e); } /** * 滑动 */ @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { float mOldX = e1.getX(), mOldY = e1.getY(); float deltaY = mOldY - e2.getY(); float deltaX = mOldX - e2.getX(); if (firstTouch) { toSeek = Math.abs(distanceX) >= Math.abs(distanceY); volumeControl=mOldX > screenWidthPixels * 0.5f; firstTouch = false; } if (toSeek) { if (!isLive) { onProgressSlide(-deltaX / videoView.getWidth()); } } else { float percent = deltaY / videoView.getHeight(); if (volumeControl) { onVolumeSlide(percent); } else { onBrightnessSlide(percent); } } return super.onScroll(e1, e2, distanceX, distanceY); } @Override public boolean onSingleTapUp(MotionEvent e) { return true; } } /** * is player support this device * @return */ public boolean isPlayerSupport() { return playerSupport; } /** * 是否正在播放 * @return */ public boolean isPlaying() { return videoView!=null?videoView.isPlaying():false; } public void stop(){ videoView.stopPlayback(); } public int getCurrentPosition(){ return videoView.getCurrentPosition(); } /** * get video duration * @return */ public int getDuration(){ return videoView.getDuration(); } public PlayerManager playInFullScreen(boolean fullScreen){ if (fullScreen) { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } return this; } public PlayerManager onError(OnErrorListener onErrorListener) { this.onErrorListener = onErrorListener; return this; } public PlayerManager onComplete(OnCompleteListener onCompleteListener) { this.onCompleteListener = onCompleteListener; return this; } public PlayerManager onInfo(OnInfoListener onInfoListener) { this.onInfoListener = onInfoListener; return this; } public PlayerManager onControlPanelVisibilityChange(OnControlPanelVisibilityChangeListener listener){ this.onControlPanelVisibilityChangeListener = listener; return this; } /** * set is live (can't seek forward) * @param isLive * @return */ public PlayerManager live(boolean isLive) { this.isLive = isLive; return this; } public PlayerManager toggleAspectRatio(){ if (videoView != null) { videoView.toggleAspectRatio(); } return this; } public interface PlayerStateListener{ void onComplete(); void onError(); void onLoading(); void onPlay(); } public interface OnErrorListener{ void onError(int what, int extra); } public interface OnCompleteListener{ void onComplete(); } public interface OnControlPanelVisibilityChangeListener{ void change(boolean isShowing); } public interface OnInfoListener{ void onInfo(int what, int extra); } } 以上东西都弄完了之后,咱再下边开始玩了
package com.example.weekthreetest; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.MotionEvent; import com.example.weekthreetest.common.PlayerManager; public class ShowActivity extends AppCompatActivity implements PlayerManager.PlayerStateListener { PlayerManager player; private String url1 = "http://2449.vod.myqcloud.com/2449_bfbbfa3cea8f11e5aac3db03cda99974.f20.mp4"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_show); initPlayer(); } private void initPlayer() { player = new PlayerManager(this); player.setFullScreenOnly(true); player.setScaleType(PlayerManager.SCALETYPE_FILLPARENT); player.playInFullScreen(true); player.setPlayerStateListener(this); player.play(url1); } @Override public boolean onTouchEvent(MotionEvent event) { if (player.gestureDetector.onTouchEvent(event)){ return true; } return super.onTouchEvent(event); } @Override public void onComplete() { } @Override public void onError() { } @Override public void onLoading() { } @Override public void onPlay() { } }
这样基本上就能玩了
有人比较菜,那我把布局也粘上来
xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.weekthreetest.ShowActivity"> <com.example.weekthreetest.widget.media.IjkVideoView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="match_parent"/> android.support.constraint.ConstraintLayout>
运行效果就不放了 如果没出来只能说你没有添加网络权限