package com.example.gsyvideoplayer.video; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.style.BackgroundColorSpan; import android.text.style.ImageSpan; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.SeekBar; import android.widget.TextView; import com.example.gsyvideoplayer.R; import com.example.gsyvideoplayer.adapter.DanamakuAdapter; import com.example.gsyvideoplayer.utils.BiliDanmukuParser; import com.shuyu.gsyvideoplayer.GSYVideoManager; import com.shuyu.gsyvideoplayer.GSYVideoPlayer; import com.shuyu.gsyvideoplayer.utils.Debuger; import com.shuyu.gsyvideoplayer.video.GSYBaseVideoPlayer; import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.HashMap; import master.flame.danmaku.controller.IDanmakuView; import master.flame.danmaku.danmaku.loader.ILoader; import master.flame.danmaku.danmaku.loader.IllegalDataException; import master.flame.danmaku.danmaku.loader.android.DanmakuLoaderFactory; import master.flame.danmaku.danmaku.model.BaseDanmaku; import master.flame.danmaku.danmaku.model.DanmakuTimer; import master.flame.danmaku.danmaku.model.IDanmakus; import master.flame.danmaku.danmaku.model.IDisplayer; import master.flame.danmaku.danmaku.model.android.BaseCacheStuffer; import master.flame.danmaku.danmaku.model.android.DanmakuContext; import master.flame.danmaku.danmaku.model.android.Danmakus; import master.flame.danmaku.danmaku.model.android.SpannedCacheStuffer; import master.flame.danmaku.danmaku.parser.BaseDanmakuParser; import master.flame.danmaku.danmaku.parser.IDataSource; import master.flame.danmaku.danmaku.util.IOUtils; import master.flame.danmaku.ui.widget.DanmakuView; /** * Created by guoshuyu on 2017/2/16. * <p> * 配置弹幕使用的播放器,目前使用的是本地模拟数据。 * <p> * 模拟数据的弹幕时常比较短,后面的时长点是没有数据的。 * <p> * 注意:这只是一个例子,演示如何集合弹幕,需要完善如弹出输入弹幕等的,可以自行完善。 * 注意:b站的弹幕so只有v5 v7 x86、没有64,所以记得配置上ndk过滤。 */ 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; 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(); if (mDanmakuView != null && mDanmakuView.isPrepared()) { mDanmakuView.pause(); } } @Override public void onVideoResume() { super.onVideoResume(); if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) { mDanmakuView.resume(); } } @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 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); } } } 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) { //todo 替换成你的数据流 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 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.getDanmakuView().prepare(gsyVideoPlayer.getParser(), gsyVideoPlayer.getDanmakuContext()); } } /** * 弹幕偏移 */ private void resolveDanmakuSeek(DanmakuVideoPlayer gsyVideoPlayer, long time) { if (GSYVideoManager.instance().getMediaPlayer() != null && 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() { 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); } }