考虑到开发直播和视频播放的必要性,网上了解到b站开源播放器(https://github.com/bilibili/ijkplayer)好用,集成下试试。
运行后发现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模式的soimplementation '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;//弹幕viewprivate 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);}@Overridepublic int getLayoutId() {return R.layout.danmaku_layout;}@Overrideprotected 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);}@Overridepublic void onPrepared() {super.onPrepared();onPrepareDanmaku(this);}@Overridepublic void onVideoPause() {super.onVideoPause();danmakuOnPause();}@Overridepublic void onVideoResume(boolean isResume) {super.onVideoResume(isResume);danmakuOnResume();}@Overrideprotected void clickStartIcon() {super.clickStartIcon();if (mCurrentState == CURRENT_STATE_PLAYING) {danmakuOnResume();} else if (mCurrentState == CURRENT_STATE_PAUSE) {danmakuOnPause();}}@Overridepublic void onCompletion() {releaseDanmaku(this);}@Overridepublic 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);}}@Overridepublic 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;}}@Overrideprotected void cloneParams(GSYBaseVideoPlayer from, GSYBaseVideoPlayer to) {((DanmakuVideoPlayer) to).mDumakuFile = ((DanmakuVideoPlayer) from).mDumakuFile;super.cloneParams(from, to);}/**处理播放器在全屏切换时,弹幕显示的逻辑需要格外注意的是,因为全屏和小屏,是切换了播放器,所以需要同步之间的弹幕状态*/@Overridepublic 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;}/**处理播放器在退出全屏时,弹幕显示的逻辑需要格外注意的是,因为全屏和小屏,是切换了播放器,所以需要同步之间的弹幕状态*/@Overrideprotected 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() {// 设置最大显示行数HashMap<Integer, Integer> maxLinesPair = new HashMap<Integer, Integer>();maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, 5); // 滚动弹幕最大显示5行// 设置是否禁止重叠HashMap<Integer, Boolean> overlappingEnablePair = new HashMap<Integer, Boolean>();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() {@Overridepublic void updateTimer(DanmakuTimer timer) {}@Overridepublic void drawingFinished() {}@Overridepublic void danmakuShown(BaseDanmaku danmaku) {}@Overridepublic 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("<i>");//分行读取while ((line = buffreader.readLine()) != null) {sb1.append(line);}sb1.append("</i>");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() {@Overridepublic 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() {@Overrideprotected 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;@Overrideprotected void initData() {}@Overrideprotected 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() {@Overridepublic void onClick(View v) {//直接横屏orientationUtils.resolveByClick();//第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbardanmakuVideoPlayer.startWindowFullscreen(VideoPlayActivity.this, true, true);}});danmakuVideoPlayer.setVideoAllCallBack(new GSYSampleCallBack() {@Overridepublic void onPrepared(String url, Object... objects) {super.onPrepared(url, objects);//开始播放了才能旋转和全屏orientationUtils.setEnable(true);isPlay = true;getDanmu();}@Overridepublic void onAutoComplete(String url, Object... objects) {super.onAutoComplete(url, objects);}@Overridepublic void onClickStartError(String url, Object... objects) {super.onClickStartError(url, objects);}@Overridepublic void onQuitFullscreen(String url, Object... objects) {super.onQuitFullscreen(url, objects);if (orientationUtils != null) {orientationUtils.backToProtVideo();}}});danmakuVideoPlayer.setLockClickListener(new LockClickListener() {@Overridepublic void onClick(View view, boolean lock) {if (orientationUtils != null) {//配合下方的onConfigurationChangedorientationUtils.setEnable(!lock);}}});}//获取弹幕private void getDanmu() {DanmakuVideoPlayer currentPlayer = (DanmakuVideoPlayer)danmakuVideoPlayer.getCurrentPlayer();File file = new File("text");currentPlayer.setDanmaKuStream(file);}private void resolveNormalVideoUI() {//增加titledanmakuVideoPlayer.getTitleTextView().setVisibility(View.GONE);danmakuVideoPlayer.getBackButton().setVisibility(View.GONE);}@Overrideprotected void bindPresenter() {}@Overridepublic int getLayoutId() {return R.layout.activity_video_player;}@Overridepublic void onBackPressed() {if (orientationUtils != null) {orientationUtils.backToProtVideo();}if (GSYVideoManager.backFromWindowFull(this)) {return;}super.onBackPressed();}@Overrideprotected void onPause() {getCurPlay().onVideoPause();super.onPause();isPause = true;}@Overrideprotected void onResume() {getCurPlay().onVideoResume();super.onResume();isPause = false;}@Overrideprotected void onDestroy() {super.onDestroy();if (isPlay) {getCurPlay().release();}//GSYPreViewManager.instance().releaseMediaPlayer();if (orientationUtils != null)orientationUtils.releaseListener();isDestory = true;}@Overridepublic 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的布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><lwp.com.afun.widget.DanmakuVideoPlayerandroid:id="@+id/danmaku_player"android:layout_width="match_parent"android:layout_height="200dp" /></LinearLayout>

Android集成bilibili播放器以及弹幕相关推荐

  1. android集成EasyPlayer播放器播放实时流媒体视屏

    android集成EasyPlayer播放器播放实时流媒体视屏 最近公司项目需要实现一个rtsp实时流媒体视频的播放,在移动端尝试了多个第三方能播放rtsp流实时视频的软件后发现EasyPlayer的 ...

  2. android 自定义MP4播放器

    昨天,在网上找了好多资料,终于做了一个自定义的播发器. 视频播放方式 在Android中播放视频的方式有两种: 1.使用MediaPlayer结合SurfaceView进行播放.其中通过Surface ...

  3. 哔哩哔哩html5播放器,哔哩哔哩(Bilibili)播放器扩展_v2.0.4

    介绍: 这是一个功能精简.轻量型的哔哩哔哩(HTML5)播放器扩展. 1. 播放器设置 此扩展会默认关闭哔哩哔哩(Bilibili)弹幕,但可以在扩展选项中关闭这个行为. 点击此扩展的菜单->选 ...

  4. 网页哔哩哔哩没有html5,哔哩哔哩(Bilibili)播放器扩展_v1.0.16

    介绍: 这是一个功能精简.轻量型的哔哩哔哩(HTML5)播放器扩展. 1. 播放器设置 此扩展会默认关闭哔哩哔哩(Bilibili)弹幕,但可以在扩展选项中关闭这个行为. 点击此扩展的菜单->选 ...

  5. 集成VR播放器--虚拟现实浪潮的一舟: UtoVR

    最近圈内刮起一股VR与AR狂潮,虚拟现实与直播推流技术方兴未艾,正好在项目中也涉及到集成第三方播放器的支持,给大家介绍一款国内的,免费的,集成很方便的产品平台--UtoVR. 在实际使用过程中难免有些 ...

  6. android 音乐播放器 获取sd卡所有音乐文件,Android Studio音乐播放器无法读取SD卡,只有内部存储器...

    我很抱歉,如果这原来是一个愚蠢的问题,它可能会成为一个快速修复,但我只是无法弄清楚.我在android studio中创建了音乐播放器,并且没有任何sdcard上的歌曲不会显示在列表视图中,只有内部内 ...

  7. android 调用系统播放器

    今天,简单讲讲android如何调用手机自带的播放器. 昨天,从服务器下载一个AVI的视频,下载后需要进行播放,所以想调用系统自带的播放器.但是由于很少用到,所以自己当时不知道怎么写,于是在网上查找资 ...

  8. 智能android电视更换播放器,GitHub - qfwc258/TvPlayer: android智能电视播放器,可以播放各电视台节目,播放基于ijkplayer的实现。...

    TvPlayer 1,android智能电视播放器,可以播放各电视台节目,播放基于ijkplayer的实现 2,测试的源地址可能失效,如需测试可以自己更换播放源.HttpUtils.getOfflin ...

  9. php仿bilibili视频类模板,高仿bilibili播放器

    [实例简介]调用方法:http://你的域名/play.php?url= [实例截图] [核心代码] Bilibili播放器 ├── css │   ├── css │   │   ├── bt_js ...

最新文章

  1. 031_MessageBox弹框
  2. mysql hypot_mysqli_stmt_prepare
  3. final const java_Java中的final关键字 与 C#中的const, readonly关键字
  4. Python并发编程理论篇
  5. Ansible-playbook 拾遗
  6. 洛谷 [P1387] 最大正方形
  7. 彻底搞懂Scrapy的中间件(三)
  8. apache+nginx 实现动静分离
  9. mysql的dql_Mysql:dql(基本数据查询)
  10. 软件工程系啦啦队报名表(全部213人)
  11. QWidget->isVisible属性的探究
  12. python 简单操作dbpedia
  13. 舆情监测技术TOOM,web技术实现
  14. linux机械硬盘提速,这方法让机械硬盘秒变固态硬盘:速度爆表
  15. git push时rejected,解决non-fast-forward errors的办法
  16. stata学习笔记|异方差问题
  17. 《奇点临近》奇点和纪元
  18. Definition of Spline Basis Function
  19. 软件体系结构设计模式
  20. 顶级技术公司的CEO是如何看待AI的?

热门文章

  1. 不写一行代码,让Excel轻松制作动态图表​
  2. 生物信息学计算机等级,生物信息学考试题.doc
  3. 2021-11-12 (2018 CCPC 吉林站)
  4. Property ‘validationMessageSource‘ threw exception
  5. Quoted-printable
  6. PDF如何导出成图片,操作教程
  7. AUTOSAR I-PDU的理解以及I-PDU的Callout
  8. Python实现股票查询
  9. 「罗马人的故事 II 汉尼拔战记」读后感
  10. Getx - 如何使用路由管理页面