Android集成bilibili播放器以及弹幕
发布日期:2021-05-27 02:49:01
浏览次数:18
分类:技术文章
本文共 18059 字,大约阅读时间需要 60 分钟。
考虑到开发直播和视频播放的必要性,网上了解到b站开源播放器()好用,集成下试试。
运行后发现b站原生的只能播放没有其他选项,考虑到方便性,采用这个方案:https://github.com/CarGuo/GSYVideoPlayer
步骤
1.gradle集成(JCenter什么的别忘了)//ijkplayer的封装 implementation 'com.shuyu:gsyVideoPlayer-java:6.0.3' //是否需要ExoPlayer模式 implementation 'com.shuyu:GSYVideoPlayer-exo2:6.0.3' //根据你的需求ijk模式的so implementation 'com.shuyu:gsyVideoPlayer-armv5:6.0.3' implementation 'com.shuyu:gsyVideoPlayer-armv7a:6.0.3' implementation 'com.shuyu:gsyVideoPlayer-arm64:6.0.3' implementation 'com.shuyu:gsyVideoPlayer-x64:6.0.3' implementation 'com.shuyu:gsyVideoPlayer-x86:6.0.3' //弹幕 implementation 'com.github.ctiao:DanmakuFlameMaster:0.9.16' compile 'com.github.ctiao:ndkbitmap-armv7a:0.9.21' compile 'com.github.ctiao:ndkbitmap-armv5:0.9.21' compile 'com.github.ctiao:ndkbitmap-x86:0.9.21'
2.播放器的封装
public class DanmakuVideoPlayer extends StandardGSYVideoPlayer { private BaseDanmakuParser mParser;//解析器对象 private IDanmakuView mDanmakuView;//弹幕view private DanmakuContext mDanmakuContext; private TextView mSendDanmaku, mToogleDanmaku; private long mDanmakuStartSeekPosition = -1; private boolean mDanmaKuShow = true; private File mDumakuFile; public DanmakuVideoPlayer(Context context, Boolean fullFlag) { super(context, fullFlag); } public DanmakuVideoPlayer(Context context) { super(context); } public DanmakuVideoPlayer(Context context, AttributeSet attrs) { super(context, attrs); } @Override public int getLayoutId() { return R.layout.danmaku_layout; } @Override protected void init(Context context) { super.init(context); mDanmakuView = (DanmakuView) findViewById(R.id.danmaku_view); mSendDanmaku = (TextView) findViewById(R.id.send_danmaku); mToogleDanmaku = (TextView) findViewById(R.id.toogle_danmaku); //初始化弹幕显示 initDanmaku(); mSendDanmaku.setOnClickListener(this); mToogleDanmaku.setOnClickListener(this); } @Override public void onPrepared() { super.onPrepared(); onPrepareDanmaku(this); } @Override public void onVideoPause() { super.onVideoPause(); danmakuOnPause(); } @Override public void onVideoResume(boolean isResume) { super.onVideoResume(isResume); danmakuOnResume(); } @Override protected void clickStartIcon() { super.clickStartIcon(); if (mCurrentState == CURRENT_STATE_PLAYING) { danmakuOnResume(); } else if (mCurrentState == CURRENT_STATE_PAUSE) { danmakuOnPause(); } } @Override public void onCompletion() { releaseDanmaku(this); } @Override public void onSeekComplete() { super.onSeekComplete(); int time = mProgressBar.getProgress() * getDuration() / 100; //如果已经初始化过的,直接seek到对于位置 if (mHadPlay && getDanmakuView() != null && getDanmakuView().isPrepared()) { resolveDanmakuSeek(this, time); } else if (mHadPlay && getDanmakuView() != null && !getDanmakuView().isPrepared()) { //如果没有初始化过的,记录位置等待 setDanmakuStartSeekPosition(time); } } @Override public void onClick(View v) { super.onClick(v); switch (v.getId()) { case R.id.send_danmaku: addDanmaku(true); break; case R.id.toogle_danmaku: mDanmaKuShow = !mDanmaKuShow; resolveDanmakuShow(); break; } } @Override protected void cloneParams(GSYBaseVideoPlayer from, GSYBaseVideoPlayer to) { ((DanmakuVideoPlayer) to).mDumakuFile = ((DanmakuVideoPlayer) from).mDumakuFile; super.cloneParams(from, to); } /** 处理播放器在全屏切换时,弹幕显示的逻辑 需要格外注意的是,因为全屏和小屏,是切换了播放器,所以需要同步之间的弹幕状态 */ @Override public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) { GSYBaseVideoPlayer gsyBaseVideoPlayer = super.startWindowFullscreen(context, actionBar, statusBar); if (gsyBaseVideoPlayer != null) { DanmakuVideoPlayer gsyVideoPlayer = (DanmakuVideoPlayer) gsyBaseVideoPlayer; //对弹幕设置偏移记录 gsyVideoPlayer.setDanmakuStartSeekPosition(getCurrentPositionWhenPlaying()); gsyVideoPlayer.setDanmaKuShow(getDanmaKuShow()); onPrepareDanmaku(gsyVideoPlayer); } return gsyBaseVideoPlayer; } /** 处理播放器在退出全屏时,弹幕显示的逻辑 需要格外注意的是,因为全屏和小屏,是切换了播放器,所以需要同步之间的弹幕状态 */ @Override protected void resolveNormalVideoShow(View oldF, ViewGroup vp, GSYVideoPlayer gsyVideoPlayer) { super.resolveNormalVideoShow(oldF, vp, gsyVideoPlayer); if (gsyVideoPlayer != null) { DanmakuVideoPlayer gsyDanmaVideoPlayer = (DanmakuVideoPlayer) gsyVideoPlayer; setDanmaKuShow(gsyDanmaVideoPlayer.getDanmaKuShow()); if (gsyDanmaVideoPlayer.getDanmakuView() != null && gsyDanmaVideoPlayer.getDanmakuView().isPrepared()) { resolveDanmakuSeek(this, gsyDanmaVideoPlayer.getCurrentPositionWhenPlaying()); resolveDanmakuShow(); releaseDanmaku(gsyDanmaVideoPlayer); } } } protected void danmakuOnPause() { if (mDanmakuView != null && mDanmakuView.isPrepared()) { mDanmakuView.pause(); } } protected void danmakuOnResume() { if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) { mDanmakuView.resume(); } } public void setDanmaKuStream(File is) { mDumakuFile = is; if (!getDanmakuView().isPrepared()) { onPrepareDanmaku((DanmakuVideoPlayer) getCurrentPlayer()); } } private void initDanmaku() { // 设置最大显示行数 HashMapmaxLinesPair = new HashMap (); maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, 5); // 滚动弹幕最大显示5行 // 设置是否禁止重叠 HashMap overlappingEnablePair = new HashMap (); overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_RL, true); overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_TOP, true); DanamakuAdapter danamakuAdapter = new DanamakuAdapter(mDanmakuView); mDanmakuContext = DanmakuContext.create(); mDanmakuContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 3).setDuplicateMergingEnabled(false).setScrollSpeedFactor(1.2f).setScaleTextSize(1.2f) .setCacheStuffer(new SpannedCacheStuffer(), danamakuAdapter) // 图文混排使用SpannedCacheStuffer .setMaximumLines(maxLinesPair) .preventOverlapping(overlappingEnablePair); if (mDanmakuView != null) { if (mDumakuFile != null) { mParser = createParser(getIsStream(mDumakuFile)); } //todo 这是为了demo效果,实际上需要去掉这个,外部传输文件进来 mParser = createParser(this.getResources().openRawResource(R.raw.comments)); mDanmakuView.setCallback(new master.flame.danmaku.controller.DrawHandler.Callback() { @Override public void updateTimer(DanmakuTimer timer) { } @Override public void drawingFinished() { } @Override public void danmakuShown(BaseDanmaku danmaku) { } @Override public void prepared() { if (getDanmakuView() != null) { getDanmakuView().start(); if (getDanmakuStartSeekPosition() != -1) { resolveDanmakuSeek(DanmakuVideoPlayer.this, getDanmakuStartSeekPosition()); setDanmakuStartSeekPosition(-1); } resolveDanmakuShow(); } } }); mDanmakuView.enableDanmakuDrawingCache(true); } } private InputStream getIsStream(File file) { try { InputStream instream = new FileInputStream(file); InputStreamReader inputreader = new InputStreamReader(instream); BufferedReader buffreader = new BufferedReader(inputreader); String line; StringBuilder sb1 = new StringBuilder(); sb1.append(" "); //分行读取 while ((line = buffreader.readLine()) != null) { sb1.append(line); } sb1.append(""); Log.e("3333333", sb1.toString()); instream.close(); return new ByteArrayInputStream(sb1.toString().getBytes()); } catch (java.io.FileNotFoundException e) { Log.d("TestFile", "The File doesn't not exist."); } catch (IOException e) { Log.d("TestFile", e.getMessage()); } return null; } /** 弹幕的显示与关闭 */ private void resolveDanmakuShow() { post(new Runnable() { @Override public void run() { if (mDanmaKuShow) { if (!getDanmakuView().isShown()) getDanmakuView().show(); mToogleDanmaku.setText("弹幕关"); } else { if (getDanmakuView().isShown()) { getDanmakuView().hide(); } mToogleDanmaku.setText("弹幕开"); } } }); } /** 开始播放弹幕 */ private void onPrepareDanmaku(DanmakuVideoPlayer gsyVideoPlayer) { if (gsyVideoPlayer.getDanmakuView() != null && !gsyVideoPlayer.getDanmakuView().isPrepared() && gsyVideoPlayer.getParser() != null) { gsyVideoPlayer.getDanmakuView().prepare(gsyVideoPlayer.getParser(), gsyVideoPlayer.getDanmakuContext()); } } /** 弹幕偏移 */ private void resolveDanmakuSeek(DanmakuVideoPlayer gsyVideoPlayer, long time) { if (mHadPlay && gsyVideoPlayer.getDanmakuView() != null && gsyVideoPlayer.getDanmakuView().isPrepared()) { gsyVideoPlayer.getDanmakuView().seekTo(time); } } /** 创建解析器对象,解析输入流 @param stream @return */ private BaseDanmakuParser createParser(InputStream stream) { if (stream == null) { return new BaseDanmakuParser() { @Override protected Danmakus parse() { return new Danmakus(); } }; } ILoader loader = DanmakuLoaderFactory.create(DanmakuLoaderFactory.TAG_BILI); try { loader.load(stream); } catch (IllegalDataException e) { e.printStackTrace(); } BaseDanmakuParser parser = new BiliDanmukuParser(); IDataSource dataSource = loader.getDataSource(); parser.load(dataSource); return parser; } /** 释放弹幕控件 */ private void releaseDanmaku(DanmakuVideoPlayer danmakuVideoPlayer) { if (danmakuVideoPlayer != null && danmakuVideoPlayer.getDanmakuView() != null) { Debuger.printfError("release Danmaku!"); danmakuVideoPlayer.getDanmakuView().release(); } } public BaseDanmakuParser getParser() { if (mParser == null) { if (mDumakuFile != null) { mParser = createParser(getIsStream(mDumakuFile)); } } return mParser; } public DanmakuContext getDanmakuContext() { return mDanmakuContext; } public IDanmakuView getDanmakuView() { return mDanmakuView; } public long getDanmakuStartSeekPosition() { return mDanmakuStartSeekPosition; } public void setDanmakuStartSeekPosition(long danmakuStartSeekPosition) { this.mDanmakuStartSeekPosition = danmakuStartSeekPosition; } public void setDanmaKuShow(boolean danmaKuShow) { mDanmaKuShow = danmaKuShow; } public boolean getDanmaKuShow() { return mDanmaKuShow; } /** 模拟添加弹幕数据 */ private void addDanmaku(boolean islive) { BaseDanmaku danmaku = mDanmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL); if (danmaku == null || mDanmakuView == null) { return; } danmaku.text = "这是一条弹幕 " + getCurrentPositionWhenPlaying(); danmaku.padding = 5; danmaku.priority = 8; // 可能会被各种过滤器过滤并隐藏显示,所以提高等级 danmaku.isLive = islive; danmaku.setTime(mDanmakuView.getCurrentTime() + 500); danmaku.textSize = 25f * (mParser.getDisplayer().getDensity() - 0.6f); danmaku.textColor = Color.RED; danmaku.textShadowColor = Color.WHITE; danmaku.borderColor = Color.GREEN; mDanmakuView.addDanmaku(danmaku); }}
3.Activity中使用该播放器播放
public class VideoPlayActivity extends BaseCommonActivity { //假地址 String path = "http://vjs.zencdn.net/v/oceans.mp4"; @BindView(R.id.danmaku_player) DanmakuVideoPlayer danmakuVideoPlayer; OrientationUtils orientationUtils; boolean isPlay; boolean isPause; boolean isDestory; @Override protected void initData() { } @Override protected void initView() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); } //使用自定义的全屏切换图片,!!!注意xml布局中也需要设置为一样的 //必须在setUp之前设置 danmakuVideoPlayer.setShrinkImageRes(R.drawable.custom_shrink); danmakuVideoPlayer.setEnlargeImageRes(R.drawable.custom_enlarge); //String url = "https://res.exexm.com/cw_145225549855002"; String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; //String url = "https://res.exexm.com/cw_145225549855002"; danmakuVideoPlayer.setUp(url, true, null, "测试视频"); resolveNormalVideoUI(); //外部辅助的旋转,帮助全屏 orientationUtils = new OrientationUtils(this, danmakuVideoPlayer); //初始化不打开外部的旋转 orientationUtils.setEnable(false); danmakuVideoPlayer.setIsTouchWiget(true); //关闭自动旋转 danmakuVideoPlayer.setRotateViewAuto(false); danmakuVideoPlayer.setLockLand(false); danmakuVideoPlayer.setShowFullAnimation(false); danmakuVideoPlayer.setNeedLockFull(true); //detailPlayer.setOpenPreView(true); danmakuVideoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //直接横屏 orientationUtils.resolveByClick(); //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbar danmakuVideoPlayer.startWindowFullscreen(VideoPlayActivity.this, true, true); } }); danmakuVideoPlayer.setVideoAllCallBack(new GSYSampleCallBack() { @Override public void onPrepared(String url, Object... objects) { super.onPrepared(url, objects); //开始播放了才能旋转和全屏 orientationUtils.setEnable(true); isPlay = true; getDanmu(); } @Override public void onAutoComplete(String url, Object... objects) { super.onAutoComplete(url, objects); } @Override public void onClickStartError(String url, Object... objects) { super.onClickStartError(url, objects); } @Override public void onQuitFullscreen(String url, Object... objects) { super.onQuitFullscreen(url, objects); if (orientationUtils != null) { orientationUtils.backToProtVideo(); } } }); danmakuVideoPlayer.setLockClickListener(new LockClickListener() { @Override public void onClick(View view, boolean lock) { if (orientationUtils != null) { //配合下方的onConfigurationChanged orientationUtils.setEnable(!lock); } } }); } //获取弹幕 private void getDanmu() { DanmakuVideoPlayer currentPlayer = (DanmakuVideoPlayer)danmakuVideoPlayer.getCurrentPlayer(); File file = new File("text"); currentPlayer.setDanmaKuStream(file); } private void resolveNormalVideoUI() { //增加title danmakuVideoPlayer.getTitleTextView().setVisibility(View.GONE); danmakuVideoPlayer.getBackButton().setVisibility(View.GONE); } @Override protected void bindPresenter() { } @Override public int getLayoutId() { return R.layout.activity_video_player; } @Override public void onBackPressed() { if (orientationUtils != null) { orientationUtils.backToProtVideo(); } if (GSYVideoManager.backFromWindowFull(this)) { return; } super.onBackPressed(); } @Override protected void onPause() { getCurPlay().onVideoPause(); super.onPause(); isPause = true; } @Override protected void onResume() { getCurPlay().onVideoResume(); super.onResume(); isPause = false; } @Override protected void onDestroy() { super.onDestroy(); if (isPlay) { getCurPlay().release(); } //GSYPreViewManager.instance().releaseMediaPlayer(); if (orientationUtils != null) orientationUtils.releaseListener(); isDestory = true; } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); //如果旋转了就全屏 if (isPlay && !isPause) { danmakuVideoPlayer.onConfigurationChanged(this, newConfig, orientationUtils, true, true); } } private GSYVideoPlayer getCurPlay() { if (danmakuVideoPlayer.getFullWindowPlayer() != null) { return danmakuVideoPlayer.getFullWindowPlayer(); } return danmakuVideoPlayer; }}
4.该activity的布局
转载地址:https://blog.csdn.net/kururunga/article/details/87366084 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
逛到本站,mark一下
[***.202.152.39]2024年08月23日 04时22分50秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Linux之tcpdump用法汇总整理
2019-05-24
Linux之tcp的结构分析
2019-05-24
WebKit之WebSocket模块的代码层初步分析
2019-05-24
WIFI之Agent调度关系
2019-05-24
WIFI之升级协议列表
2019-05-24
MCU之STM32可用硬件(外部接口)一览表
2019-05-24
MySql之设备管理的数据表设计列表
2019-05-24
WIFI之系统启动的脚本配置
2019-05-24
Python之服务器模块设计学习
2019-05-24
WIFI之3GControl模块调度草图
2019-05-24
WIFI之系统部署环境
2019-05-24
C++之UML关系说明图
2019-05-24
网络之Snmp的学习总结
2019-05-24
WIFI之服务器系统设计组成
2019-05-24
Linux之Arinc驱动设计草图
2019-05-24
Linux之grub.conf的内容介绍
2019-05-24
网址之Curl API整理说明
2019-05-24
Python之ftp的用法整理
2019-05-24
MStar之公司简介
2019-05-24
STB之业务架构图
2019-05-24