Android集成bilibili播放器以及弹幕
·
考虑到开发直播和视频播放的必要性,网上了解到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模式的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() {
// 设置最大显示行数
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() {
@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("<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() {
@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的布局
<?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.DanmakuVideoPlayer
android:id="@+id/danmaku_player"
android:layout_width="match_parent"
android:layout_height="200dp" />
</LinearLayout>
更多推荐
已为社区贡献2条内容
所有评论(0)