03.视频播放器Api说明
目录介绍
- 01.最简单的播放
- 02.如何切换视频内核
- 03.切换视频模式
- 04.切换视频清晰度
- 05.视频播放监听
- 06.列表中播放处理
- 07.悬浮窗口播放
- 08.其他重要功能Api
- 09.播放多个视频
- 10.VideoPlayer相关Api
- 11.Controller相关Api
- 12.边播放边缓存api
- 13.类似抖音视频预加载
- 14.视频播放器埋点
00.视频播放器通用框架
- 基础封装视频播放器player,可以在ExoPlayer、MediaPlayer,声网RTC视频播放器内核,原生MediaPlayer可以自由切换
- 对于视图状态切换和后期维护拓展,避免功能和业务出现耦合。比如需要支持播放器UI高度定制,而不是该lib库中UI代码
- 针对视频播放,音频播放,播放回放,以及视频直播的功能。使用简单,代码拓展性强,封装性好,主要是和业务彻底解耦,暴露接口监听给开发者处理业务具体逻辑
- 该播放器整体架构:播放器内核(自由切换) + 视频播放器 + 边播边缓存 + 高度定制播放器UI视图层
- 项目地址:https://github.com/yangchong211/YCVideoPlayer
- 关于视频播放器整体功能介绍文档:https://juejin.im/post/6883457444752654343
01.最简单的播放
-
必须需要的四步骤代码如下所示
1//创建基础视频播放器,一般播放器的功能 2BasisVideoController controller = new BasisVideoController(this); 3//设置控制器 4mVideoPlayer.setVideoController(controller); 5//设置视频播放链接地址 6mVideoPlayer.setUrl(url); 7//开始播放 8mVideoPlayer.start(); -
开始播放
1//播放视频 2videoPlayer.start();
02.如何切换视频内核
-
创建视频播放器
1PlayerFactory playerFactory = IjkPlayerFactory.create(); 2IjkVideoPlayer ijkVideoPlayer = (IjkVideoPlayer) playerFactory.createPlayer(this); 3PlayerFactory playerFactory = ExoPlayerFactory.create(); 4ExoMediaPlayer exoMediaPlayer = (ExoMediaPlayer) playerFactory.createPlayer(this); 5PlayerFactory playerFactory = MediaPlayerFactory.create(); 6AndroidMediaPlayer androidMediaPlayer = (AndroidMediaPlayer) playerFactory.createPlayer(this); -
如何配置视频内核
1//播放器配置,注意:此为全局配置,例如下面就是配置ijk内核播放器 2VideoViewManager.setConfig(VideoPlayerConfig.newBuilder() 3 .setLogEnabled(true)//调试的时候请打开日志,方便排错 4 .setPlayerFactory(IjkPlayerFactory.create()) 5 .build()); -
切换视频内核处理代码
1@SuppressLint("SetTextI18n") 2private void setChangeVideoType(@ConstantKeys.PlayerType int type){ 3 //切换播放核心,不推荐这么做,我这么写只是为了方便测试 4 VideoPlayerConfig config = VideoViewManager.getConfig(); 5 try { 6 Field mPlayerFactoryField = config.getClass().getDeclaredField("mPlayerFactory"); 7 mPlayerFactoryField.setAccessible(true); 8 PlayerFactory playerFactory = null; 9 switch (type) { 10 case ConstantKeys.VideoPlayerType.TYPE_IJK: 11 playerFactory = IjkPlayerFactory.create(); 12 mTvTitle.setText("视频内核:" + " (IjkPlayer)"); 13 break; 14 case ConstantKeys.VideoPlayerType.TYPE_EXO: 15 playerFactory = ExoPlayerFactory.create(); 16 mTvTitle.setText("视频内核:" + " (ExoPlayer)"); 17 break; 18 case ConstantKeys.VideoPlayerType.TYPE_NATIVE: 19 playerFactory = MediaPlayerFactory.create(); 20 mTvTitle.setText("视频内核:" + " (MediaPlayer)"); 21 break; 22 case ConstantKeys.VideoPlayerType.TYPE_RTC: 23 break; 24 } 25 mPlayerFactoryField.set(config, playerFactory); 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } 29}
03.切换视频模式
-
关于全屏模式相关api
1//进入全屏 2mVideoPlayer.startFullScreen(); 3//退出全屏 4mVideoPlayer.stopFullScreen(); -
关于小窗口播放相关api
1//开启小屏 2mVideoPlayer.startTinyScreen(); 3//退出小屏 4mVideoPlayer.stopTinyScreen();
04.切换视频清晰度
05.视频播放监听
-
这个分为两部分:第一部分是播放模式监听,第二部分是播放状态监听,暴露给开发者。这里不建议使用0,1,非常不方便简明之意,采用注解限定。
1mVideoPlayer.setOnStateChangeListener(new OnVideoStateListener() { 2 /** 3 * 播放模式 4 * 普通模式,小窗口模式,正常模式三种其中一种 5 * MODE_NORMAL 普通模式 6 * MODE_FULL_SCREEN 全屏模式 7 * MODE_TINY_WINDOW 小屏模式 8 * @param playerState 播放模式 9 */ 10 @Override 11 public void onPlayerStateChanged(int playerState) { 12 switch (playerState) { 13 case ConstantKeys.PlayMode.MODE_NORMAL: 14 //普通模式 15 break; 16 case ConstantKeys.PlayMode.MODE_FULL_SCREEN: 17 //全屏模式 18 break; 19 case ConstantKeys.PlayMode.MODE_TINY_WINDOW: 20 //小屏模式 21 break; 22 } 23 } 24 25 /** 26 * 播放状态 27 * -1 播放错误 28 * 0 播放未开始 29 * 1 播放准备中 30 * 2 播放准备就绪 31 * 3 正在播放 32 * 4 暂停播放 33 * 5 正在缓冲(播放器正在播放时,缓冲区数据不足,进行缓冲,缓冲区数据足够后恢复播放) 34 * 6 暂停缓冲(播放器正在播放时,缓冲区数据不足,进行缓冲,此时暂停播放器,继续缓冲,缓冲区数据足够后恢复暂停 35 * 7 播放完成 36 * 8 开始播放中止 37 * @param playState 播放状态,主要是指播放器的各种状态 38 */ 39 @Override 40 public void onPlayStateChanged(int playState) { 41 switch (playState) { 42 case ConstantKeys.CurrentState.STATE_IDLE: 43 //播放未开始,初始化 44 break; 45 case ConstantKeys.CurrentState.STATE_START_ABORT: 46 //开始播放中止 47 break; 48 case ConstantKeys.CurrentState.STATE_PREPARING: 49 //播放准备中 50 break; 51 case ConstantKeys.CurrentState.STATE_PREPARED: 52 //播放准备就绪 53 break; 54 case ConstantKeys.CurrentState.STATE_ERROR: 55 //播放错误 56 break; 57 case ConstantKeys.CurrentState.STATE_BUFFERING_PLAYING: 58 //正在缓冲 59 break; 60 case ConstantKeys.CurrentState.STATE_PLAYING: 61 //正在播放 62 break; 63 case ConstantKeys.CurrentState.STATE_PAUSED: 64 //暂停播放 65 break; 66 case ConstantKeys.CurrentState.STATE_BUFFERING_PAUSED: 67 //暂停缓冲 68 break; 69 case ConstantKeys.CurrentState.STATE_COMPLETED: 70 //播放完成 71 break; 72 } 73 } 74});
06.在列表中播放
-
第一步:初始化视频播放器,创建VideoPlayer对象
1mVideoView = new VideoPlayer(context); 2mVideoView.setOnStateChangeListener(new VideoPlayer.SimpleOnStateChangeListener() { 3 @Override 4 public void onPlayStateChanged(int playState) { 5 //监听VideoViewManager释放,重置状态 6 if (playState == ConstantKeys.CurrentState.STATE_IDLE) { 7 PlayerUtils.removeViewFormParent(mVideoView); 8 mLastPos = mCurPos; 9 mCurPos = -1; 10 } 11 } 12}); 13mController = new BasisVideoController(context); 14mVideoView.setController(mController); -
第二步:设置RecyclerView和Adapter
1mAdapter.setOnItemChildClickListener(new OnItemChildClickListener() { 2 @Override 3 public void onItemChildClick(int position) { 4 //点击item播放视频 5 startPlay(position); 6 } 7}); 8mRecyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() { 9 @Override 10 public void onChildViewAttachedToWindow(@NonNull View view) { 11 12 } 13 14 @Override 15 public void onChildViewDetachedFromWindow(@NonNull View view) { 16 FrameLayout playerContainer = view.findViewById(R.id.player_container); 17 View v = playerContainer.getChildAt(0); 18 if (v != null && v == mVideoView && !mVideoView.isFullScreen()) { 19 //销毁视频 20 releaseVideoView(); 21 } 22 } 23}); -
第三步:播放视频和销毁视频的逻辑代码
1/** 2 * 开始播放 3 * @param position 列表位置 4 */ 5protected void startPlay(int position) { 6 if (mCurPos == position) return; 7 if (mCurPos != -1) { 8 releaseVideoView(); 9 } 10 VideoInfoBean videoBean = mVideos.get(position); 11 mVideoView.setUrl(videoBean.getVideoUrl()); 12 View itemView = mLinearLayoutManager.findViewByPosition(position); 13 if (itemView == null) return; 14 VideoRecyclerViewAdapter.VideoHolder viewHolder = (VideoRecyclerViewAdapter.VideoHolder) itemView.getTag(); 15 //把列表中预置的PrepareView添加到控制器中,注意isPrivate此处只能为true。 16 mController.addControlComponent(viewHolder.mPrepareView, true); 17 PlayerUtils.removeViewFormParent(mVideoView); 18 viewHolder.mPlayerContainer.addView(mVideoView, 0); 19 //播放之前将VideoView添加到VideoViewManager以便在别的页面也能操作它 20 VideoViewManager.instance().add(mVideoView, "list"); 21 mVideoView.start(); 22 mCurPos = position; 23} 24 25private void releaseVideoView() { 26 mVideoView.release(); 27 if (mVideoView.isFullScreen()) { 28 mVideoView.stopFullScreen(); 29 } 30 if(getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { 31 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 32 } 33 mCurPos = -1; 34}
08.其他重要功能Api
-
设置视频播放器背景图,和视频标题。
1//注意,下面这个controller是指BasisVideoController 2//设置视频背景图 3ImageView thumb = controller.getThumb(); 4Glide.with(this).load(R.drawable.image_default).into(controller.getThumb()); 5//设置视频标题 6controller.setTitle("视频标题"); -
判断是否锁屏
1//判断是否锁屏 2boolean locked = controller.isLocked(); 3//设置是否锁屏 4controller.setLocked(true); -
设置播放视频缩放类型。借鉴于网络博客,类似图片缩放。建议选择16:9类型,最常见
1mVideoPlayer.setScreenScaleType(ConstantKeys.PlayerScreenScaleType.SCREEN_SCALE_16_9); 2mVideoPlayer.setScreenScaleType(ConstantKeys.PlayerScreenScaleType.SCREEN_SCALE_DEFAULT); 3mVideoPlayer.setScreenScaleType(ConstantKeys.PlayerScreenScaleType.SCREEN_SCALE_4_3); 4mVideoPlayer.setScreenScaleType(ConstantKeys.PlayerScreenScaleType.SCREEN_SCALE_MATCH_PARENT); 5mVideoPlayer.setScreenScaleType(ConstantKeys.PlayerScreenScaleType.SCREEN_SCALE_ORIGINAL); 6mVideoPlayer.setScreenScaleType(ConstantKeys.PlayerScreenScaleType.SCREEN_SCALE_CENTER_CROP);
09.播放多个视频
-
这个举一个例子,比如同时播放两个视频,当然这种情况在app中可能比较少
1//必须设置 2player1.setUrl(VOD_URL_1); 3VideoPlayerBuilder.Builder builder = VideoPlayerBuilder.newBuilder(); 4builder.setEnableAudioFocus(false); 5VideoPlayerBuilder videoPlayerBuilder = new VideoPlayerBuilder(builder); 6player1.setVideoBuilder(videoPlayerBuilder); 7BasisVideoController controller1 = new BasisVideoController(this); 8player1.setController(controller1); 9mVideoViews.add(player1); 10 11//必须设置 12player2.setUrl(VOD_URL_2); 13VideoPlayerBuilder.Builder builder2 = VideoPlayerBuilder.newBuilder(); 14builder.setEnableAudioFocus(false); 15VideoPlayerBuilder videoPlayerBuilder2 = new VideoPlayerBuilder(builder2); 16player2.setVideoBuilder(videoPlayerBuilder2); 17BasisVideoController controller2 = new BasisVideoController(this); 18player2.setController(controller2); 19mVideoViews.add(player2); -
那么要是页面切换到后台,如何处理多个视频的暂停功能呢?如下所示:
1@Override 2protected void onPause() { 3 super.onPause(); 4 for (VideoPlayer vv : mVideoViews) { 5 vv.pause(); 6 } 7} 8 9@Override 10protected void onResume() { 11 super.onResume(); 12 for (VideoPlayer vv : mVideoViews) { 13 vv.pause(); 14 } 15} 16 17@Override 18protected void onDestroy() { 19 super.onDestroy(); 20 for (VideoPlayer vv : mVideoViews) { 21 vv.release(); 22 } 23} 24 25@Override 26public void onBackPressed() { 27 for (VideoPlayer vv : mVideoViews) { 28 if (vv.onBackPressed()) 29 return; 30 } 31 super.onBackPressed(); 32}
10.VideoPlayer相关Api
-
关于视频播放相关的api如下所示
1//暂停播放 2mVideoPlayer.pause(); 3//视频缓冲完毕,准备开始播放时回调 4mVideoPlayer.onPrepared(); 5//重新播放 6mVideoPlayer.replay(true); 7//继续播放 8mVideoPlayer.resume(); 9//调整播放进度 10mVideoPlayer.seekTo(100); 11//循环播放, 默认不循环播放 12mVideoPlayer.setLooping(true); 13//设置播放速度 14mVideoPlayer.setSpeed(1.1f); 15//设置音量 0.0f-1.0f 之间 16mVideoPlayer.setVolume(1,1); 17//开始播放 18mVideoPlayer.start(); -
关于视频切换播放模式相关api
1//判断是否处于全屏状态 2boolean fullScreen = mVideoPlayer.isFullScreen(); 3//是否是小窗口模式 4boolean tinyScreen = mVideoPlayer.isTinyScreen(); 5//进入全屏 6mVideoPlayer.startFullScreen(); 7//退出全屏 8mVideoPlayer.stopFullScreen(); 9//开启小屏 10mVideoPlayer.startTinyScreen(); 11//退出小屏 12mVideoPlayer.stopTinyScreen(); -
关于其他比如获取速度,音量,设置属性相关Api
1//VideoPlayer相关 2VideoPlayerBuilder.Builder builder = VideoPlayerBuilder.newBuilder(); 3VideoPlayerBuilder videoPlayerBuilder = new VideoPlayerBuilder(builder); 4//设置视频播放器的背景色 5builder.setPlayerBackgroundColor(Color.BLACK); 6//设置小屏的宽高 7int[] mTinyScreenSize = {0, 0}; 8builder.setTinyScreenSize(mTinyScreenSize); 9//是否开启AudioFocus监听, 默认开启 10builder.setEnableAudioFocus(false); 11mVideoPlayer.setVideoBuilder(videoPlayerBuilder); 12//截图 13Bitmap bitmap = mVideoPlayer.doScreenShot(); 14//移除所有播放状态监听 15mVideoPlayer.clearOnStateChangeListeners(); 16//获取当前缓冲百分比 17int bufferedPercentage = mVideoPlayer.getBufferedPercentage(); 18//获取当前播放器的状态 19int currentPlayerState = mVideoPlayer.getCurrentPlayerState(); 20//获取当前的播放状态 21int currentPlayState = mVideoPlayer.getCurrentPlayState(); 22//获取当前播放的位置 23long currentPosition = mVideoPlayer.getCurrentPosition(); 24//获取视频总时长 25long duration = mVideoPlayer.getDuration(); 26//获取倍速速度 27float speed = mVideoPlayer.getSpeed(); 28//获取缓冲速度 29long tcpSpeed = mVideoPlayer.getTcpSpeed(); 30//获取视频宽高 31int[] videoSize = mVideoPlayer.getVideoSize(); 32//是否处于静音状态 33boolean mute = mVideoPlayer.isMute();
11.Controller相关Api
-
Controller控制器相关的Api说明
1//设置视频背景图 2ImageView thumb = controller.getThumb(); 3Glide.with(this).load(R.drawable.image_default).into(controller.getThumb()); 4//设置视频标题 5controller.setTitle("视频标题"); 6//添加自定义视图。每添加一个视图,都是方式层级树的最上层 7CustomErrorView customErrorView = new CustomErrorView(this); 8controller.addControlComponent(customErrorView); 9//移除控制组件 10controller.removeControlComponent(customErrorView); 11//移除所有的组件 12controller.removeAllControlComponent(); 13//隐藏播放视图 14controller.hide(); 15//显示播放视图 16controller.show(); 17//是否开启根据屏幕方向进入/退出全屏 18controller.setEnableOrientation(true); 19//显示移动网络播放提示 20controller.showNetWarning(); 21//刘海的高度 22int cutoutHeight = controller.getCutoutHeight(); 23//是否有刘海屏 24boolean b = controller.hasCutout(); 25//设置是否适配刘海屏 26controller.setAdaptCutout(true); 27//停止刷新进度 28controller.stopProgress(); 29//开始刷新进度,注意:需在STATE_PLAYING时调用才会开始刷新进度 30controller.startProgress(); 31//判断是否锁屏 32boolean locked = controller.isLocked(); 33//设置是否锁屏 34controller.setLocked(true); 35//取消计时 36controller.stopFadeOut(); 37//开始计时 38controller.startFadeOut(); 39//设置播放视图自动隐藏超时 40controller.setDismissTimeout(8); 41//销毁 42controller.destroy();
12.边播放边缓存api
-
如下所示
1controller = new BasisVideoController(this); 2//设置视频背景图 3Glide.with(this).load(R.drawable.image_default).into(controller.getThumb()); 4//设置控制器 5mVideoPlayer.setController(controller); 6HttpProxyCacheServer server = new HttpProxyCacheServer(this); 7String proxyVideoUrl = server.getProxyUrl(URL_AD); 8mVideoPlayer.setUrl(proxyUrl); 9mVideoPlayer.start();
13.类似抖音视频预加载
-
如下所示,这个是针对ViewPager
1//获取PreloadManager预加载管理者对象 2mPreloadManager = PreloadManager.getInstance(this); 3//在播放视频的时候 4String playUrl = mPreloadManager.getPlayUrl(url); 5VideoLogUtils.i("startPlay: " + "position: " + position + " url: " + playUrl); 6mVideoPlayer.setUrl(playUrl); 7//在页面滚动的时候 8mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { 9 @Override 10 public void onPageScrollStateChanged(int state) { 11 super.onPageScrollStateChanged(state); 12 if (state == VerticalViewPager.SCROLL_STATE_IDLE) { 13 mPreloadManager.resumePreload(mCurPos, mIsReverseScroll); 14 } else { 15 mPreloadManager.pausePreload(mCurPos, mIsReverseScroll); 16 } 17 } 18}); -
如下所示,这个是针对RecyclerView
1recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { 2 /** 3 * 是否反向滑动 4 */ 5 private boolean mIsReverseScroll; 6 7 @Override 8 public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { 9 super.onScrolled(recyclerView, dx, dy); 10 if (dy>0){ 11 //表示下滑 12 mIsReverseScroll = false; 13 } else { 14 //表示上滑 15 mIsReverseScroll = true; 16 } 17 } 18 19 @Override 20 public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { 21 super.onScrollStateChanged(recyclerView, newState); 22 if (newState == VerticalViewPager.SCROLL_STATE_IDLE) { 23 mPreloadManager.resumePreload(mCurPos, mIsReverseScroll); 24 } else { 25 mPreloadManager.pausePreload(mCurPos, mIsReverseScroll); 26 } 27 } 28});
14.视频播放器埋点
-
代码如下所示,写一个类,实现BuriedPointEvent即可。即可埋点视频的播放次数,播放进度,点击视频广告啥的,方便统一管理
public class BuriedPointEventImpl implements BuriedPointEvent {
1/** 2 * 进入视频播放 3 * @param url 视频url 4 */ 5@Override 6public void playerIn(String url) { 7 8} 9 10/** 11 * 退出视频播放 12 * @param url 视频url 13 */ 14@Override 15public void playerDestroy(String url) { 16 17} 18 19/** 20 * 视频播放完成 21 * @param url 视频url 22 */ 23@Override 24public void playerCompletion(String url) { 25 26} 27 28/** 29 * 视频播放异常 30 * @param url 视频url 31 * @param isNetError 是否是网络异常 32 */ 33@Override 34public void onError(String url, boolean isNetError) { 35 36} 37 38/** 39 * 点击了视频广告 40 * @param url 视频url 41 */ 42@Override 43public void clickAd(String url) { 44 45} 46 47/** 48 * 退出视频播放时候的播放进度百度分 49 * @param url 视频url 50 * @param progress 视频进度,计算百分比【退出时候进度 / 总进度】 51 */ 52@Override 53public void playerOutProgress(String url, float progress) { 54 55} 56 57/** 58 * 视频切换音频 59 * @param url 视频url 60 */ 61@Override 62public void videoToMedia(String url) { 63 64}}
15.播放器示例展示图
