1.ijkplayer简介

ijkplayer是一个基于FFmpeg的轻量级Android/iOS视频播放器。FFmpeg的是全球领先的多媒体框架,能够解码,编码, 转码,复用,解复用,流,过滤器和播放大部分的视频格式。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。

2.功能的展示

      

支持横竖屏切换展示,上下滑动声音亮度的调节等功能。

3. androidStudio 接入

1)添加.so库:

如图所示:将.so文件添加到自己的项目中:

2)在Gradle中添加依赖:

// Bili required, enough for most devices.
compile 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
compile 'pub.devrel:easypermissions:1.0.1'

3)开始代码的实现

1.创建一个类CusIjkView,继承frameLayout,(自定义播放器样式)

代码如下:
// 打印日志的TAG
private String TAG = this.getClass().getSimpleName();
// mCurrentState is a  object's current state.
private int mCurrentState = PlayStateParams.STATE_IDLE;
// 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 mTargetState = PlayStateParams.STATE_IDLE;
// 播放地址通过客户端可设置
private Uri mUri;
// 播放器的一些基本配置
private Map<String, String> mHeaders;
// 所有我们需要播放和显示视频的东西
private IRenderView.ISurfaceHolder mSurfaceHolder = null;
// 视频宽度
private int mVideoWidth;
// 视频高度
private int mVideoHeight;
// 窗口宽度
private int mSurfaceWidth;
// 窗口高度
private int mSurfaceHeight;
// 视频旋转角度
private int mVideoRotationDegree;
// 媒体播放器
private IMediaPlayer mMediaPlayer;
// 媒体控制器
private IMediaController mMediaController;
// 播放完成监听
private IMediaPlayer.OnCompletionListener mOnCompletionListener;
// 播放准备监听
private IMediaPlayer.OnPreparedListener mOnPreparedListener;
// 播放缓冲监听
private int mCurrentBufferPercentage;
// 播放错误监听
private IMediaPlayer.OnErrorListener mOnErrorListener;
// 播放其他信息监听
private IMediaPlayer.OnInfoListener mOnInfoListener;
// 为记录seek位置而做准备
private long mSeekWhenPrepared;
private Context mAppContext;
private IRenderView mRenderView;
private int mVideoSarNum;
private int mVideoSarDen;
// 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];
// Extend: Render
public static final int RENDER_SURFACE_VIEW = 1;
public static final int RENDER_TEXTURE_VIEW = 2;
private List<Integer> mAllRenders = new ArrayList<>();
// 使用编解码器硬编码还是软编码,true 硬编码 false 为软编码
private boolean usingMediaCodec;
// 使用编解码是否自转
private boolean usingMediaCodecAutoRotate;
private boolean usingAndroidPlayer;
private boolean usingOpenSLES;
private boolean enableBackgroundPlay;public CusIjkView(Context context) {super(context);initVideoView(context);
}public CusIjkView(Context context, AttributeSet attrs) {super(context, attrs);initVideoView(context);
}public CusIjkView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initVideoView(context);
}/*** 初始化视频view*/
private void initVideoView(Context context) {mAppContext = context.getApplicationContext();initRenders();mVideoWidth = 0;mVideoHeight = 0;// 设置为可触摸并且获取焦点setFocusable(true);setFocusableInTouchMode(true);requestFocus();mCurrentState = PlayStateParams.STATE_IDLE;mTargetState = PlayStateParams.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.MATCH_PARENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER);renderUIView.setLayoutParams(lp);addView(renderUIView);mRenderView.addRenderCallback(mSHCallback);mRenderView.setVideoRotation(mVideoRotationDegree);
}/*** 设置旋转角度*/
public void setPlayerRotation(int rotation) {mVideoRotationDegree = rotation;if (mRenderView != null) {mRenderView.setVideoRotation(mVideoRotationDegree);}
}/*** 旋转渲染器*/
public void setRender(int render) {switch (render) {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;}
}/*** 设置视频路径*/
public void setVideoPath(String path) {setVideoURI(Uri.parse(path));
}/*** 设置视频URI*/
public void setVideoURI(Uri uri) {setVideoURI(uri, null);
}/*** Sets video URI using specific headers.** @param uri     the URI of the video.* @param headers the headers for the URI request.*                Note that the cross domain redirection is allowed by default, but that can be*                changed with key/value pairs through the headers parameter with*                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value*                to disallow or allow cross domain redirection.*/
private void setVideoURI(Uri uri, Map<String, String> headers) {mUri = uri;mHeaders = headers;mSeekWhenPrepared = 0;openVideo();requestLayout();invalidate();
}/*** 停止视频*/
public void stopPlayback() {if (mMediaPlayer != null) {mMediaPlayer.stop();mMediaPlayer.release();mMediaPlayer = null;mCurrentState = PlayStateParams.STATE_IDLE;mTargetState = PlayStateParams.STATE_IDLE;AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);if (am != null) {am.abandonAudioFocus(null);}}
}/*** 打开视频*/
private void openVideo() {if (mUri == null || mSurfaceHolder == null) {return;}release(false);AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);if (am != null) {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);}// Auto Select=,RGB 565=fcc-rv16,RGB 888X=fcc-rv32,YV12=fcc-yv12,默认为RGB 888XString pixelFormat = "";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);}if (mMediaPlayer == null) {return;}// 视频准备播放监听mMediaPlayer.setOnPreparedListener(mPreparedListener);// 视频播放完成监听mMediaPlayer.setOnCompletionListener(mCompletionListener);// 视频错误监听mMediaPlayer.setOnErrorListener(mErrorListener);// 视频其他信息监听mMediaPlayer.setOnInfoListener(mInfoListener);// 视频界面大小改变监听mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);// 视频缓冲监听mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);mCurrentBufferPercentage = 0;String scheme = mUri.getScheme();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M&& usingAndroidPlayer && (TextUtils.isEmpty(scheme) || scheme.equalsIgnoreCase("file"))) {IMediaDataSource dataSource = new FileMediaDataSource(new File(mUri.toString()));mMediaPlayer.setDataSource(dataSource);} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders);} else {mMediaPlayer.setDataSource(mUri.toString());}bindSurfaceHolder(mMediaPlayer, mSurfaceHolder);mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mMediaPlayer.setScreenOnWhilePlaying(true);mMediaPlayer.prepareAsync();// 这里不设置目标状态,但保护的目标状态mCurrentState = PlayStateParams.STATE_PREPARING;attachMediaController();} catch (IOException | IllegalArgumentException ex) {Log.w(TAG, "Unable to open content: " + mUri, ex);mCurrentState = PlayStateParams.STATE_ERROR;mTargetState = PlayStateParams.STATE_ERROR;mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);}
}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);}requestLayout();}}
};IMediaPlayer.OnPreparedListener mPreparedListener = new IMediaPlayer.OnPreparedListener() {public void onPrepared(IMediaPlayer mp) {// 这里不设置目标状态,但保护的目标状态mCurrentState = PlayStateParams.STATE_PREPARED;if (mOnPreparedListener != null) {mOnPreparedListener.onPrepared(mMediaPlayer);}if (mMediaController != null) {mMediaController.setEnabled(true);}if (mOnInfoListener != null) {mOnInfoListener.onInfo(mMediaPlayer, mCurrentState, 0);}mVideoWidth = mp.getVideoWidth();mVideoHeight = mp.getVideoHeight();long seekToPosition = mSeekWhenPrepared;if (seekToPosition != 0) {seekTo((int) seekToPosition);}if (mVideoWidth != 0 && mVideoHeight != 0) {if (mRenderView != null) {mRenderView.setVideoSize(mVideoWidth, mVideoHeight);mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);if (!mRenderView.shouldWaitForResize() || mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {if (mTargetState == PlayStateParams.STATE_PLAYING) {start();if (mMediaController != null) {mMediaController.show();}} else if (!isPlaying() &&(seekToPosition != 0 || getCurrentPosition() > 0)) {if (mMediaController != null) {mMediaController.show(0);}}}}} else {if (mTargetState == PlayStateParams.STATE_PLAYING) {start();}}}
};private IMediaPlayer.OnCompletionListener mCompletionListener = new IMediaPlayer.OnCompletionListener() {public void onCompletion(IMediaPlayer mp) {mCurrentState = PlayStateParams.STATE_COMPLETED;mTargetState = PlayStateParams.STATE_COMPLETED;if (mMediaController != null) {mMediaController.hide();}if (mOnCompletionListener != null) {mOnCompletionListener.onCompletion(mMediaPlayer);}if (mOnInfoListener != null) {mOnInfoListener.onInfo(mMediaPlayer, PlayStateParams.STATE_COMPLETED, 0);}}
};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 = PlayStateParams.STATE_ERROR;mTargetState = PlayStateParams.STATE_ERROR;if (mMediaController != null) {mMediaController.hide();}if (mOnErrorListener != null) {mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err);}if (mOnInfoListener != null) {if (mOnInfoListener.onInfo(mMediaPlayer, framework_err, 0)) {return true;}}if (getWindowToken() != null) {String message = "Unknown error";if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {message = "Invalid progressive playback";}new AlertDialog.Builder(getContext()).setMessage(message).setPositiveButton("error", new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int whichButton) {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.*/
public void setOnPreparedListener(IMediaPlayer.OnPreparedListener listener) {mOnPreparedListener = listener;
}/*** Register a callback to be invoked when the end of a media file has been reached during playback.*/
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.*/
public void setOnErrorListener(IMediaPlayer.OnErrorListener l) {mOnErrorListener = l;
}/*** Register a callback to be invoked when an informational event occurs during playback or setup.*/
public void setOnInfoListener(IMediaPlayer.OnInfoListener l) {mOnInfoListener = l;
}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() {@Overridepublic 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 == PlayStateParams.STATE_PLAYING);boolean hasValidSize = !mRenderView.shouldWaitForResize() || (mVideoWidth == w && mVideoHeight == h);if (mMediaPlayer != null && isValidState && hasValidSize) {if (mSeekWhenPrepared != 0) {seekTo((int) mSeekWhenPrepared);}start();}}@Overridepublic 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);elseopenVideo();}@Overridepublic void onSurfaceDestroyed(@NonNull IRenderView.ISurfaceHolder holder) {if (holder.getRenderView() != mRenderView) {Log.e(TAG, "onSurfaceDestroyed: unmatched render callback\n");return;}mSurfaceHolder = null;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;mCurrentState = PlayStateParams.STATE_IDLE;if (clearTargetState) {mTargetState = PlayStateParams.STATE_IDLE;}AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);if (am != null) {am.abandonAudioFocus(null);}}
}@Override
public boolean onTouchEvent(MotionEvent ev) {if (isInPlaybackState() && mMediaController != null) {toggleMediaControlsVisibility();}return false;
}@Override
public boolean onTrackballEvent(MotionEvent ev) {if (isInPlaybackState() && mMediaController != null) {toggleMediaControlsVisibility();}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 {toggleMediaControlsVisibility();}}return super.onKeyDown(keyCode, event);
}private void toggleMediaControlsVisibility() {if (mMediaController.isShowing()) {mMediaController.hide();} else {mMediaController.show();}
}@Override
public void start() {if (isInPlaybackState()) {mMediaPlayer.start();mCurrentState = PlayStateParams.STATE_PLAYING;}mTargetState = PlayStateParams.STATE_PLAYING;
}@Override
public void pause() {if (isInPlaybackState()) {if (mMediaPlayer.isPlaying()) {mMediaPlayer.pause();mCurrentState = PlayStateParams.STATE_PAUSED;}}mTargetState = PlayStateParams.STATE_PAUSED;
}public void onPause() {release(false);
}public void onResume() {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 != PlayStateParams.STATE_ERROR &&mCurrentState != PlayStateParams.STATE_IDLE &&mCurrentState != PlayStateParams.STATE_PREPARING);
}@Override
public boolean canPause() {return true;
}@Override
public boolean canSeekBackward() {return false;
}@Override
public boolean canSeekForward() {return false;
}@Override
public int getAudioSessionId() {return 0;
}public void toggleAspectRatio() {mCurrentAspectRatioIndex++;mCurrentAspectRatioIndex %= s_allAspectRatio.length;mCurrentAspectRatio = s_allAspectRatio[mCurrentAspectRatioIndex];if (mRenderView != null) {mRenderView.setAspectRatio(mCurrentAspectRatio);}
}/*** 初始化渲染器*/
private void initRenders() {mAllRenders.clear();mAllRenders.add(RENDER_SURFACE_VIEW); // 添加surface渲染int mCurrentRenderIndex;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {mAllRenders.add(RENDER_TEXTURE_VIEW); // 添加texture渲染mCurrentRenderIndex = 1;} else {mCurrentRenderIndex = 0;}int mCurrentRender = mAllRenders.get(mCurrentRenderIndex);setRender(mCurrentRender);
}/*** 设置播放区域拉伸类型*/
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;}}
}

2.播放器功能的实现类(代码中有功能的注释)

  // 通知使用的flagsprivate static final int MESSAGE_SHOW_PROGRESS = 1; // 同步进度private static final int MESSAGE_SEEK_NEW_POSITION = 3; // 设置新位置private static final int MESSAGE_HIDE_CENTER_BOX = 4; // 隐藏提示的boxprivate static final int MESSAGE_RESTART_PLAY = 5; // 重新播放// 控件部分private final Activity mActivity; // 依附的容器Activityprivate final LayoutQuery mLayoutQuery; // 界面的中布局的查询器private final CusIjkView mIjkView; // 原生IjkPlayerprivate final View mTopBarView; // 播放器顶部控制barprivate final View mBottomBarView; // 播放器底部控制barprivate final ImageView mThumbnailImg; // 播放前的封面或缩略图private final ImageView mBackImage; // 视频返回按钮private final ImageView mBottomBarPlayerImage; // 视频bottomBar的播放按钮private final ImageView mCenterPlayerImage; // 视频中间的播放按钮private final ImageView mFullScreenImage; // 视频全屏按钮private final TextView mPlayerSpeedTv; // 视频加载速度private final SeekBar mProgressSeekBar; // 视频播放进度条// 视频相关参数private List<VideoIjkBean> mListVideos = new ArrayList<>(); // 码流列表private int mCurrStatus = PlayStateParams.STATE_IDLE; // 当前状态private int mCurrShowType = PlayStateParams.FIT_PARENT; // 视频显示比例,默认保持原视频的大小private final AudioManager mAudioManager; // 音频管理器private String mCurrVideoUrl; // 当前播放地址private int mCurrPlayPosition; // 当前播放位置private long mNewPosition = -1; // 滑动进度条得到的新位置,和当前播放位置是有区别的,mNewPosition =0也会调用设置的,故初始化值为-1private int mCurrVolume; // 当前声音大小private int mMaxVolume = 0; // 设备最大音量private final int mScreenWidthPix; // 获取当前设备的宽度private final int mPlayerViewInitHeight; // 记录播放器竖屏时的高度private float mCurrBrightness; // 当前亮度大小private int mBgState; // 记录进行后台时的播放状态0为播放,1为暂停private int mAutoConnectTime = 5000; // 自动重连的时间private boolean mIsPlayerSupport; // 第三方so是否支持,默认不支持,true为支持private boolean mIsLive; // 是否是直播,默认为非直播,true为直播false为点播。mIsLive()方法判断直播的方式比较片面private boolean mIsForbidTouch; // 禁止触摸,默认可以触摸,true为禁止false为可触摸private boolean mIsForbidHideControlPanel; // 禁止收起控制面板,默认可以收起,true为禁止false为可触摸private boolean mIsForbidDoubleUp; // 是否禁止双击,默认不禁止,true为禁止,false为不禁止private boolean mIsDragging; // 是否在拖动进度条中,默认为停止拖动,true为在拖动中,false为停止拖动private boolean mIsErrorStop = true; // 是否出错停止播放,默认是出错停止播放,true出错停止播放,false为用户点击停止播放private boolean mIsOnlyFullScreen; // 是否只有全屏,默认非全屏,true为全屏,false为非全屏private boolean mIsPortrait = true; // 是否是竖屏,默认为竖屏,true为竖屏,false为横屏private boolean mIsAutoReConnect = true; // 是否自动重连,默认5秒重连,true为重连,false为不重连private boolean mIsHideCenterPlayer; // 是否隐藏中间播放按钮,默认不隐藏,true为隐藏,false为不隐藏private boolean mIsHideTopBar; // 是否隐藏topBar,true为隐藏,false为不隐藏private boolean mIsHideBottomBar; // 是否隐藏bottomBar,true为隐藏,false为不隐藏private boolean mIsShowControlPanel; // 是否显示控制面板,默认为隐藏,true为显示false为隐藏private boolean mIsCharge; // 是否免费观看private int mMaxPlaytime; // 设置最大播放时间private boolean mIsHasSwitchStream; // 当前是否切换视频流,默认为否,true是切换视频流,false没有切换private boolean mIsNetWorkTip; // 播放时是否有网络提示,true为显示网络提示,false不显示网络提示private boolean mIsFirstPlayer = true; // 是否是第一次播放// 视频和UI的回调监听private Handler mHandler = new CusUiHandler();private AutoPlayRunnable mAutoPlayRunnable = new AutoPlayRunnable(); // 控制面板收起或者显示的轮询监听private OnPlayerBackListener mPlayerBack; // 视频的返回键监听private IMediaPlayer.OnInfoListener mOnInfoListener; // 视频播放时信息回调private OrientationEventListener mOrientationEventListener; // Activity界面方向监听private OnControlVisibilityChangeListener mOnControlVisibilityChangeListener; // 控制面板显示或隐藏监听// ========================================== 初始化 ========================================== //public PlayerView(Activity activity, View rootView) {this.mActivity = activity;try {IjkMediaPlayer.loadLibrariesOnce(null);IjkMediaPlayer.native_profileBegin("libijkplayer.so");mIsPlayerSupport = true;} catch (Throwable e) {e.printStackTrace();}try {int e = Settings.System.getInt(mActivity.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS);float progress = 1.0F * (float) e / 255.0F;WindowManager.LayoutParams layout = this.mActivity.getWindow().getAttributes();layout.screenBrightness = progress;mActivity.getWindow().setAttributes(layout);} catch (Settings.SettingNotFoundException e) {e.printStackTrace();}mAudioManager = (AudioManager) mActivity.getSystemService(Context.AUDIO_SERVICE);if (mAudioManager != null) {mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);}// 播放器整个界面View rootBoxRL;if (rootView == null) {mLayoutQuery = new LayoutQuery(mActivity);rootBoxRL = mActivity.findViewById(R.id.app_video_box);mIjkView = mActivity.findViewById(R.id.video_view);mTopBarView = mActivity.findViewById(R.id.app_video_top_box);mBottomBarView = mActivity.findViewById(R.id.ll_bottom_bar);mThumbnailImg = mActivity.findViewById(R.id.iv_trumb);mBackImage = mActivity.findViewById(R.id.app_video_finish);mBottomBarPlayerImage = mActivity.findViewById(R.id.app_video_play);mCenterPlayerImage = mActivity.findViewById(R.id.play_icon);mFullScreenImage = mActivity.findViewById(R.id.app_video_fullscreen);mPlayerSpeedTv = mActivity.findViewById(R.id.app_video_speed);mProgressSeekBar = mActivity.findViewById(R.id.app_video_seekBar);mProgressSeekBar.setMax(1000);} else {mLayoutQuery = new LayoutQuery(mActivity, rootView);rootBoxRL = rootView.findViewById(R.id.app_video_box);mIjkView = rootView.findViewById(R.id.video_view);mTopBarView = rootView.findViewById(R.id.app_video_top_box);mBottomBarView = rootView.findViewById(R.id.ll_bottom_bar);mThumbnailImg = rootView.findViewById(R.id.iv_trumb);mBackImage = rootView.findViewById(R.id.app_video_finish);mBottomBarPlayerImage = rootView.findViewById(R.id.app_video_play);mCenterPlayerImage = rootView.findViewById(R.id.play_icon);mFullScreenImage = rootView.findViewById(R.id.app_video_fullscreen);mPlayerSpeedTv = rootView.findViewById(R.id.app_video_speed);mProgressSeekBar = rootView.findViewById(R.id.app_video_seekBar);mProgressSeekBar.setMax(1000);}mProgressSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {if (fromUser) {long duration = getDuration();int position = (int) ((duration * progress * 1.0) / 1000);String time = generateTime(position);mLayoutQuery.id(R.id.app_video_currentTime).text(time);mLayoutQuery.id(R.id.app_video_currentTime_full).text(time);mLayoutQuery.id(R.id.app_video_currentTime_left).text(time);}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {mIsDragging = true;mHandler.removeMessages(MESSAGE_SHOW_PROGRESS);}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {long duration = getDuration();mIjkView.seekTo((int) ((duration * seekBar.getProgress() * 1.0) / 1000));mHandler.removeMessages(MESSAGE_SHOW_PROGRESS);mIsDragging = false;mHandler.sendEmptyMessageDelayed(MESSAGE_SHOW_PROGRESS, 1000);}});View.OnClickListener onClickListener = new View.OnClickListener() {@Overridepublic void onClick(View v) {if (v.getId() == R.id.app_video_fullscreen) {toggleFullScreen();} else if (v.getId() == R.id.app_video_play || v.getId() == R.id.play_icon) {if (mIjkView.isPlaying()) {if (mIsLive) {mIjkView.stopPlayback();} else {pausePlay();}} else {startPlay();if (mIjkView.isPlaying()) {// TODO ijkPlayer内部的监听没有回调,只能手动修改状态mCurrStatus = PlayStateParams.STATE_PREPARING;hideStatusUI();}}updatePausePlay();} else if (v.getId() == R.id.app_video_finish) {backClick();} else if (v.getId() == R.id.app_video_netTie_icon) {// 使用移动网络提示继续播放mIsNetWorkTip = false;hideStatusUI();startPlay();updatePausePlay();} else if (v.getId() == R.id.app_video_replay_icon) {// 重新播放mCurrStatus = PlayStateParams.STATE_ERROR;hideStatusUI();startPlay();updatePausePlay();}}};mBottomBarPlayerImage.setOnClickListener(onClickListener);mCenterPlayerImage.setOnClickListener(onClickListener);mFullScreenImage.setOnClickListener(onClickListener);mBackImage.setOnClickListener(onClickListener);mLayoutQuery.id(R.id.app_video_netTie_icon).clicked(onClickListener);mLayoutQuery.id(R.id.app_video_replay_icon).clicked(onClickListener);mIjkView.setOnInfoListener(new IMediaPlayer.OnInfoListener() {@Overridepublic boolean onInfo(IMediaPlayer mp, int what, int extra) {if (what == PlayStateParams.MEDIA_INFO_NETWORK_BANDWIDTH || what == PlayStateParams.MEDIA_INFO_BUFFERING_BYTES_UPDATE) {if (mPlayerSpeedTv != null) {mPlayerSpeedTv.setText(getFormatSize(extra));}}statusChange(what);if (mOnInfoListener != null) {mOnInfoListener.onInfo(mp, what, extra);}if (mIsCharge && mMaxPlaytime < getCurrPlayPosition()) {mLayoutQuery.id(R.id.app_video_freeTie).visible();pausePlay();}return true;}});final GestureDetector detector = new GestureDetector(mActivity, new PlayerGestureListener());rootBoxRL.setClickable(true);rootBoxRL.setOnTouchListener(new View.OnTouchListener() {@SuppressLint("ClickableViewAccessibility")@Overridepublic boolean onTouch(View view, MotionEvent motionEvent) {switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN:if (mAutoPlayRunnable != null) {mAutoPlayRunnable.stop();}break;}if (detector.onTouchEvent(motionEvent)) {return true;}// 处理手势结束switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_UP:endGesture();break;default:break;}return false;}});mOrientationEventListener = new OrientationEventListener(mActivity) {@Overridepublic void onOrientationChanged(int orientation) {if (orientation >= 0 && orientation <= 30 || orientation >= 330 || (orientation >= 150 && orientation <= 210)) {if (mIsPortrait) {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);mOrientationEventListener.disable();}} else if ((orientation >= 90 && orientation <= 120) || (orientation >= 240 && orientation <= 300)) {if (!mIsPortrait) {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);mOrientationEventListener.disable();}}}};if (mIsOnlyFullScreen) {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);}mIsPortrait = (getScreenOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);mPlayerViewInitHeight = rootBoxRL.getLayoutParams().height;mScreenWidthPix = mActivity.getResources().getDisplayMetrics().widthPixels;hideAllUI();if (!mIsPlayerSupport) {showStatus(mActivity.getResources().getString(R.string.not_support));} else {mLayoutQuery.id(R.id.ll_bg).visible();}}// ========================================= 生命周期 ========================================= //public void onPause() {mBgState = (mIjkView.isPlaying() ? 0 : 1);getCurrPlayPosition();mIjkView.onPause();}public void onResume() {mIjkView.onResume();if (mIsLive) {mIjkView.seekTo(0);} else {mIjkView.seekTo(mCurrPlayPosition);}if (mBgState != 0) {pausePlay();}}public void onDestroy() {mOrientationEventListener.disable();mHandler.removeMessages(MESSAGE_RESTART_PLAY);mHandler.removeMessages(MESSAGE_SEEK_NEW_POSITION);mIjkView.stopPlayback();}public void onConfigurationChanged(final Configuration newConfig) {mIsPortrait = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT;doOnConfigurationChanged(mIsPortrait);}public boolean onBackPressed() {if (!mIsOnlyFullScreen && getScreenOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);doOnNoAllScreen();return true;}return false;}// ========================================= 对外接口 ========================================= ///*** 设置播放信息监听回调*/public void setOnInfoListener(IMediaPlayer.OnInfoListener onInfoListener) {this.mOnInfoListener = onInfoListener;}/*** 设置播放器中的返回键监听*/public void setPlayerBackListener(OnPlayerBackListener listener) {this.mPlayerBack = listener;}/*** 设置控制面板显示隐藏监听*/public void setOnControlPanelVisibilityChangListenter(OnControlVisibilityChangeListener listener) {this.mOnControlVisibilityChangeListener = listener;}/*** 显示缩略图*/public void showThumbnail(OnShowThumbnailListener onShowThumbnailListener) {if (onShowThumbnailListener != null && mThumbnailImg != null) {onShowThumbnailListener.onShowThumbnail(mThumbnailImg);}}/*** 设置播放地址*/public void setPlaySource(String stream, String url) {VideoIjkBean mVideoIjkBean = new VideoIjkBean();mVideoIjkBean.setStream(stream);mVideoIjkBean.setUrl(url);setPlaySource(mVideoIjkBean);}/*** 设置播放地址*/public void setPlaySource(VideoIjkBean videoIjkBean) {mListVideos.clear();if (videoIjkBean != null) {mListVideos.add(videoIjkBean);if (mListVideos.size() > 0) {mCurrVideoUrl = mListVideos.get(0).getUrl();mListVideos.get(0).setSelect(true);isLive();if (mIjkView.isPlaying()) {getCurrPlayPosition();mIjkView.release(false);}mIsHasSwitchStream = true;}}}/*** 开始播放*/public void startPlay() {if (mIsLive) {mIjkView.setVideoPath(mCurrVideoUrl);mIjkView.seekTo(0);} else {if (mIsHasSwitchStream || mCurrStatus == PlayStateParams.STATE_ERROR) {// TODO 换源之后声音可播,画面卡住,主要是渲染问题,目前只是提供了软解方式,后期提供设置方式mIjkView.setRender(CusIjkView.RENDER_TEXTURE_VIEW);mIjkView.setVideoPath(mCurrVideoUrl);mIjkView.seekTo(mCurrPlayPosition);mIsHasSwitchStream = false;}}hideStatusUI();if (mIsNetWorkTip && (NetworkUtils.getNetworkType(mActivity) == 4|| NetworkUtils.getNetworkType(mActivity) == 5|| NetworkUtils.getNetworkType(mActivity) == 6)) {mLayoutQuery.id(R.id.app_video_netTie).visible();} else {if (mIsCharge && mMaxPlaytime < getCurrPlayPosition()) {mLayoutQuery.id(R.id.app_video_freeTie).visible();} else {if (mIsPlayerSupport) {mIjkView.start();if (mIsFirstPlayer) {mLayoutQuery.id(R.id.app_video_loading).visible();mIsFirstPlayer = false;}} else {showStatus(mActivity.getResources().getString(R.string.not_support));}}}}/*** 暂停播放*/public void pausePlay() {mCurrStatus = PlayStateParams.STATE_PAUSED;getCurrPlayPosition();mIjkView.pause();}/*** 停止播放*/public void stopPlay() {mIjkView.stopPlayback();mIsErrorStop = true;if (mHandler != null) {mHandler.removeMessages(MESSAGE_RESTART_PLAY);}}public boolean isPlaying() {return mIjkView.isPlaying();}public void backClick() {if (!mIsOnlyFullScreen && !mIsPortrait) {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);doOnNoAllScreen();} else {if (mPlayerBack != null) {mPlayerBack.onPlayerBack();} else {mActivity.finish();}}}/*** 设置播放位置*/public void seekTo(int playtime) {mIjkView.seekTo(playtime);}/*** 获取当前播放位置*/public int getCurrPlayPosition() {if (!mIsLive) {mCurrPlayPosition = mIjkView.getCurrentPosition();} else {mCurrPlayPosition = -1;}return mCurrPlayPosition;}/*** 获取视频播放总时长*/public long getDuration() {return (long) mIjkView.getDuration();}/*** 百分比显示切换*/public void toggleAspectRatio() {if (mIjkView != null) {mIjkView.toggleAspectRatio();}}/*** 设置视频名称*/public void setTitle(String title) {mLayoutQuery.id(R.id.app_video_title).text(title);}/*** 设置最大观看时长** @param isCharge    true为收费 false为免费即不做限制* @param maxPlaytime 最大能播放时长,单位秒*/public void setChargeState(boolean isCharge, int maxPlaytime) {this.mIsCharge = isCharge;this.mMaxPlaytime = maxPlaytime * 1000;}/*** 设置播放区域拉伸类型*/public void setScaleType(int showType) {mCurrShowType = showType;mIjkView.setAspectRatio(mCurrShowType);}/*** 旋转指定角度*/public void setPlayerRotation(int rotation) {if (mIjkView != null) {mIjkView.setPlayerRotation(rotation);mIjkView.setAspectRatio(mCurrShowType);}}/*** 设置自动重连的模式或者重连时间,isAuto true 出错重连,false出错不重连,connectTime重连的时间*/public void setAutoReConnect(boolean isAuto, int connectTime) {this.mIsAutoReConnect = isAuto;this.mAutoConnectTime = connectTime;}/*** 是否仅仅为全屏*/public void setOnlyFullScreen(boolean isFull) {this.mIsOnlyFullScreen = isFull;tryFullScreen(mIsOnlyFullScreen);if (mIsOnlyFullScreen) {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);} else {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);}}/*** 设置是否禁止双击*/public void setForbidDoubleUp(boolean flag) {this.mIsForbidDoubleUp = flag;}/*** 设置是否禁止隐藏bar*/public void setForbidHideControlPanel(boolean flag) {this.mIsForbidHideControlPanel = flag;}/*** 是否禁止触摸*/public void forbidTouch(boolean forbidTouch) {this.mIsForbidTouch = forbidTouch;}/*** 隐藏返回键,true隐藏,false为显示*/public void hideBack(boolean isHide) {mBackImage.setVisibility(isHide ? View.GONE : View.VISIBLE);}/*** 隐藏全屏按钮,true隐藏,false为显示*/public void hideFullscreen(boolean isHide) {mFullScreenImage.setVisibility(isHide ? View.GONE : View.VISIBLE);}/*** 隐藏中间播放按钮,ture为隐藏,false为不做隐藏处理,但不是显示*/public void hideCenterPlayer(boolean isHide) {mIsHideCenterPlayer = isHide;mCenterPlayerImage.setVisibility(mIsHideCenterPlayer ? View.GONE : View.VISIBLE);}/*** 是否隐藏topbar,true为隐藏,false为不隐藏,但不一定是显示*/public void hideHideTopBar(boolean isHide) {mIsHideTopBar = isHide;mTopBarView.setVisibility(mIsHideTopBar ? View.GONE : View.VISIBLE);}/*** 是否隐藏bottonbar,true为隐藏,false为不隐藏,但不一定是显示*/public void hideBottomBar(boolean isHide) {mIsHideBottomBar = isHide;mBottomBarView.setVisibility(mIsHideBottomBar ? View.GONE : View.VISIBLE);}/*** 是否隐藏上下bar,true为隐藏,false为不隐藏,但不一定是显示*/public void hideControlPanl(boolean isHide) {hideBottomBar(isHide);hideHideTopBar(isHide);}/*** 显示菜单设置*/public void showMenu() {if (!mIsForbidHideControlPanel) {mTopBarView.setVisibility(View.GONE);mBottomBarView.setVisibility(View.GONE);}}/*** 设置显示加载网速或者隐藏*/public void setShowSpeed(boolean isShow) {mPlayerSpeedTv.setVisibility(isShow ? View.VISIBLE : View.GONE);}// ========================================= 内部实现 ========================================= ///*** 当前播放的是否是直播*/private void isLive() {mIsLive = mCurrVideoUrl != null&& (mCurrVideoUrl.startsWith("rtmp://")|| (mCurrVideoUrl.startsWith("http://") && mCurrVideoUrl.endsWith(".m3u8"))|| (mCurrVideoUrl.startsWith("http://") && mCurrVideoUrl.endsWith(".flv")));}/*** 隐藏所有界面*/private void hideAllUI() {if (!mIsForbidHideControlPanel) {mTopBarView.setVisibility(View.GONE);mBottomBarView.setVisibility(View.GONE);}hideStatusUI();}/*** 显示或隐藏操作面板*/private void operatorPanel() {mIsShowControlPanel = !mIsShowControlPanel;if (mIsShowControlPanel) {mTopBarView.setVisibility(mIsHideTopBar ? View.GONE : View.VISIBLE);mBottomBarView.setVisibility(mIsHideBottomBar ? View.GONE : View.VISIBLE);if (mIsLive) {mLayoutQuery.id(R.id.app_video_process_panl).invisible();} else {mLayoutQuery.id(R.id.app_video_process_panl).visible();}if (mIsOnlyFullScreen || mIsForbidDoubleUp) {mFullScreenImage.setVisibility(View.GONE);} else {mFullScreenImage.setVisibility(View.VISIBLE);}if (mOnControlVisibilityChangeListener != null) {mOnControlVisibilityChangeListener.change(true);}// 显示面板的时候再根据状态显示播放按钮if (mCurrStatus == PlayStateParams.STATE_PLAYING|| mCurrStatus == PlayStateParams.STATE_PREPARED|| mCurrStatus == PlayStateParams.STATE_PREPARING|| mCurrStatus == PlayStateParams.STATE_PAUSED) {if (mIsHideCenterPlayer) {mCenterPlayerImage.setVisibility(View.GONE);} else {mCenterPlayerImage.setVisibility(mIsLive ? View.GONE : View.VISIBLE);}} else {mCenterPlayerImage.setVisibility(View.GONE);}updatePausePlay();mHandler.sendEmptyMessage(MESSAGE_SHOW_PROGRESS);mAutoPlayRunnable.start();} else {if (mIsHideTopBar) {mTopBarView.setVisibility(View.GONE);} else {mTopBarView.setVisibility(mIsForbidHideControlPanel ? View.VISIBLE : View.GONE);}if (mIsHideBottomBar) {mBottomBarView.setVisibility(View.GONE);} else {mBottomBarView.setVisibility(mIsForbidHideControlPanel ? View.VISIBLE : View.GONE);}if (!mIsLive && mCurrStatus == PlayStateParams.STATE_PAUSED && !mIjkView.isPlaying()) {if (mIsHideCenterPlayer) {mCenterPlayerImage.setVisibility(View.GONE);} else {// 暂停时一直显示按钮mCenterPlayerImage.setVisibility(View.VISIBLE);}} else {mCenterPlayerImage.setVisibility(View.GONE);}mHandler.removeMessages(MESSAGE_SHOW_PROGRESS);if (mOnControlVisibilityChangeListener != null) {mOnControlVisibilityChangeListener.change(false);}mAutoPlayRunnable.stop();}}/*** 全屏切换*/private void toggleFullScreen() {if (getScreenOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);doOnNoAllScreen();} else {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);doOnAllScreen();}updateFullScreenButton();}/*** 进度条和时长显示的方向切换*/private void toggleProcessDurationOrientation() {setProcessDurationOrientation(PlayStateParams.PROCESS_PORTRAIT);}/*** 设置进度条和时长显示的方向,默认为上下显示,true为上下显示false为左右显示*/private void setProcessDurationOrientation(int portrait) {if (portrait == PlayStateParams.PROCESS_CENTER) {mLayoutQuery.id(R.id.app_video_currentTime_full).gone();mLayoutQuery.id(R.id.app_video_endTime_full).gone();mLayoutQuery.id(R.id.app_video_center).gone();mLayoutQuery.id(R.id.app_video_lift).visible();} else if (portrait == PlayStateParams.PROCESS_LANDSCAPE) {mLayoutQuery.id(R.id.app_video_currentTime_full).visible();mLayoutQuery.id(R.id.app_video_endTime_full).visible();mLayoutQuery.id(R.id.app_video_center).gone();mLayoutQuery.id(R.id.app_video_lift).gone();} else {mLayoutQuery.id(R.id.app_video_currentTime_full).gone();mLayoutQuery.id(R.id.app_video_endTime_full).gone();mLayoutQuery.id(R.id.app_video_center).visible();mLayoutQuery.id(R.id.app_video_lift).gone();}}/*** 获取界面方向*/private int getScreenOrientation() {int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();DisplayMetrics dm = new DisplayMetrics();mActivity.getWindowManager().getDefaultDisplay().getMetrics(dm);int width = dm.widthPixels;int height = dm.heightPixels;int orientation;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;}} 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;}/*** 状态改变同步UI*/private void statusChange(int newStatus) {if (newStatus == PlayStateParams.STATE_COMPLETED) {mCurrStatus = PlayStateParams.STATE_COMPLETED;mCurrPlayPosition = 0;hideAllUI();showStatus("播放结束");} else if (newStatus == PlayStateParams.STATE_PREPARING || newStatus == PlayStateParams.MEDIA_INFO_BUFFERING_START) {mCurrStatus = PlayStateParams.STATE_PREPARING;hideStatusUI(); // 视频缓冲mLayoutQuery.id(R.id.app_video_loading).visible();} else if (newStatus == PlayStateParams.MEDIA_INFO_VIDEO_RENDERING_START|| newStatus == PlayStateParams.STATE_PLAYING|| newStatus == PlayStateParams.STATE_PREPARED|| newStatus == PlayStateParams.MEDIA_INFO_BUFFERING_END|| newStatus == PlayStateParams.STATE_PAUSED) {if (mCurrStatus == PlayStateParams.STATE_PAUSED) {mCurrStatus = PlayStateParams.STATE_PAUSED;} else {mCurrStatus = PlayStateParams.STATE_PLAYING;}// 视频缓冲结束后隐藏缩列图mHandler.postDelayed(new Runnable() {@Overridepublic void run() {hideStatusUI();// 显示控制barmIsShowControlPanel = false;if (!mIsForbidTouch) {operatorPanel();}mLayoutQuery.id(R.id.ll_bg).gone(); // 延迟0.5秒隐藏视频封面隐藏}}, 500);} else if (newStatus == PlayStateParams.MEDIA_INFO_VIDEO_INTERRUPT) {// 直播停止推流mCurrStatus = PlayStateParams.STATE_ERROR;if (!(mIsNetWorkTip && (NetworkUtils.getNetworkType(mActivity) == 4|| NetworkUtils.getNetworkType(mActivity) == 5|| NetworkUtils.getNetworkType(mActivity) == 6))) {if (mIsCharge && mMaxPlaytime < getCurrPlayPosition()) {mLayoutQuery.id(R.id.app_video_freeTie).visible();} else {hideAllUI();if (mIsLive) {showStatus("获取不到直播源");} else {showStatus(mActivity.getResources().getString(R.string.small_problem));}// 5秒尝试重连if (!mIsErrorStop && mIsAutoReConnect) {mHandler.sendEmptyMessageDelayed(MESSAGE_RESTART_PLAY, mAutoConnectTime);}}} else {mLayoutQuery.id(R.id.app_video_netTie).visible();}} else if (newStatus == PlayStateParams.STATE_ERROR|| newStatus == PlayStateParams.MEDIA_INFO_UNKNOWN|| newStatus == PlayStateParams.MEDIA_ERROR_IO|| newStatus == PlayStateParams.MEDIA_ERROR_MALFORMED|| newStatus == PlayStateParams.MEDIA_ERROR_UNSUPPORTED|| newStatus == PlayStateParams.MEDIA_ERROR_TIMED_OUT|| newStatus == PlayStateParams.MEDIA_ERROR_SERVER_DIED) {mCurrStatus = PlayStateParams.STATE_ERROR;if (!(mIsNetWorkTip && (NetworkUtils.getNetworkType(mActivity) == 4|| NetworkUtils.getNetworkType(mActivity) == 5|| NetworkUtils.getNetworkType(mActivity) == 6))) {if (mIsCharge && mMaxPlaytime < getCurrPlayPosition()) {mLayoutQuery.id(R.id.app_video_freeTie).visible();} else {hideStatusUI();showStatus(mActivity.getResources().getString(R.string.small_problem));// 秒尝试重连if (!mIsErrorStop && mIsAutoReConnect) {mHandler.sendEmptyMessageDelayed(MESSAGE_RESTART_PLAY, mAutoConnectTime);}}} else {mLayoutQuery.id(R.id.app_video_netTie).visible();}}}/*** 显示视频播放状态提示*/private void showStatus(String statusText) {mLayoutQuery.id(R.id.app_video_replay).visible();mLayoutQuery.id(R.id.app_video_status_text).text(statusText);}/*** 界面方向改变时刷新界面*/private void doOnConfigurationChanged(final boolean portrait) {if (mIjkView != null && !mIsOnlyFullScreen) {mHandler.post(new Runnable() {@Overridepublic void run() {tryFullScreen(!portrait);if (portrait) {mLayoutQuery.id(R.id.app_video_box).height(mPlayerViewInitHeight, false);} else {int heightPixels = mActivity.getResources().getDisplayMetrics().heightPixels;int widthPixels = mActivity.getResources().getDisplayMetrics().widthPixels;mLayoutQuery.id(R.id.app_video_box).height(Math.min(heightPixels, widthPixels), false);}updateFullScreenButton();}});mOrientationEventListener.enable();}}private void doOnAllScreen() {setFullScreen(true);if (mIjkView != null && !mIsOnlyFullScreen) {mHandler.post(new Runnable() {@Overridepublic void run() {mIsPortrait = false;int heightPixels = mActivity.getResources().getDisplayMetrics().heightPixels;int widthPixels = mActivity.getResources().getDisplayMetrics().widthPixels;mLayoutQuery.id(R.id.app_video_box).height(Math.min(heightPixels, widthPixels), false);}});}}private void doOnNoAllScreen() {setFullScreen(false);if (mIjkView != null && !mIsOnlyFullScreen) {mHandler.post(new Runnable() {@Overridepublic void run() {mIsPortrait = true;mLayoutQuery.id(R.id.app_video_box).height(mPlayerViewInitHeight, false);}});}}/*** 设置界面方向*/private void setFullScreen(boolean fullScreen) {if (mActivity != null) {WindowManager.LayoutParams attrs = mActivity.getWindow().getAttributes();if (fullScreen) {attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;mActivity.getWindow().setAttributes(attrs);mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);} else {attrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);mActivity.getWindow().setAttributes(attrs);mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);}toggleProcessDurationOrientation();}}/*** 设置界面方向带隐藏actionbar*/private void tryFullScreen(boolean fullScreen) {if (mActivity instanceof AppCompatActivity) {ActionBar supportActionBar = ((AppCompatActivity) mActivity).getSupportActionBar();if (supportActionBar != null) {if (fullScreen) {supportActionBar.hide();} else {supportActionBar.show();}}}setFullScreen(fullScreen);}/*** 隐藏状态界面*/private void hideStatusUI() {mCenterPlayerImage.setVisibility(View.GONE);mLayoutQuery.id(R.id.app_video_replay).gone();mLayoutQuery.id(R.id.app_video_netTie).gone();mLayoutQuery.id(R.id.app_video_freeTie).gone();mLayoutQuery.id(R.id.app_video_loading).gone();if (mOnControlVisibilityChangeListener != null) {mOnControlVisibilityChangeListener.change(false);}}/*** 手势结束*/private void endGesture() {mCurrVolume = -1;mCurrBrightness = -1f;if (mNewPosition >= 0) {mHandler.removeMessages(MESSAGE_SEEK_NEW_POSITION);mHandler.sendEmptyMessage(MESSAGE_SEEK_NEW_POSITION);}mHandler.removeMessages(MESSAGE_HIDE_CENTER_BOX);mHandler.sendEmptyMessageDelayed(MESSAGE_HIDE_CENTER_BOX, 500);if (mAutoPlayRunnable != null) {mAutoPlayRunnable.start();}}/*** 同步进度*/private long syncProgress() {if (mIsDragging) {return 0;}long position = mIjkView.getCurrentPosition();long duration = mIjkView.getDuration();if (mProgressSeekBar != null) {if (duration > 0) {long pos = 1000L * position / duration;mProgressSeekBar.setProgress((int) pos);}int percent = mIjkView.getBufferPercentage();mProgressSeekBar.setSecondaryProgress(percent * 10);}if (mIsCharge && mMaxPlaytime + 1000 < getCurrPlayPosition()) {mLayoutQuery.id(R.id.app_video_freeTie).visible();pausePlay();} else {mLayoutQuery.id(R.id.app_video_currentTime).text(generateTime(position));mLayoutQuery.id(R.id.app_video_currentTime_full).text(generateTime(position));mLayoutQuery.id(R.id.app_video_currentTime_left).text(generateTime(position));mLayoutQuery.id(R.id.app_video_endTime).text(generateTime(duration));mLayoutQuery.id(R.id.app_video_endTime_full).text(generateTime(duration));mLayoutQuery.id(R.id.app_video_endTime_left).text(generateTime(duration));}return position;}/*** 更新播放、暂停和停止按钮*/private void updatePausePlay() {if (mIjkView.isPlaying()) {if (mIsLive) {mBottomBarPlayerImage.setImageResource(R.drawable.simple_player_stop_white_24dp);} else {mBottomBarPlayerImage.setImageResource(R.drawable.simple_player_icon_media_pause);mCenterPlayerImage.setImageResource(R.drawable.simple_player_center_pause);}} else {mBottomBarPlayerImage.setImageResource(R.drawable.simple_player_arrow_white_24dp);mCenterPlayerImage.setImageResource(R.drawable.simple_player_center_play);}}/*** 更新全屏和半屏按钮*/private void updateFullScreenButton() {if (getScreenOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {mFullScreenImage.setImageResource(R.drawable.simple_player_icon_fullscreen_shrink);} else {mFullScreenImage.setImageResource(R.drawable.simple_player_icon_fullscreen_stretch);}}/*** 滑动改变声音大小*/private void onVolumeSlide(float percent) {if (mCurrVolume == -1) {mCurrVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);if (mCurrVolume < 0) {mCurrVolume = 0;}}int index = (int) (percent * mMaxVolume) + mCurrVolume;if (index > mMaxVolume) {index = mMaxVolume;} else if (index < 0) {index = 0;}// 变更声音mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0);// 变更进度条int i = (int) (index * 1.0 / mMaxVolume * 100);String s = i + "%";if (i == 0) {s = "off";}// 显示mLayoutQuery.id(R.id.app_video_volume_icon).image(i == 0? R.drawable.simple_player_volume_off_white_36dp : R.drawable.simple_player_volume_up_white_36dp);mLayoutQuery.id(R.id.app_video_brightness_box).gone();mLayoutQuery.id(R.id.app_video_volume_box).visible();mLayoutQuery.id(R.id.app_video_volume_box).visible();mLayoutQuery.id(R.id.app_video_volume).text(s).visible();}/*** 快进或者快退滑动改变进度*/private void onProgressSlide(float percent) {int position = mIjkView.getCurrentPosition();long duration = mIjkView.getDuration();long deltaMax = Math.min(100 * 1000, duration - position);long delta = (long) (deltaMax * percent);mNewPosition = delta + position;if (mNewPosition > duration) {mNewPosition = duration;} else if (mNewPosition <= 0) {mNewPosition = 0;delta = -position;}int showDelta = (int) delta / 1000;if (showDelta != 0) {mLayoutQuery.id(R.id.app_video_fastForward_box).visible();String text = showDelta > 0 ? ("+" + showDelta) : "" + showDelta;mLayoutQuery.id(R.id.app_video_fastForward).text(text + "s");mLayoutQuery.id(R.id.app_video_fastForward_target).text(generateTime(mNewPosition) + "/");mLayoutQuery.id(R.id.app_video_fastForward_all).text(generateTime(duration));}}/*** 亮度滑动改变亮度*/private void onBrightnessSlide(float percent) {if (mCurrBrightness < 0) {mCurrBrightness = mActivity.getWindow().getAttributes().screenBrightness;if (mCurrBrightness <= 0.00f) {mCurrBrightness = 0.50f;} else if (mCurrBrightness < 0.01f) {mCurrBrightness = 0.01f;}}Log.d(this.getClass().getSimpleName(), "mCurrBrightness:" + mCurrBrightness + ",percent:" + percent);mLayoutQuery.id(R.id.app_video_brightness_box).visible();WindowManager.LayoutParams lpa = mActivity.getWindow().getAttributes();lpa.screenBrightness = mCurrBrightness + percent;if (lpa.screenBrightness > 1.0f) {lpa.screenBrightness = 1.0f;} else if (lpa.screenBrightness < 0.01f) {lpa.screenBrightness = 0.01f;}mLayoutQuery.id(R.id.app_video_brightness).text(((int) (lpa.screenBrightness * 100)) + "%");mActivity.getWindow().setAttributes(lpa);}/*** 时长格式化显示*/@SuppressLint("DefaultLocale")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 String getFormatSize(int size) {long fileSize = (long) size;String showSize = "";if (fileSize >= 0 && fileSize < 1024) {showSize = fileSize + "Kb/s";} else if (fileSize >= 1024 && fileSize < (1024 * 1024)) {showSize = Long.toString(fileSize / 1024) + "KB/s";} else if (fileSize >= (1024 * 1024) && fileSize < (1024 * 1024 * 1024)) {showSize = Long.toString(fileSize / (1024 * 1024)) + "MB/s";}return showSize;}// ========================================== 内部类 ========================================== //@SuppressLint("HandlerLeak")private class CusUiHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {// 滑动完成,隐藏滑动提示的boxcase MESSAGE_HIDE_CENTER_BOX:mLayoutQuery.id(R.id.app_video_volume_box).gone();mLayoutQuery.id(R.id.app_video_brightness_box).gone();mLayoutQuery.id(R.id.app_video_fastForward_box).gone();break;// 滑动完成,设置播放进度case MESSAGE_SEEK_NEW_POSITION:if (!mIsLive && mNewPosition >= 0) {mIjkView.seekTo((int) mNewPosition);mNewPosition = -1;}break;// 滑动中,同步播放进度case MESSAGE_SHOW_PROGRESS:long pos = syncProgress();if (!mIsDragging && mIsShowControlPanel) {msg = obtainMessage(MESSAGE_SHOW_PROGRESS);sendMessageDelayed(msg, 1000 - (pos % 1000));updatePausePlay();}break;// 重新去播放case MESSAGE_RESTART_PLAY:mCurrStatus = PlayStateParams.STATE_ERROR;startPlay();updatePausePlay();break;}}}/*** 收起控制面板轮询,默认5秒无操作,收起控制面板*/private class AutoPlayRunnable implements Runnable {private int AUTO_PLAY_INTERVAL = 5000;private boolean mShouldAutoPlay;AutoPlayRunnable() {mShouldAutoPlay = false;}void start() {if (!mShouldAutoPlay) {mShouldAutoPlay = true;mHandler.removeCallbacks(this);mHandler.postDelayed(this, AUTO_PLAY_INTERVAL);}}void stop() {if (mShouldAutoPlay) {mHandler.removeCallbacks(this);mShouldAutoPlay = false;}}@Overridepublic void run() {if (mShouldAutoPlay) {mHandler.removeCallbacks(this);if (!mIsForbidTouch) {operatorPanel();}}}}/*** 播放器的手势监听*/public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener {private boolean isDownTouch; // 是否是按下的标识,默认为其他动作,true为按下标识,false为其他动作private boolean isVolume; // 是否声音控制,默认为亮度控制,true为声音控制,false为亮度控制private boolean isLandscape; // 是否横向滑动,默认为纵向滑动,true为横向滑动,false为纵向滑动/*** 双击,视频视窗双击事件*/@Overridepublic boolean onDoubleTap(MotionEvent e) {
//            if (!mIsForbidTouch && !mIsOnlyFullScreen && !mIsForbidDoubleUp) {
//                toggleFullScreen();
//            }return true;}/*** 按下*/@Overridepublic boolean onDown(MotionEvent e) {isDownTouch = true;return super.onDown(e);}/*** 滑动*/@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {if (!mIsForbidTouch) {float mOldX = e1.getX(), mOldY = e1.getY();float deltaY = mOldY - e2.getY();float deltaX = mOldX - e2.getX();if (isDownTouch) {isLandscape = Math.abs(distanceX) >= Math.abs(distanceY);isVolume = mOldX > mScreenWidthPix * 0.5f;isDownTouch = false;}if (isLandscape) {if (!mIsLive) {// 进度设置onProgressSlide(-deltaX / mIjkView.getWidth());}} else {float percent = deltaY / mIjkView.getHeight();if (isVolume) {// 声音设置onVolumeSlide(percent);} else {// 亮度设置onBrightnessSlide(percent);}}}return super.onScroll(e1, e2, distanceX, distanceY);}/*** 单击,视频视窗单击事件*/@Overridepublic boolean onSingleTapUp(MotionEvent e) {if (!mIsForbidTouch) {operatorPanel();}return true;}}

3.具体的使用:

在你实现播放器的activity使用:

1)首先初始化播放器:

2)对视频进行网络判断

3)吊起播放:

项目地址:

https://download.csdn.net/download/wk_beicai/11177930

ijkplayer打造完美视频播放器相关推荐

  1. 视频教程-FFmpeg+OpenGL ES+OpenSL ES打造Android视频播放器-Android

    FFmpeg+OpenGL ES+OpenSL ES打造Android视频播放器 从事Android移动端开发多年.主导开发过直播.电商.聊天等各种类型APP和游戏SDK:熟悉Android音视频开发 ...

  2. 基于IJKPlayer的简易视频播放器

    写在前面 PS:没错,这就是那篇躺在草稿箱里好几个月的僵尸博客,直到现在(2017年1月中旬)才打算写完,简单总结一下知识点,以备不时之需. 现在的项目是一个电影预告的APP,必然得有个视频播放器,之 ...

  3. HTML5+CSS3+JQuery打造自定义视频播放器

    简介 HTML5的<video>标签已经被目前大多数主流浏览器所支持,包括还未正式发布的IE9也声明将支持<video>标签,利用浏览器原生特性嵌入视频有很多好处,所以很多开发 ...

  4. 使用CSS3+JQuery打造自定义视频播放器

    简介HTML5的<video>标签已经被目前大多数主流浏览器所支持,包括还未正式发布的IE9也声明将支持<video>标签,利用浏览器原生特性嵌入视频有很多好处,所以很多开发者 ...

  5. Exoplayer+Exomedia打造自定义视频播放器(一)

    写在前面的话 对App视频播放模块进行扩展,需要自定义播放器的样式.监听视频播放过程中各种事件(播放开始.暂停.重新播放.结束.拖拽进度条.横竖屏切换等).横竖屏切换.手动控制播放进度等. 自定义功能 ...

  6. FFmpeg+OpenGL ES+OpenSL ES打造Android视频播放器课程总结

    今天看完了视频播放器的全部课程.这个课程跳过了我认为重复的部分,主要是为了赶进度,快速地过了一遍.在这段时间,我简略地查阅了R语言的深度学习和CSAPP等知识点. 主要还是感觉得到数学的重要性,以前学 ...

  7. NiceVieoPlayer 完美视频播放器 解决黑屏坑问题

    重点部分大家都懂了,我主要来说说这里面的坑    就是在播放有些视频 出现黑屏或者播放一半左右出现播放错错误问题, 出现的原因就是android本身是不支持解析fmpeg协议  所以你以moudle形 ...

  8. Exoplayer+Exomedia打造自定义视频播放器

    转载自:https://blog.csdn.net/xunshishi/article/details/74171321 写在前面的话 对App视频播放模块进行扩展,需要自定义播放器的样式.监听视频播 ...

  9. C++打造暴风影音视频播放器项目,手把手教你打造个人播放器

    众所周知,RTMP是以flash为客户端播放器的直播协议,主要应用在B/S形式的场景中.本人研究并用C++开发实现了RTMP直播流协议的播放器,结合之前做的RTMP直播采集端,可以将RTMP协议完全扩 ...

最新文章

  1. python文字教程-Python在图片中添加文字的两种方法
  2. C++类里面的哪些成员函数是内联函数?
  3. IBM DW上关于SOA的规范归纳
  4. 浅谈C++类(6)--复制构造函数
  5. Qt 布局(水平、垂直、网格)
  6. 无表头单链表增删改查操作
  7. linux-centos7 常用的基本命令--文件内容查看、硬链接和软链接
  8. 2019国内某知名科技公司技术资料
  9. 20172324 2018-2019-1《程序设计与数据结构》实验1报告
  10. SLAM会议笔记(五)LLOAM
  11. android am start activity,adb shell am start -n package/.XXXactivity命令学习汇总
  12. python访问字符串中的部分字符的操作_小白学Python-13(字符串基础与简单操作)...
  13. Linux进程间通信(2)信号量
  14. 一个正经的前端学习 开源 仓库(阶段二十六)
  15. 程序猿的中秋节——浪漫至死不渝!!
  16. 土拍熔断意味着什么_熔断意味着什么
  17. 腾讯产品/策划笔试面试经验
  18. 优雅编程之这样使用枚举和注解,你就“正常”了(二十九)
  19. 学生写给老师的一封邮件
  20. Win7 UltraIso刻录时报错解决办法

热门文章

  1. 在准备播音主持专业艺考的过程中需要注意什么
  2. 百度搜索“一起自学吧”做大数据专家,不做第一,只做唯一
  3. OSChina 周五乱弹 —— 空即是色,色即是空
  4. 石家庄独特的地理位置容易出现雾霾天气
  5. TPC-DS 生成数据
  6. 中小型企业的网站建设策划方案怎么写?
  7. Python 直接读写硬盘扇区数据
  8. Android N编译之Out of memory error
  9. Oracle帐号转载
  10. Android如何把图片换一张,换手机了,怎么把相册里的照片转移到新手机?