JiaoZiVideoPlayer视频学习

一、前言:

饺子 视频GitHub:
入门文档 1:
入门文档 2:
常见问题:

主要特点

  1. 可以完全自定义UI和任何功能
  2. 一行代码切换播放引擎,支持的视频格式和协议取决于播放引擎,android.media.MediaPlayer ijkplayer
  3. 可实现全屏播放,小窗播放
  4. 能在ListView、ViewPager和ListView、ViewPager和Fragment等多重嵌套模式下全屏工作
  5. 可以在加载、暂停、播放等各种状态中正常进入全屏和退出全屏
  6. 多种视频适配屏幕的方式,可铺满全屏,可以全屏剪裁
  7. 重力感应自动进入全屏
  8. 全屏后手势修改进度和音量
  9. Home键退出界面暂停播放,返回界面继续播放
  10. WebView嵌套本地视频控件
  11. demo中添加视频缓存的例子
  12. 完美检测列表滑动

二、基本使用

即便是自定义UI,或者对Library有过修改,也是这五步骤来使用播放器。

1. 添加类库
//饺子视频播放器
implementation 'cn.jzvd:jiaozivideoplayer:7.0.5'

2. 添加布局


自定义的CustJzvdStd布局 View

/*** 这里可以监听到视频播放的生命周期和播放状态* 所有关于视频的逻辑都应该写在这里* Created by Nathen on 2017/7/2.*/
public class CustJzvdStd extends JzvdStd {private CommonInterface.OnVideoCompletion onVideoCompletion;public CustJzvdStd(Context context) {super(context);}public CustJzvdStd(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void init(Context context) {super.init(context);}@Overridepublic void setUp(JZDataSource jzDataSource, int screen) {super.setUp(jzDataSource, screen);if (this.screen == SCREEN_FULLSCREEN) {titleTextView.setVisibility(View.VISIBLE);} else {titleTextView.setVisibility(View.INVISIBLE);}}@Overridepublic void onClick(View v) {super.onClick(v);int i = v.getId();if (i == cn.jzvd.R.id.fullscreen) {Log.i(TAG, "onClick: fullscreen button");} else if (i == R.id.start) {Log.i(TAG, "onClick: start button");}}@Overridepublic boolean onTouch(View v, MotionEvent event) {super.onTouch(v, event);int id = v.getId();if (id == cn.jzvd.R.id.surface_container) {switch (event.getAction()) {case MotionEvent.ACTION_UP:if (mChangePosition) {Log.i(TAG, "Touch screen seek position");}if (mChangeVolume) {Log.i(TAG, "Touch screen change volume");}break;}}return false;}@Overridepublic int getLayoutId() {return R.layout.jz_layout_std;}@Overridepublic void startVideo() {super.startVideo();Log.i(TAG, "startVideo");}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {super.onStopTrackingTouch(seekBar);Log.i(TAG, "Seek position ");}@Overridepublic void gotoScreenFullscreen() {super.gotoScreenFullscreen();Log.i(TAG, "goto Fullscreen");titleTextView.setVisibility(View.VISIBLE);}@Overridepublic void gotoScreenNormal() {super.gotoScreenNormal();Log.i(TAG, "quit Fullscreen");titleTextView.setVisibility(View.INVISIBLE);}@Overridepublic void autoFullscreen(float x) {super.autoFullscreen(x);Log.i(TAG, "auto Fullscreen");}@Overridepublic void onClickUiToggle() {super.onClickUiToggle();Log.i(TAG, "click blank");}//onState 代表了播放器引擎的回调,播放视频各个过程的状态的回调@Overridepublic void onStateNormal() {super.onStateNormal();}@Overridepublic void onStatePreparing() {super.onStatePreparing();}@Overridepublic void onStatePlaying() {super.onStatePlaying();}@Overridepublic void onStatePause() {super.onStatePause();}@Overridepublic void onStateError() {super.onStateError();}@Overridepublic void onStateAutoComplete() {super.onStateAutoComplete();Log.i(TAG, "Auto complete");}//changeUiTo 真能能修改ui的方法@Overridepublic void changeUiToNormal() {super.changeUiToNormal();}@Overridepublic void changeUiToPreparing() {super.changeUiToPreparing();}@Overridepublic void changeUiToPlayingShow() {super.changeUiToPlayingShow();}@Overridepublic void changeUiToPlayingClear() {super.changeUiToPlayingClear();}@Overridepublic void changeUiToPauseShow() {super.changeUiToPauseShow();}@Overridepublic void changeUiToPauseClear() {super.changeUiToPauseClear();}@Overridepublic void changeUiToComplete() {super.changeUiToComplete();}@Overridepublic void changeUiToError() {super.changeUiToError();}@Overridepublic void onInfo(int what, int extra) {super.onInfo(what, extra);}@Overridepublic void onError(int what, int extra) {super.onError(what, extra);}/*** 播放完成*/@Overridepublic void onAutoCompletion() {super.onAutoCompletion();//播完完,回调这个方法onVideoCompletion.onVideoCompletion();}public void setOnVideoCompletion(CommonInterface.OnVideoCompletion onVideoCompletion){this.onVideoCompletion = onVideoCompletion;}
}

TRTCActivity 使用
我们可能遇到一个问题,视频不能充满整个布局,周围都是黑边,因此我们需要自定义布局大小。
代码如下:

/*** 视频播放和直播展示界面*/
public class TRTCActivity extends AppCompatActivity {@BindView(R.id.tv_tip)TextView tvTip;private static String TAG = "LUO";@BindView(R.id.videoplayer)CustJzvdStd videoplayer;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 应用运行时,保持不锁屏、全屏化getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);requestWindowFeature(Window.FEATURE_NO_TITLE);getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);setContentView(R.layout.activity_trtc);ButterKnife.bind(this);//播放视频videoPlay();}/*** 播放视频*/private void videoPlay() {//设置视频大小setImageSize();//设置播放视频的 URLvideoplayer.setUp("https://sumansoul.oss-cn-shanghai.aliyuncs.com/public/video/6732627438732659604.MP4", "", JzvdStd.SYSTEM_UI_FLAG_VISIBLE);//设置图片大小videoplayer.thumbImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);//设置视频裁剪方式Jzvd.setVideoImageDisplayType(Jzvd.VIDEO_IMAGE_DISPLAY_TYPE_FILL_SCROP);Glide.with(this).load("http://jzvd-pic.nathen.cn/jzvd-pic/1bb2ebbe-140d-4e2e-abd2-9e7e564f71ac.png").into(videoplayer.thumbImageView);//只有开始时播放时间videoplayer.seekToInAdvance = 10000;//自动播放//myJzvdStd.startVideo();//倒计时播放countDown();}/*** 倒计时显示*/private void countDown() {CountDownTimer timer = new CountDownTimer(5000, 1000) {@Overridepublic void onTick(long millisUntilFinished) {tvTip.setText("视频正在加载中" + millisUntilFinished / 1000 + "s");}@Overridepublic void onFinish() {tvTip.setVisibility(View.GONE);//自动播放videoplayer.startVideo();}}.start();}/*** 视频设置控件大小*/private void setImageSize() {//控件大小videoplayer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) videoplayer.getLayoutParams();// -DensityUtils.dp2px(context,14*2)    params.getMarginStart()int width = ScreenUtils.getScreenWidth();//int h=(int)(width*9/16+0.5);//16:9int h = (int) (width + 2);params.height = h;//将设置好的布局参数应用到控件中videoplayer.setLayoutParams(params);//设置容器内播放器高Jzvd.setVideoImageDisplayType(Jzvd.VIDEO_IMAGE_DISPLAY_TYPE_FILL_PARENT);//Jzvd.setVideoImageDisplayType(Jzvd.VIDEO_IMAGE_DISPLAY_TYPE_FILL_SCROP);}});}@Overrideprotected void onPause() {super.onPause();Jzvd.releaseAllVideos();}@Overridepublic void onBackPressed() {if (Jzvd.backPress()) {return;}super.onBackPressed();}
}
3.设置视频地址、缩略图地址、标题
 MyJzvdStd myJzvdStd= findViewById(R.id.videoplayer);myJzvdStd.setUp("http://jzvd.nathen.cn/342a5f7ef6124a4a8faf00e738b8bee4/cf6d9db0bd4d41f59d09ea0a81e918fd-5287d2089db37e62345123a1be272f8b.mp4", "饺子快长大", JzvdStd.SCREEN_WINDOW_NORMAL);Glide.with(this).load("http://jzvd-pic.nathen.cn/jzvd-pic/1bb2ebbe-140d-4e2e-abd2-9e7e564f71ac.png").into(myJzvdStd.thumbImageView);
4. 在Activity中
 @Overrideprotected void onPause() {super.onPause();Jzvd.releaseAllVideos();}@Overridepublic void onBackPressed() {if (Jzvd.backPress()) {return;}super.onBackPressed();}
5. 在AndroidManifest.xml中
 

三、功能使用

1. 加缩略载图

Glide.with(this).load(Url).into(myJzvdStd.thumbImageView); //推荐使用Glide

2. 自动播放有两种 这里随便选择添加一个
  1. myJzvdStd.startButton.performClick();
  2. myJzvdStd.startVideo();
3. 跳转制定位置播放

//这里只有开始播放时才生效
mJzvdStd.seekToInAdvance = 20000;
//跳转制定位置播放
JZMediaManager.seekTo(30000)

4. 播放sd卡下视频
public void cpAssertVideoToLocalPath() {try {InputStream myInput;OutputStream myOutput = new FileOutputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera/local_video.mp4");myInput = this.getAssets().open("local_video.mp4");byte[] buffer = new byte[1024];int length = myInput.read(buffer);while (length > 0) {myOutput.write(buffer, 0, length);length = myInput.read(buffer);}myOutput.flush();myInput.close();myOutput.close();} catch (IOException e) {e.printStackTrace();}}
myJzvdStd.setUp(Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera/local_video.mp4", "饺子不信",Jzvd.SCREEN_WINDOW_NORMAL, );
这里很多人问为什么播不了,请认真怒url,播不了就是url没怒对
5. 播放assets目录下的视频
       复制Demo中CustomMediaPlayerAssertFolder类到你的项目下----------------------------------------------------------------------------JZDataSource jzDataSource = null;try {jzDataSource = new JZDataSource(getAssets().openFd("local_video.mp4"));jzDataSource.title = "饺子快长大";} catch (IOException e) {e.printStackTrace();}jzvdStd.setUp(jzDataSource, JzvdStd.SCREEN_WINDOW_NORMAL);Glide.with(this).load("http://jzvd-pic.nathen.cn/jzvd-pic/1bb2ebbe-140d-4e2e-abd2-9e7e564f71ac.png").into(jzvdStd.thumbImageView);Jzvd.setMediaInterface(new CustomMediaPlayerAssertFolder());//进入此页面修改MediaInterface,让此页面的jzvd正常工作
6. 直接全屏播放

JzvdStd.startFullscreen(this, JzvdStd.class, VideoConstant.videoUrlList[6], “饺子辛苦了”);

7. 开启小窗播放

mJzvdStd.startWindowTiny();

8. 列表Item划出开启小窗播放
1.ListviewlistView.setOnScrollListener(new AbsListView.OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) { }@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {Jzvd.onScrollAutoTiny(view, firstVisibleItem, visibleItemCount, totalItemCount);// Jzvd.onScrollReleaseAllVideos(view, firstVisibleItem, visibleItemCount, totalItemCount);  这是不开启列表划出小窗 同时也是画出屏幕释放JZ 划出暂停}});
2. RecyclerView  划出列表开启小窗recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {@Overridepublic void onChildViewAttachedToWindow(View view) {Jzvd.onChildViewAttachedToWindow(view, R.id.videoplayer);}@Overridepublic void onChildViewDetachedFromWindow(View view) {Jzvd.onChildViewDetachedFromWindow(view);}});
2.1 RecyclerView划出屏幕释放JZ,同时也是不开启列表划出显示小窗recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {@Overridepublic void onChildViewAttachedToWindow(View view) {}@Overridepublic void onChildViewDetachedFromWindow(View view) {Jzvd jzvd = view.findViewById(R.id.videoplayer);if (jzvd != null && jzvd.jzDataSource.containsTheUrl(JZMediaManager.getCurrentUrl())) {Jzvd currentJzvd = JzvdMgr.getCurrentJzvd();if (currentJzvd != null && currentJzvd.currentScreen != Jzvd.SCREEN_WINDOW_FULLSCREEN) {Jzvd.releaseAllVideos();}}}});
9. 小屏播放无声音,全屏有声音
创建一个类继承JzvdStd并在XML设置
public class JzvdStdVolumeAfterFullscreen extends JzvdStd {public JzvdStdVolumeAfterFullscreen(Context context) {super(context);}public JzvdStdVolumeAfterFullscreen(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void onPrepared() {super.onPrepared();if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {JZMediaManager.instance().jzMediaInterface.setVolume(1f, 1f);} else {JZMediaManager.instance().jzMediaInterface.setVolume(0f, 0f);}}/*** 进入全屏模式的时候关闭静音模式*/@Overridepublic void startWindowFullscreen() {super.startWindowFullscreen();JZMediaManager.instance().jzMediaInterface.setVolume(1f, 1f);}/*** 退出全屏模式的时候开启静音模式*/@Overridepublic void playOnThisJzvd() {super.playOnThisJzvd();JZMediaManager.instance().jzMediaInterface.setVolume(0f, 0f);}
}
10. 全屏状态播放完成,不退出全屏
创建一个类继承JzvdStd并在XML设置
public class JzvdStdAutoCompleteAfterFullscreen extends JzvdStd {public JzvdStdAutoCompleteAfterFullscreen(Context context) {super(context);}public JzvdStdAutoCompleteAfterFullscreen(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void startVideo() {if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {Log.d(TAG, "startVideo [" + this.hashCode() + "] ");initTextureView();addTextureView();AudioManager mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);mAudioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);JZUtils.scanForActivity(getContext()).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);JZMediaManager.setDataSource(jzDataSource);JZMediaManager.instance().positionInList = positionInList;onStatePreparing();} else {super.startVideo();}}@Overridepublic void onAutoCompletion() {if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {onStateAutoComplete();} else {super.onAutoCompletion();}}
}
11. 全屏模式下显示分享按钮
复制DEMO下的layout文件在 layout_top 布局下 添加你的分享按钮
public class JzvdStdShowShareButtonAfterFullscreen extends JzvdStd {public ImageView shareButton;public JzvdStdShowShareButtonAfterFullscreen(Context context) {super(context);}public JzvdStdShowShareButtonAfterFullscreen(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void init(Context context) {super.init(context);shareButton = findViewById(R.id.share);shareButton.setOnClickListener(this);}@Overridepublic int getLayoutId() {return R.layout.layout_standard_with_share_button;}@Overridepublic void onClick(View v) {super.onClick(v);if (v.getId() == R.id.share) {Toast.makeText(getContext(), "Whatever the icon means", Toast.LENGTH_SHORT).show();}}@Overridepublic void setUp(JZDataSource jzDataSource, int screen) {super.setUp(jzDataSource, screen);if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {shareButton.setVisibility(View.VISIBLE);} else {shareButton.setVisibility(View.INVISIBLE);}}
}
12. 小屏状态下不显示标题,全屏模式下显示标题
public class JzvdStdShowTitleAfterFullscreen extends JzvdStd {public JzvdStdShowTitleAfterFullscreen(Context context) {super(context);}public JzvdStdShowTitleAfterFullscreen(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void setUp(JZDataSource jzDataSource, int screen) {super.setUp(jzDataSource, screen);if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {titleTextView.setVisibility(View.VISIBLE);} else {titleTextView.setVisibility(View.INVISIBLE);}}
}
13. 播放MP3
public class JzvdStdMp3 extends JzvdStd {public JzvdStdMp3(Context context) {super(context);}public JzvdStdMp3(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic int getLayoutId() {return R.layout.jz_layout_standard_mp3;}@Overridepublic void onClick(View v) {if (v.getId() == cn.jzvd.R.id.thumb &&(currentState == CURRENT_STATE_PLAYING ||currentState == CURRENT_STATE_PAUSE)) {onClickUiToggle();} else if (v.getId() == R.id.fullscreen) {} else {super.onClick(v);}}//changeUiTo 真能能修改ui的方法@Overridepublic void changeUiToNormal() {super.changeUiToNormal();}@Overridepublic void changeUiToPreparing() {super.changeUiToPreparing();}@Overridepublic void changeUiToPlayingShow() {super.changeUiToPlayingShow();thumbImageView.setVisibility(View.VISIBLE);}@Overridepublic void changeUiToPlayingClear() {super.changeUiToPlayingClear();thumbImageView.setVisibility(View.VISIBLE);}@Overridepublic void changeUiToPauseShow() {super.changeUiToPauseShow();thumbImageView.setVisibility(View.VISIBLE);}@Overridepublic void changeUiToPauseClear() {super.changeUiToPauseClear();thumbImageView.setVisibility(View.VISIBLE);}@Overridepublic void changeUiToComplete() {super.changeUiToComplete();}@Overridepublic void changeUiToError() {super.changeUiToError();}
}jzvdStdMp3 = findViewById(R.id.jz_videoplayer_mp3);jzvdStdMp3.setUp(URL, "饺子摇摆", Jzvd.SCREEN_WINDOW_NORMAL);Glide.with(this).load(VideoConstant.videoThumbs[0][1]).into(jzvdStdMp3.thumbImageView);
14. 播放完成不显示预览图
public class JzvdStdShowTextureViewAfterAutoComplete extends JzvdStd {public JzvdStdShowTextureViewAfterAutoComplete(Context context) {super(context);}public JzvdStdShowTextureViewAfterAutoComplete(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void onAutoCompletion() {super.onAutoCompletion();thumbImageView.setVisibility(View.GONE);}}
15. Home键退出界面暂停播放,返回界面继续播放
 @Overrideprotected void onResume() {super.onResume();//home backJzvdStd.goOnPlayOnResume();}@Overrideprotected void onPause() {super.onPause();//     Jzvd.clearSavedProgress(this, null);//home backJzvdStd.goOnPlayOnPause();}
16. 边播边缓存和清晰度切换
1. 集成videocache implementation 'com.danikula:videocache:2.7.0',并初始化
public class ApplicationDemo extends Application {@Overridepublic void onCreate() {super.onCreate();
//        LeakCanary.install(this);}private HttpProxyCacheServer proxy;public static HttpProxyCacheServer getProxy(Context context) {ApplicationDemo app = (ApplicationDemo) context.getApplicationContext();return app.proxy == null ? (app.proxy = app.newProxy()) : app.proxy;}private HttpProxyCacheServer newProxy() {return new HttpProxyCacheServer(this);}
}
2.引用LinkedHashMap map = new LinkedHashMap();String proxyUrl = ApplicationDemo.getProxy(this).getProxyUrl(VideoConstant.videoUrls[0][9]);map.put("高清", proxyUrl);map.put("标清", VideoConstant.videoUrls[0][6]);map.put("普清", VideoConstant.videoUrlList[0]);JZDataSource jzDataSource = new JZDataSource(map, "饺子不信");jzDataSource.looping = true;jzDataSource.currentUrlIndex = 2;jzDataSource.headerMap.put("key", "value");//headermJzvdStd.setUp(jzDataSource, JzvdStd.SCREEN_WINDOW_NORMAL);Glide.with(this).load(VideoConstant.videoThumbList[0]).into(mJzvdStd.thumbImageView);
17. 重复播放
创建一个类集成JzvdStd并在XML设置
public class JZVideoPlayerStandardLoopVideo extends JzvdStd{public JZVideoPlayerStandardLoopVideo (Context context) {super(context);}public JZVideoPlayerStandardLoopVideo (Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void onAutoCompletion() {super.onAutoCompletion();startVideo();}
}
还有一种方法就是上面清晰度切换loop循环标志
18. 重力感应
Jzvd.FULLSCREEN_ORIENTATION=ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
Jzvd.NORMAL_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
两个变量控制全屏前后的屏幕方向
19. 不保存播放进度

Jzvd.SAVE_PROGRESS = false;

20. 取消播放时在非WIFIDialog提示

Jzvd.WIFI_TIP_DIALOG_SHOWED=true;

21. 清除某个URL进度

Jzvd.clearSavedProgress(this, “url”);

22. 切换播放内核
ijk复制Demo中JZMediaIjkplayer类到你的项目下 implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.4'implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.4'Jzvd.setMediaInterface(new JZMediaIjkplayer());  //  ijkMediaPlayer
MediaplayerJzvd.setMediaInterface(new JZMediaSystem());  // 
exo复制Demo中JZExoPlayer类到你的项目下 implementation 'com.google.android.exoplayer:exoplayer:2.7.1'Jzvd.setMediaInterface(new JZExoPlayer());  //exo
23. 用户埋点统计
    Jzvd.setJzUserAction(new MyUserActionStd());/*** 这只是给埋点统计用户数据用的,不能写和播放相关的逻辑,监听事件请参考MyJzvdStd,复写函数取得相应事件*/class MyUserActionStd implements JZUserActionStd {@Overridepublic void onEvent(int type, Object url, int screen, Object... objects) {switch (type) {case JZUserAction.ON_CLICK_START_ICON:Log.i("USER_EVENT", "ON_CLICK_START_ICON" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserAction.ON_CLICK_START_ERROR:Log.i("USER_EVENT", "ON_CLICK_START_ERROR" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserAction.ON_CLICK_START_AUTO_COMPLETE:Log.i("USER_EVENT", "ON_CLICK_START_AUTO_COMPLETE" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserAction.ON_CLICK_PAUSE:Log.i("USER_EVENT", "ON_CLICK_PAUSE" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserAction.ON_CLICK_RESUME:Log.i("USER_EVENT", "ON_CLICK_RESUME" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserAction.ON_SEEK_POSITION:Log.i("USER_EVENT", "ON_SEEK_POSITION" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserAction.ON_AUTO_COMPLETE:Log.i("USER_EVENT", "ON_AUTO_COMPLETE" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserAction.ON_ENTER_FULLSCREEN:Log.i("USER_EVENT", "ON_ENTER_FULLSCREEN" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserAction.ON_QUIT_FULLSCREEN:Log.i("USER_EVENT", "ON_QUIT_FULLSCREEN" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserAction.ON_ENTER_TINYSCREEN:Log.i("USER_EVENT", "ON_ENTER_TINYSCREEN" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserAction.ON_QUIT_TINYSCREEN:Log.i("USER_EVENT", "ON_QUIT_TINYSCREEN" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserAction.ON_TOUCH_SCREEN_SEEK_VOLUME:Log.i("USER_EVENT", "ON_TOUCH_SCREEN_SEEK_VOLUME" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserAction.ON_TOUCH_SCREEN_SEEK_POSITION:Log.i("USER_EVENT", "ON_TOUCH_SCREEN_SEEK_POSITION" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserActionStd.ON_CLICK_START_THUMB:Log.i("USER_EVENT", "ON_CLICK_START_THUMB" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;case JZUserActionStd.ON_CLICK_BLANK:Log.i("USER_EVENT", "ON_CLICK_BLANK" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);break;default:Log.i("USER_EVENT", "unknow");break;}}}

四、 相关函数回调,屏幕状态,播放器状态,事件

1. 继承JzvdStd之后,可以通过父类的mCurrentState,取得当前的播放状态。
  • CURRENT_STATE_IDLE 未知状态,指控件被new出来之后什么都没做
  • CURRENT_STATE_NORMAL 普通状态
  • CURRENT_STATE_PREPARING 视频准备状态
  • CURRENT_STATE_PREPARING_CHANGING_URL 播放中切换url的准备状态
  • CURRENT_STATE_PLAYING 播放中状态
  • CURRENT_STATE_PAUSE 暂停状态
  • CURRENT_STATE_AUTO_COMPLETE 自动播放完成状态
  • CURRENT_STATE_ERROR 错误状态

复写进入播放状态的函数,取得播放状态的回调

  • onStateNormal 进入普通状态,通常指setUp之后
  • onStatePreparing 进入准备中状态,就是loading状态
  • onStatePlaying 进入播放状态
  • onStatePause 进入暂停状态
  • onStateError 进入错误状态
  • onStateAutoComplete 进入自动播放完成状态
2. 了解当前屏幕类型

全屏、小窗、非全屏分别是不同的实例,在继承JzvdStd后,通过mCurrentScreen变量,取得当前屏幕类型

  • SCREEN_WINDOW_NORMAL 普通窗口(进入全屏之前的)
  • SCREEN_WINDOW_LIST 列表窗口(进入全屏之前)
  • SCREEN_WINDOW_FULLSCREEN 全屏
  • SCREEN_WINDOW_TINY 小窗
3. 事件
  • 复写onProgress函数,取得每次播放器设置底部seekBar的进度回调
  • 调用changeUrl函数,切换url
  • 复写onClick函数,取得各种按钮的点击事件
  • 复写onTouch函数,取得全屏之后的手势操作

五、一些需求实现(以下实现都是本人的思考逻辑和借鉴一些网络,不完善,要根据你自己项目实际情况来)

1.会员试看功能 首先从思路上理清步骤
  • 首先拿到当前播放时间和是试看时间做判断(考虑拖动进度条超过试看时间)
  • 从后台拿到特定字段判断是否付费
  • 弹出试看结束功能Dialog
  • 付费完回来继续播放(省去付费步骤)———取消
根据自己项目的实际情况来 ,这里只是简单的测试
public class TryToSeeVideo extends JzvdStd {RelativeLayout mRelativeLayout;TextView mExit, mPay, mTryToSee;boolean isNoPay;public TryToSeeVideo(Context context) {super(context);}public TryToSeeVideo(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void init(Context context) {super.init(context);mRelativeLayout = findViewById(R.id.try_to_see_tip);mExit = findViewById(R.id.exit);   //退出按钮mPay = findViewById(R.id.pay);     //支付按钮mTryToSee = findViewById(R.id.try_see);    //重新试看按钮mExit.setOnClickListener(this);mPay.setOnClickListener(this);mTryToSee.setOnClickListener(this);}@Overridepublic int getLayoutId() {return R.layout.try_to_see_video_layout;}@Overridepublic void onClick(View v) {super.onClick(v);switch (v.getId()) {case R.id.exit://finish界面  建议回调出当前事件break;case R.id.pay://进入支付界面  支付成功后 Jzvd.goOnPlayOnResume();getContext().startActivity(new Intent(getContext(), ActivityMain.class));  //模拟跳转支付界面break;case R.id.try_see:JZMediaManager.seekTo(0);//回到0秒Jzvd.goOnPlayOnResume();progressBar.setProgress(0);mRelativeLayout.setVisibility(GONE);break;}}/*** @param progress 百分比* @param position 当前时间* @param duration 总时长*/@Overridepublic void onProgress(int progress, long position, long duration) {super.onProgress(progress, position, duration);long totalSeconds = position / 1000;if (true) {   //这里是从服务器拿到特定字段判断是否付费  这里模拟未付费if (totalSeconds >= 30) {   //考虑未付费情况下拖动进度条超过试看时间if (!isNoPay) {   //加个标记 因为此函数一直在回调JZMediaManager.seekTo(30000);   //回到30秒progressBar.setProgress(30);mRelativeLayout.setVisibility(VISIBLE);   //在适当的时候隐藏Jzvd.goOnPlayOnPause();           //在适当的时候(支付成功后)播放startButton.setEnabled(false);    //在适当的时候取消isNoPay = true;}}}}@Overridepublic void onStatePlaying() {super.onStatePlaying();isNoPay = false;startButton.setEnabled(true);}
}

2.列表页到详情页无时差播放
3.列表自动播放
4.自动播放下一集剧集

2. 函数 changeUrl 三个重载 根据自己的实际情况来
  • changeUrl(String url, String title, long seekToInAdvance) //seekToInAdvance 时间
  • changeUrl(JZDataSource jzDataSource, long seekToInAdvance)
  • changeUrl(int urlMapIndex, long seekToInAdvance)
    这里是演示自动播放下一集(如果要自己选集,自定义UI定义出剧集列表,调用上面三个任意一个)
public class AutoPlayNextVideo extends JzvdStd {int UrlIndex = 0;  //剧集标识public AutoPlayNextVideo(Context context) {super(context);}public AutoPlayNextVideo(Context context, AttributeSet attrs) {super(context, attrs);}/***    自动播放下一集*    若果需要手动选集 自定义Ui定义出剧集列表 调用相关changeUrl*/@Overridepublic void onAutoCompletion() {super.onAutoCompletion();if (UrlIndex
3. 倍速播放
public class JzvdStdSpeed extends JzvdStd {TextView tvSpeed;int currentSpeedIndex = 2;public JzvdStdSpeed(Context context) {super(context);}public JzvdStdSpeed(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void init(Context context) {super.init(context);tvSpeed = findViewById(R.id.tv_speed);tvSpeed.setOnClickListener(this);}@Overridepublic void setUp(JZDataSource jzDataSource, int screen) {super.setUp(jzDataSource, screen);if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {tvSpeed.setVisibility(View.VISIBLE);} else {tvSpeed.setVisibility(View.GONE);}if (jzDataSource.objects == null) {Object[] object = {2};jzDataSource.objects = object;currentSpeedIndex = 2;} else {currentSpeedIndex = (int) jzDataSource.objects[0];}if (currentSpeedIndex == 2) {tvSpeed.setText("倍速");} else {tvSpeed.setText(getSpeedFromIndex(currentSpeedIndex) + "X");}}@Overridepublic void onClick(View v) {super.onClick(v);if (v.getId() == R.id.tv_speed) {//0.5 0.75 1.0 1.25 1.5 1.75 2.0if (currentSpeedIndex == 6) {currentSpeedIndex = 0;} else {currentSpeedIndex += 1;}JZMediaManager.setSpeed(getSpeedFromIndex(currentSpeedIndex));tvSpeed.setText(getSpeedFromIndex(currentSpeedIndex) + "X");jzDataSource.objects[0] = currentSpeedIndex;}}@Overridepublic int getLayoutId() {return R.layout.layout_std_speed;}private float getSpeedFromIndex(int index) {float ret = 0f;if (index == 0) {ret = 0.5f;} else if (index == 1) {ret = 0.75f;} else if (index == 2) {ret = 1.0f;} else if (index == 3) {ret = 1.25f;} else if (index == 4) {ret = 1.5f;} else if (index == 5) {ret = 1.75f;} else if (index == 6) {ret = 2.0f;}return ret;}

六、常见问题 视频有声音,无画面

  • 开启硬件加速
1. 获取视频某一帧
/**
*  context 上下文
*  uri 视频地址
*  imageView 设置image
*  frameTimeMicros 获取某一时间帧
*/
public static void loadVideoScreenshot(final Context context, String uri, ImageView imageView, long frameTimeMicros) {RequestOptions requestOptions = RequestOptions.frameOf(frameTimeMicros);requestOptions.set(FRAME_OPTION, MediaMetadataRetriever.OPTION_CLOSEST);requestOptions.transform(new BitmapTransformation() {@Overrideprotected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {return toTransform;}@Overridepublic void updateDiskCacheKey(MessageDigest messageDigest) {try {messageDigest.update((context.getPackageName() + "RotateTransform").getBytes("utf-8"));} catch (Exception e) {e.printStackTrace();}}});Glide.with(context).load(uri).apply(requestOptions).into(imageView);
}
2. 视频圆角

ViewOutlineProvider (注:该类是5.0的特性,低于5.0没效果,如果要适配5.0以下这里给出一个思路.9图遮罩)
创建一个类继承ViewOutLineProvider 实现改该方法

public class JzViewOutlineProvider extends ViewOutlineProvider {private float mRadius;public JzViewOutlineProvider(float radius) {this.mRadius = radius;}@Overridepublic void getOutline(View view, Outline outline) {Rect rect = new Rect();view.getGlobalVisibleRect(rect);Rect selfRect = new Rect(0, 0,view.getWeight(),view.getHeight());outline.setRoundRect(selfRect,mRadius);}
}mJzvdStd.setOutlineProvider(new JzViewOutlineProvider(radius));mJzvdStd.setClipToOutline(true);

点击全屏按钮闪退,报错空指针

  • 检查是否正确按照基本使用在manifest中配置。Activity是否继承自AppCompatActivity
 

视屏宽高不填充屏幕,有黑边

  Jzvd.setTextureViewRotation(90);   //视频旋转90度根据自己情况选择一个 填充Jzvd.setVideoImageDisplayType(Jzvd.VIDEO_IMAGE_DISPLAY_TYPE_FILL_PARENT); Jzvd.setVideoImageDisplayType(Jzvd.VIDEO_IMAGE_DISPLAY_TYPE_FILL_SCROP);   Jzvd.setVideoImageDisplayType(Jzvd.VIDEO_IMAGE_DISPLAY_TYPE_ORIGINAL);  //原始大小

背景高斯模糊

  • 以module的形式导入项目,在jz_layout_standard.xml 的布局下添加一个Imageview
 

这里我为了方便重写了Imageview 使他宽填充屏幕宽,这里怎么简单怎么来想进一切办法让他填充满宽度

在JzvdStd类下的init 方法里面获取上面的Imageview控件,好了到此就完了,然后在设置缩略图的下面用能高斯模糊的方法设置就OK,下面是我的做法你可以根据你自己来。

myJzvdStd.setUp("http://jzvd.nathen.cn/342a5f7ef6124a4a8faf00e738b8bee4/cf6d9db0bd4d41f59d09ea0a81e918fd-5287d2089db37e62345123a1be272f8b.mp4", "饺子快长大", JzvdStd.SCREEN_WINDOW_NORMAL);Glide.with(this).load("http://jzvd-pic.nathen.cn/jzvd-pic/1bb2ebbe-140d-4e2e-abd2-9e7e564f71ac.png").into(myJzvdStd.thumbImageView);Jzvd.setJzUserAction(new MyUserActionStd());Glide.with(ActivityMain.this).load("http://t2.hddhhn.com/uploads/tu/201810/9999/9139710e12.jpg").dontAnimate()// 设置高斯模糊.bitmapTransform(new BlurTransformation(this, 14, 5)).into(myJzvdStd.mFrameLayout);

参考作者:TrueloveSomeGIRl
链接:https://juejin.im/post/5cb837f1518825329f6cf300


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部