基础封装视频播放器player,可以在ExoPlayer、MediaPlayer原生MediaPlayer可以自由切换内核;该播放器整体架构:播放器内核(自由切换) + 视频播放器 + 边播边缓存 + 高度定制播放器UI视图层。支持视频简单播放,列表播放,仿抖音滑动播放,自动切换播放,使用案例丰富,拓展性强。
|
7 年之前 | |
---|---|---|
.idea | 7 年之前 | |
YCVideoPlayerLib | 7 年之前 | |
app | 7 年之前 | |
gradle | 7 年之前 | |
jiaozivideoplayer | 7 年之前 | |
.gitignore | 7 年之前 | |
LICENSE | 7 年之前 | |
README.md | 7 年之前 | |
build.gradle | 7 年之前 | |
gradle.properties | 7 年之前 | |
gradlew | 7 年之前 | |
gradlew.bat | 7 年之前 | |
settings.gradle | 7 年之前 |
1.1.6 可以设置竖屏模式下全屏模式和横屏模式下的全屏模式,方便多种使用场景
1.1.6 支持一遍播放一遍缓冲的功能,其中缓冲包括两部分,第一种是播放过程中缓冲,第二种是暂停过程中缓冲
1.1.7 基于ijkPlayer的封装播放器,支持多种格式视频播放
1.1.8 可以设置是否记录播放位置,设置播放速度,设置屏幕比例
1.1.9 支持滑动改变音量【屏幕右边】,改变屏幕亮度【屏幕左边】,支持切换视频清晰度模式
1.1.0 支持list页面中视频播放,滚动后暂停播放,播放可以自由设置是否记录状态。并且还支持删除视频播放位置状态。
C1产品需求:类似优酷,爱奇艺视频播放器部分逻辑。比如如果用户没有登录也没有看视频权限,则提示试看视频[自定义布局];如果用户没有登录但是有看视频权限,则正常观看;如果用户登录,但是没有充值会员,部分需要权限视频则进入试看模式,试看结束后弹出充值会员界面;如果用户余额不足,比如余额只有99元,但是视频观看要199元,则又有其他提示。
C2自身需求:比如封装好了视频播放库,那么点击视频上登录按钮则跳到登录页面;点击充值会员页面也跳到充值页面。这个通过定义接口,可以让使用者通过方法调用,灵活处理点击事件。
C.1.1 可以设置试看模式,设置试看时长。试看结束后就提示登录或者充值……
C.1.2 对于设置视频的宽高,建议设置成4:3或者16:9或者常用比例,如果不是常用比例,则可能会有黑边。其中黑边的背景可以设置
C.1.3 可以设置播放有权限的视频时的各种文字描述,而没有把它写在封装库中,使用者自己设定
C.1.4 锁定屏幕功能
D.1.1 可以支持屏幕截图功能,视频添加水印效果
D.1.2 支持弹幕功能
D.1.3 后期待定
至于官方库就不说了,jiecao的库是基于ijkplayer视频框架,目前封装库有许多,下面几个只是star比较多,其中jiecao库比较类似。
ijkplayer官方库
https://github.com/Bilibili/ijkplayer
Vitamio官方库
https://github.com/yixia/VitamioBundle
以jiecao为例的封装库
https://github.com/JasonChow1989/JieCaoVideoPlayer-develop 2年前
https://github.com/open-android/JieCaoVideoPlayer 1年前
https://github.com/lipangit/JiaoZiVideoPlayer 4个月前
https://github.com/CarGuo/GSYVideoPlayer
其他库
https://github.com/danylovolokh/VideoPlayerManager
针对视频播放页面布局,由于视频播放状态众多,我封装这库不同状态布局有十几种,许多库的视图布局没注释,显示比较臃肿,如果修改或者定位,不熟悉或者好久不操作,都要花时间找。展示我的布局代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--https://github.com/yangchong211-->
<!--如果你觉得好,请给个star,让更多人使用,避免重复造轮子-->
<!--底图,主要是显示视频缩略图-->
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:visibility="visible"/>
<!--加载动画view-->
<include layout="@layout/custom_video_player_loading"/>
<!--改变播放位置-->
<include layout="@layout/custom_video_player_change_position"/>
<!--改变亮度-->
<include layout="@layout/custom_video_player_change_brightness"/>
<!--改变声音-->
<include layout="@layout/custom_video_player_change_volume"/>
<!--播放完成,你也可以自定义-->
<include layout="@layout/custom_video_player_completed"/>
<!--播放错误-->
<include layout="@layout/custom_video_player_error"/>
<!--顶部控制区-->
<include layout="@layout/custom_video_player_top"/>
<!--底部控制区-->
<include layout="@layout/custom_video_player_bottom"/>
<!--右下角初始显示的总时长-->
<TextView
android:id="@+id/length"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_marginBottom="12dp"
android:layout_marginEnd="8dp"
android:padding="4dp"
android:visibility="visible"
android:text="00:00"
android:textColor="@android:color/white"
android:textSize="12sp"/>
<!--中间开始播放按钮-->
<ImageView
android:id="@+id/center_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/ic_player_center_start"
android:visibility="visible"/>
<!--试看按钮-->
<ImageView
android:id="@+id/iv_try_see"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/selector_try_see"
android:visibility="gone"/>
<!--试看布局,非会员显示该布局-->
<include layout="@layout/custom_video_player_try_see"/>
</RelativeLayout>
compile 'cn.yc:YCVideoPlayerLib:2.4.6'
如果宽高比变形,则会有黑边
<org.yczbj.ycvideoplayerlib.VideoPlayer
android:id="@+id/video_player"
android:layout_width="match_parent"
android:layout_height="240dp"/>
```
//设置播放类型
// IjkPlayer or MediaPlayer
videoPlayer1.setPlayerType(VideoPlayer.TYPE_NATIVE);
//网络视频地址
String videoUrl = DataUtil.getVideoListData().get(0).getVideoUrl();
//设置视频地址和请求头部
videoPlayer1.setUp(videoUrl, null);
//是否从上一次的位置继续播放
videoPlayer1.continueFromLastPosition(true);
//设置播放速度
videoPlayer1.setSpeed(1.0f);
//创建视频控制器
VideoPlayerController controller = new VideoPlayerController(this);
controller.setTitle("办快来围观拉,自定义视频播放器可以播放视频拉");
//设置视频时长
controller.setLength(98000);
//设置5秒不操作后则隐藏头部和底部布局视图
controller.setHideTime(5000);
//controller.setImage(R.drawable.image_default);
ImageUtil.loadImgByPicasso(this, R.drawable.image_default, R.drawable.image_default, controller.imageView());
//设置视频控制器
videoPlayer1.setController(controller);
```
```
//设置视频加载缓冲时加载窗的类型,多种类型
controller.setLoadingType(2);
ArrayList<String> content = new ArrayList<>();
content.add("试看结束,yc观看全部内容请开通会员1111。");
content.add("试看结束,yc观看全部内容请开通会员2222。");
content.add("试看结束,yc观看全部内容请开通会员3333。");
content.add("试看结束,yc观看全部内容请开通会员4444。");
controller.setMemberContent(content);
controller.setHideTime(5000);
//设置设置会员权限类型,第一个参数是否登录,第二个参数是否有权限看,第三个参数试看完后展示的文字内容,第四个参数是否保存进度位置
controller.setMemberType(false,false,3,true);
controller.imageView().setBackgroundResource(R.color.blackText);
//ImageUtil.loadImgByPicasso(this, R.color.blackText, R.drawable.image_default, controller.imageView());
//设置试看结束后,登录或者充值会员按钮的点击事件
controller.setOnMemberClickListener(new OnMemberClickListener() {
@Override
public void onClick(int type) {
switch (type){
case ConstantKeys.Gender.LOGIN:
//调到用户登录页面
startActivity(MeLoginActivity.class);
break;
case ConstantKeys.Gender.MEMBER:
//调到用户充值会员页面
startActivity(MeMemberActivity.class);
break;
default:
break;
}
}
});
```
如果是在Activity中的话,建议设置下面这段代码
@Override
protected void onStop() {
super.onStop();
VideoPlayerManager.instance().releaseVideoPlayer();
}
@Override
public void onBackPressed() {
if (VideoPlayerManager.instance().onBackPressed()) return;
super.onBackPressed();
}
如果是在Fragment中的话,建议设置下面这段代码
//在宿主Activity中设置代码如下
@Override
protected void onStop() {
super.onStop();
VideoPlayerManager.instance().releaseVideoPlayer();
}
@Override
public void onBackPressed() {
if (VideoPlayerManager.instance().onBackPressed()) return;
super.onBackPressed();
}
//--------------------------------------------------
//在此Fragment中设置代码如下
@Override
public void onStop() {
super.onStop();
VideoPlayerManager.instance().releaseVideoPlayer();
}
这两个变量控制全屏后和退出全屏的屏幕方向
<activity android:name=".ui.test2.TestMyActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:screenOrientation="portrait"/>
controller.setOnVideoBackListener(new OnVideoBackListener() {
@Override
public void onBackClick() {
onBackPressed();
}
});
controller.setOnPlayOrPauseListener(new OnPlayOrPauseListener() {
@Override
public void onPlayOrPauseClick(boolean isPlaying) {
}
});
controller.setOnMemberClickListener(new OnMemberClickListener() {
@Override
public void onClick(int type) {
switch (type){
case ConstantKeys.Gender.LOGIN:
ToastUtil.showToast(VideoPlayerMeActivity.this,"登录");
break;
case ConstantKeys.Gender.MEMBER:
ToastUtil.showToast(VideoPlayerMeActivity.this,"充值会员");
break;
default:
break;
}
}
});
controller.setOnVideoControlListener(new OnVideoControlListener() {
@Override
public void onVideoControlClick(int type) {
switch (type){
case ConstantKeys.VideoControl.DOWNLOAD:
ToastUtil.showToast(VideoPlayerMeActivity.this,"下载音视频");
break;
case ConstantKeys.VideoControl.AUDIO:
ToastUtil.showToast(VideoPlayerMeActivity.this,"切换音频");
break;
case ConstantKeys.VideoControl.SHARE:
ToastUtil.showToast(VideoPlayerMeActivity.this,"分享内容");
break;
default:
break;
}
}
});
第一步:在activity或者fragment中
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
VideoAdapter adapter = new VideoAdapter(this, DataUtil.getVideoListData());
recyclerView.setAdapter(adapter);
//注意:下面这个方法不能漏掉
recyclerView.setRecyclerListener(new RecyclerView.RecyclerListener() {
@Override
public void onViewRecycled(RecyclerView.ViewHolder holder) {
VideoPlayer videoPlayer = ((VideoAdapter.VideoViewHolder) holder).mVideoPlayer;
if (videoPlayer == VideoPlayerManager.instance().getCurrentVideoPlayer()) {
VideoPlayerManager.instance().releaseVideoPlayer();
}
}
});
第二步:在RecyclerView的适配器Adapter中
public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoViewHolder> {
private Context mContext;
private List<Video> mVideoList;
VideoAdapter(Context context, List<Video> videoList) {
mContext = context;
mVideoList = videoList;
}
@Override
public VideoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(mContext).inflate(R.layout.item_test_my_video, parent, false);
VideoViewHolder holder = new VideoViewHolder(itemView);
//创建视频播放控制器,主要只要创建一次就可以呢
VideoPlayerController controller = new VideoPlayerController(mContext);
holder.setController(controller);
return holder;
}
@Override
public void onBindViewHolder(VideoViewHolder holder, int position) {
Video video = mVideoList.get(position);
holder.bindData(video);
}
@Override
public int getItemCount() {
return mVideoList==null ? 0 : mVideoList.size();
}
class VideoViewHolder extends RecyclerView.ViewHolder {
VideoPlayerController mController;
VideoPlayer mVideoPlayer;
VideoViewHolder(View itemView) {
super(itemView);
mVideoPlayer = (VideoPlayer) itemView.findViewById(R.id.nice_video_player);
// 将列表中的每个视频设置为默认16:9的比例
ViewGroup.LayoutParams params = mVideoPlayer.getLayoutParams();
// 宽度为屏幕宽度
params.width = itemView.getResources().getDisplayMetrics().widthPixels;
// 高度为宽度的9/16
params.height = (int) (params.width * 9f / 16f);
mVideoPlayer.setLayoutParams(params);
}
/**
* 设置视频控制器参数
* @param controller 控制器对象
*/
void setController(VideoPlayerController controller) {
mController = controller;
mVideoPlayer.setController(mController);
}
void bindData(Video video) {
mController.setTitle(video.getTitle());
mController.setLength(video.getLength());
Glide.with(itemView.getContext())
.load(video.getImageUrl())
.placeholder(R.drawable.image_default)
.crossFade()
.into(mController.imageView());
mVideoPlayer.setUp(video.getVideoUrl(), null);
}
}
}
if (videoPlayer.isIdle()) {
Toast.makeText(this, "要点击播放后才能进入小窗口", Toast.LENGTH_SHORT).show();
} else {
videoPlayer.enterTinyWindow();
}
//设置竖屏方法上的全屏模式
mVideoPlayer.enterVerticalScreenScreen();
//设置横屏方向上的全屏模式
mVideoPlayer.enterFullScreen();
如果上线产品后不想打印日志,可以在初始化时设置,注意需要在初始化播放器之前设置
//如果不想打印库中的日志,可以设置
VideoLogUtil.isLog = false;
//设置播放类型
// IjkPlayer or MediaPlayer
videoPlayer1.setPlayerType(VideoPlayer.TYPE_NATIVE);
//设置视频地址和请求头部
videoPlayer1.setUp(videoUrl, null);
//创建视频控制器
VideoPlayerController controller = new VideoPlayerController(this);
//设置视频控制器
videoPlayer1.setController(controller);
//设置播放类型
// MediaPlayer
videoPlayer.setPlayerType(VideoPlayer.TYPE_NATIVE);
// IjkPlayer
videoPlayer.setPlayerType(VideoPlayer.TYPE_IJK);
//网络视频地址
String videoUrl = DataUtil.getVideoListData().get(1).getVideoUrl();
//设置视频地址和请求头部
videoPlayer.setUp(videoUrl, null);
//是否从上一次的位置继续播放
videoPlayer.continueFromLastPosition(false);
//设置播放速度
videoPlayer.setSpeed(1.0f);
//设置播放位置
//videoPlayer.seekTo(3000);
//设置音量
videoPlayer.setVolume(50);
//设置竖屏全屏播放
videoPlayer.enterVerticalScreenScreen();
//设置横屏全屏播放
videoPlayer.enterFullScreen();
//设置小屏幕播放
videoPlayer.enterTinyWindow();
//退出全屏
videoPlayer.exitFullScreen();
//退出小窗口播放
videoPlayer.exitTinyWindow();
//释放,内部的播放器被释放掉,同时如果在全屏、小窗口模式下都会退出
videoPlayer.release();
//释放播放器,注意一定要判断对象是否为空,增强严谨性
videoPlayer.releasePlayer();
//是否从上一次的位置继续播放,不必须
videoPlayer.continueFromLastPosition(false);
//获取最大音量
int maxVolume = videoPlayer.getMaxVolume();
//获取音量值
int volume = videoPlayer.getVolume();
//获取持续时长
long duration = videoPlayer.getDuration();
//获取播放位置
long currentPosition = videoPlayer.getCurrentPosition();
//获取缓冲区百分比
int bufferPercentage = videoPlayer.getBufferPercentage();
//获取播放速度
float speed = videoPlayer.getSpeed(1);
//开始播放
videoPlayer.start();
//开始播放,从某位置播放
videoPlayer.start(3000);
//重新播放
videoPlayer.restart();
//暂停播放
videoPlayer.pause();
//判断是否开始播放
boolean idle = videoPlayer.isIdle();
//判断视频是否播放准备中
boolean preparing = videoPlayer.isPreparing();
//判断视频是否准备就绪
boolean prepared = videoPlayer.isPrepared();
//判断视频是否正在缓冲
boolean bufferingPlaying = videoPlayer.isBufferingPlaying();
//判断是否是否缓冲暂停
boolean bufferingPaused = videoPlayer.isBufferingPaused();
//判断视频是否暂停播放
boolean paused = videoPlayer.isPaused();
//判断视频是否正在播放
boolean playing = videoPlayer.isPlaying();
//判断视频是否播放错误
boolean error = videoPlayer.isError();
//判断视频是否播放完成
boolean completed = videoPlayer.isCompleted();
//判断视频是否播放全屏
boolean fullScreen = videoPlayer.isFullScreen();
//判断视频是否播放小窗口
boolean tinyWindow = videoPlayer.isTinyWindow();
//判断视频是否正常播放
boolean normal = videoPlayer.isNormal();
//创建视频控制器
VideoPlayerController controller = new VideoPlayerController(this);
//设置视频标题
controller.setTitle("高仿优酷视频播放页面");
//设置视频时长
//controller.setLength(98000);
//设置视频加载缓冲时加载窗的类型,多种类型
controller.setLoadingType(2);
ArrayList<String> content = new ArrayList<>();
content.add("试看结束,观看全部内容请开通会员1111。");
content.add("试看结束,观看全部内容请开通会员2222。");
content.add("试看结束,观看全部内容请开通会员3333。");
content.add("试看结束,观看全部内容请开通会员4444。");
//设置会员权限话术内容
controller.setMemberContent(content);
//设置不操作后,5秒自动隐藏头部和底部布局
controller.setHideTime(5000);
//设置设置会员权限类型,第一个参数是否登录,第二个参数是否有权限看,第三个参数试看完后展示的文字内容,第四个参数是否保存进度位置
controller.setMemberType(false,false,3,true);
//设置背景图片
controller.imageView().setBackgroundResource(R.color.blackText);
//ImageUtil.loadImgByPicasso(this, R.color.blackText, R.drawable.image_default, controller.imageView());
//设置试看结束后,登录或者充值会员按钮的点击事件
controller.setOnMemberClickListener(new OnMemberClickListener() {
@Override
public void onClick(int type) {
switch (type){
case ConstantKeys.Gender.LOGIN:
//调到用户登录也米娜
startActivity(MeLoginActivity.class);
break;
case ConstantKeys.Gender.MEMBER:
//调到用户充值会员页面
startActivity(MeMemberActivity.class);
break;
default:
break;
}
}
});
//设置视频清晰度
//videoPlayer.setClarity(list,720);
//设置视频控制器
videoPlayer.setController(controller);
@Override
public void release() {
// 保存播放位置
if (isPlaying() || isBufferingPlaying() || isBufferingPaused() || isPaused()) {
VideoPlayerUtils.savePlayPosition(mContext, mUrl, getCurrentPosition());
} else if (isCompleted()) {
//如果播放完成,则保存播放位置为0,也就是初始位置
VideoPlayerUtils.savePlayPosition(mContext, mUrl, 0);
}
// 退出全屏或小窗口
if (isFullScreen()) {
exitFullScreen();
}
if (isTinyWindow()) {
exitTinyWindow();
}
mCurrentMode = MODE_NORMAL;
// 释放播放器
releasePlayer();
// 恢复控制器
if (mController != null) {
mController.reset();
}
// gc回收
Runtime.getRuntime().gc();
}
//释放播放器,注意一定要判断对象是否为空,增强严谨性
@Override
public void releasePlayer() {
if (mAudioManager != null) {
//放弃音频焦点。使以前的焦点所有者(如果有的话)接收焦点。
mAudioManager.abandonAudioFocus(null);
//置空
mAudioManager = null;
}
if (mMediaPlayer != null) {
//释放视频焦点
mMediaPlayer.release();
mMediaPlayer = null;
}
//从视图中移除TextureView
mContainer.removeView(mTextureView);
if (mSurface != null) {
mSurface.release();
mSurface = null;
}
//如果SurfaceTexture不为null,则释放
if (mSurfaceTexture != null) {
mSurfaceTexture.release();
mSurfaceTexture = null;
}
//设置状态
mCurrentState = STATE_IDLE;
}
可以直接看下面的参考案例,有记录
JZVideoPlayer为继承自FrameLayout实现的一个组合自定义View来实现了视频播放器的View相关的内容。
JZVideoPlayerStandard则是继承自JZVideoPlayer实现了一些自身的功能。
JZMediaManager是用来对于MediaPlayer的管理,对于MediaPlayer的一些监听器方法的回调和TextrueView的相关回调处理。
JZVideoPlayerManager管理JZVideoPlayer
和自定义相关的工作,最主要是先继承JCVideoPlayerStandard
JZMediaSystem主要是实现系统的播放引擎
不得不说,大神封装代码的思路以及代码逻辑的确很强
关于封装库其他感受
第一:不过,感觉大神更新频率大高,而且没有找到每次更新的日志说明,不知道大神又解决了那些bug
第二:黄色警告多,而且注释少,因为视频封装库不像一般库,有时候需求不同,可拓展性要求高。除了自己继承JCVideoPlayerStandard创建视频播放器,其他如果想改代码,还是有点复杂的。
第三:关于使用虽然很简单,但是在JZVideoPlayerStandard这个方法中,布局的对象都是用public修饰,如果你要想自己甚至某个控件背景或者图标等等,则要这样应用。如果你不去看看源代码中布局名称,你根本就不知道这个对象对应的是什么东西。对于不同修饰符,要合适的,如果不合适,那么就会有淡黄色警告。我看了buttonKnife,retrofit,阿里vlayout等等,可以说黄色警告很少……
Picasso.with(this)
.load("http://jzvd-pic.nathen.cn/jzvd-pic/1bb2ebbe-140d-4e2e-abd2-9e7e564f71ac.png")
.into(jzVideo.thumbImageView);
a4.而基于IjkMediaPlayer的视频播放,需要添加各种监听事件,通过阅读IMediaPlayer源码可以知道:可以在这些监听事件中添加各种对视频的操作逻辑,具体可以看代码。
void setOnPreparedListener(IMediaPlayer.OnPreparedListener var1);
void setOnCompletionListener(IMediaPlayer.OnCompletionListener var1);
void setOnBufferingUpdateListener(IMediaPlayer.OnBufferingUpdateListener var1);
void setOnSeekCompleteListener(IMediaPlayer.OnSeekCompleteListener var1);
void setOnVideoSizeChangedListener(IMediaPlayer.OnVideoSizeChangedListener var1);
void setOnErrorListener(IMediaPlayer.OnErrorListener var1);
void setOnInfoListener(IMediaPlayer.OnInfoListener var1);
void setOnTimedTextListener(IMediaPlayer.OnTimedTextListener var1);
a5.定义好了监听事件后,就创建了播放,重置播放,暂停等各种方法
//设置全屏播放
videoPlayer.enterFullScreen();
//设置小屏幕播放
videoPlayer.enterTinyWindow();
//退出全屏
videoPlayer.exitFullScreen();
//退出小窗口播放
videoPlayer.exitTinyWindow();
//释放,内部的播放器被释放掉,同时如果在全屏、小窗口模式下都会退出
videoPlayer.release();
//释放播放器,注意一定要判断对象是否为空,增强严谨性
videoPlayer.releasePlayer();
public class VideoPlayerManager {
private VideoPlayer mVideoPlayer;
private static VideoPlayerManager sInstance;
private VideoPlayerManager() {}
//一定要使用单例模式,保证同一时刻只有一个视频在播放,其他的都是初始状态
public static synchronized VideoPlayerManager instance() {
if (sInstance == null) {
sInstance = new VideoPlayerManager();
}
return sInstance;
}
public VideoPlayer getCurrentVideoPlayer() {
return mVideoPlayer;
}
void setCurrentVideoPlayer(VideoPlayer videoPlayer) {
if (mVideoPlayer != videoPlayer) {
releaseVideoPlayer();
mVideoPlayer = videoPlayer;
}
}
//当视频正在播放或者正在缓冲时,调用该方法暂停视频
public void suspendVideoPlayer() {
if (mVideoPlayer != null && (mVideoPlayer.isPlaying() || mVideoPlayer.isBufferingPlaying())) {
mVideoPlayer.pause();
}
}
//当视频暂停时或者缓冲暂停时,调用该方法重新开启视频播放
public void resumeVideoPlayer() {
if (mVideoPlayer != null && (mVideoPlayer.isPaused() || mVideoPlayer.isBufferingPaused())) {
mVideoPlayer.restart();
}
}
//释放,内部的播放器被释放掉,同时如果在全屏、小窗口模式下都会退出
public void releaseVideoPlayer() {
if (mVideoPlayer != null) {
mVideoPlayer.release();
mVideoPlayer = null;
}
}
//处理返回键逻辑.如果是全屏,则退出全屏 如果是小窗口,则退出小窗口
public boolean onBackPressed() {
if (mVideoPlayer != null) {
if (mVideoPlayer.isFullScreen()) {
return mVideoPlayer.exitFullScreen();
} else if (mVideoPlayer.isTinyWindow()) {
return mVideoPlayer.exitTinyWindow();
}
}
return false;
}
}
播放控制界面上,播放、暂停、播放进度、缓冲动画、全屏/小屏等触发都是直接调用播放器对应的操作的。
//创建视频控制器
VideoPlayerController controller = new VideoPlayerController(this);
//设置视频标题
controller.setTitle("高仿优酷视频播放页面");
//设置视频时长
//controller.setLength(98000);
//设置视频加载缓冲时加载窗的类型,多种类型
controller.setLoadingType(2);
ArrayList<String> content = new ArrayList<>();
content.add("试看结束,观看全部内容请开通会员1111。");
content.add("试看结束,观看全部内容请开通会员2222。");
content.add("试看结束,观看全部内容请开通会员3333。");
content.add("试看结束,观看全部内容请开通会员4444。");
//设置会员权限话术内容
controller.setMemberContent(content);
//设置不操作后,5秒自动隐藏头部和底部布局
controller.setHideTime(5000);
//设置设置会员权限类型,第一个参数是否登录,第二个参数是否有权限看,第三个参数试看完后展示的文字内容,第四个参数是否保存进度位置
controller.setMemberType(false,false,3,true);
//设置背景图片
controller.imageView().setBackgroundResource(R.color.blackText);
//ImageUtil.loadImgByPicasso(this, R.color.blackText, R.drawable.image_default, controller.imageView());
//设置试看结束后,登录或者充值会员按钮的点击事件
controller.setOnMemberClickListener(new OnMemberClickListener() {
@Override
public void onClick(int type) {
switch (type){
case ConstantKeys.Gender.LOGIN:
//调到用户登录也米娜
startActivity(MeLoginActivity.class);
break;
case ConstantKeys.Gender.MEMBER:
//调到用户充值会员页面
startActivity(MeMemberActivity.class);
break;
default:
break;
}
}
});
//设置视频清晰度
//videoPlayer.setClarity(list,720);
//设置视频控制器
videoPlayer.setController(controller);
/**
* 设置视频Url,以及headers
*
* @param url 视频地址,可以是本地,也可以是网络视频
* @param headers 请求header.
*/
void setUp(String url, Map<String, String> headers);
/**
* 开始播放
*/
void start();
/**
* 从指定的位置开始播放
*
* @param position 播放位置
*/
void start(long position);
/**
* 重新播放,播放器被暂停、播放错误、播放完成后,需要调用此方法重新播放
*/
void restart();
/**
* 暂停播放
*/
void pause();
/**
* seek到制定的位置继续播放
*
* @param pos 播放位置
*/
void seekTo(long pos);
/**
* 设置音量
*
* @param volume 音量值
*/
void setVolume(int volume);
/**
* 设置播放速度,目前只有IjkPlayer有效果,原生MediaPlayer暂不支持
*
* @param speed 播放速度
*/
void setSpeed(float speed);
/**
* 开始播放时,是否从上一次的位置继续播放
*
* @param continueFromLastPosition true 接着上次的位置继续播放,false从头开始播放
*/
void continueFromLastPosition(boolean continueFromLastPosition);
第二步:然后重写部分你需要更改功能的方法,只需要选择你需要重写的方法即可。
public class YCVideoPlayer extends VideoPlayer {
public YCVideoPlayer(Context context) {
super(context);
}
@Override
public void setUp(String url, Map<String, String> headers) {
super.setUp(url, headers);
}
@Override
public void setController(AbsVideoPlayerController controller) {
super.setController(controller);
}
@Override
public void setPlayerType(int playerType) {
super.setPlayerType(playerType);
}
@Override
public void continueFromLastPosition(boolean continueFromLastPosition) {
super.continueFromLastPosition(continueFromLastPosition);
}
@Override
public void setSpeed(float speed) {
super.setSpeed(speed);
}
@Override
public void start() {
super.start();
}
@Override
public void start(long position) {
super.start(position);
}
@Override
public void restart() {
super.restart();
}
@Override
public void pause() {
super.pause();
}
@Override
public void seekTo(long pos) {
super.seekTo(pos);
}
@Override
public void setVolume(int volume) {
super.setVolume(volume);
}
@Override
public boolean isIdle() {
return super.isIdle();
}
@Override
public boolean isPreparing() {
return super.isPreparing();
}
@Override
public boolean isPrepared() {
return super.isPrepared();
}
@Override
public boolean isBufferingPlaying() {
return super.isBufferingPlaying();
}
@Override
public boolean isBufferingPaused() {
return super.isBufferingPaused();
}
@Override
public boolean isPlaying() {
return super.isPlaying();
}
@Override
public boolean isPaused() {
return super.isPaused();
}
@Override
public boolean isError() {
return super.isError();
}
}
初期最简单功能
- 9.1.1 支持最简单视频播放,暂停,缓冲,全屏播放等基础功能。
- 9.1.2 支持滑动改变音量,改变声音大小的功能
9.1.3 还有其他基本功能
9.2 V1.0.1 更新于2017年11月18日**
最简单的封装,并且阅读相关视频案例,借鉴了相关思路和复用了部分代码
测试环节
9.3 v1.1.0 更新于2018年1月15日**
9.3.1 添加了设置视频播放权限的功能,用户可以自由设置权限,不过目前只是设置了用户是否登录,和登录用户是否有观看权限,因为公司需求是这样的,所以只有这两个。后期遇到其他需求再添加。逻辑已经在库中写好了,用户自己实现就可以呢。
9.3.2 关于权限肯定有话术内容,那么用户可以通过调用接口直接设置展示在播放器试看结束后的内容。十分方便,这块参考了优酷和爱奇艺视频
9.3.3 添加了用户多久不操作视频界面后,自动隐藏底部和头部布局视图。如果不设置,默认时间为5秒
9.3.4 添加了多种视频加载时候的加载效果,目前有两种,一种是转圈效果,一种是帧动画效果。当然你可以自己添加动画加载效果
9.4 v1.1.1 更新于2018年1月18日**
9.4.1 修改了视频横向播放时,点击手机物理返回键,画面展示状态栏问题
9.4.2 修改了在list页面(recyclerView)的视频,当上拉加载更多时,加载十几次会导致崩溃问题
9.4.3 精简了布局文件,方便修改定制和阅读
9.4.4 修改了在网络不好或者飞行模式下,用户播放视频,应该是播放错误而不是一直转圈加载问题
9.4.5 完善了代码的注释,现在几乎所有的方法都有相关注释,方便阅读和理解。去掉了无用的代码
9.4.6 添加了暴露接口之用户登录和用户购买会员的接口,用户可以自己实现监听之后的操作或者跳转页面
9.4.7 添加了视频左上方的返回键监听,用户可以自己实现返回逻辑
9.4.8 添加了锁定屏幕方向的功能,还在测试中,有点问题
9.6 v1.3.0 更新于2018年8月2日
9.6.1 添加了竖屏下的全屏播放模式
9.6.2 解决了横屏下全屏播放模式的导航栏显示问题
https://github.com/CarGuo/GSYVideoPlayer
https://github.com/danylovolokh/VideoPlayerManager
https://github.com/HotBitmapGG/bilibili-android-client
https://github.com/jjdxmashl/jjdxm_ijkplayer
https://github.com/JasonChow1989/JieCaoVideoPlayer-develop 2年前
https://github.com/open-android/JieCaoVideoPlayer 1年前
https://github.com/lipangit/JiaoZiVideoPlayer 4个月前
https://github.com/xiaoyanger0825/NiceVieoPlayer
https://github.com/curtis2/SuperVideoPlayer
https://github.com/tcking/GiraffePlayer
https://segmentfault.com/a/1190000011959615
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1213/2153.html
http://blog.csdn.net/junwang19891012/article/details/8444743
https://www.jianshu.com/p/420f7b14d6f6
http://blog.csdn.net/candicelijx/article/details/39495271
1.1 VideoView 的使用非常简单,播放视频的步骤:
11.2.2.Google 开源视频播放框架 ExoPlayer
2.1 框架地址:https://github.com/google/ExoPlayer
2.2 用法
2.3 优缺点
ExoPlayer 相较于 MediaPlayer 有很多很多的优点:
ExoPlayer 的缺点:
11.2.3.Vitamio 视频播放框架
3.1 用法
Vitamio 的使用步骤:
3.2 优点
11.2.4.Bilibili 开源视频播放框架ijkplayer
4.1 特点
4.2 优缺点