Android 直播 弹幕

34 篇文章 0 订阅

弹幕使用场景
  1. 直播(实时性)

弹幕是直播系统的核心功能之一。一段摘抄的直播弹幕描述(貌似美拍工程师写的)

  • 美拍直播弹幕系统从 2015 年 11 月到现在,经过了三个阶段的演进,目前能支撑百万用户同时在线
  • 前中期使用 HTTP 轮询方案,中后期替换为长连接方案
  • 直播间消息,相对于 IM 的场景,有如下几个特点:
  • 消息要求及时,过时的消息对于用户来说不重要
  • 松散的群聊,用户随时进群,随时退群
  • 用户进群后,离线期间(接听电话)的消息不需要重发
  1. 视频(实时性 + 弹幕跟帧时间点关联)
弹幕引擎(B站开源弹幕)

GitHub:https://github.com/bilibili/DanmakuFlameMaster

DanmakuFlameMaster 是 Android 上开源弹幕解析绘制引擎项目,貌似Android 上最好的开源弹幕引擎·烈焰弹幕

DanmakuFlameMaster 开发包已被包括优酷土豆、开迅视频、MissEvan、echo回声、斗鱼TV、天天动听、被窝声次元、ACFUN 等 APP 使用

DanmakuFlameMaster 特点
  1. 使用多种方式(View/SurfaceView/TextureView)实现高效绘制
  2. B站xml弹幕格式解析
  3. 基础弹幕精确还原绘制
  4. 支持mode7特殊弹幕
  5. 多核机型优化,高效的预缓存机制
  6. 支持多种显示效果选项实时切换
  7. 实时弹幕显示支持
  8. 换行弹幕支持/运动弹幕支持
  9. 支持自定义字体
  10. 支持多种弹幕参数设置
  11. 支持多种方式的弹幕屏蔽
DanmakuFlameMaster 细节API
  1. 模式在BaseDanmaku里有声明,总结一下就是
  	public final static int TYPE_SCROLL_RL = 1; // 水平右往左

    public final static int TYPE_SCROLL_LR = 6; // 水平左往右

    public final static int TYPE_FIX_TOP = 5; // 固定在顶部

    public final static int TYPE_FIX_BOTTOM = 4; // 固定在底部

    public final static int TYPE_SPECIAL = 7; // 特殊弹幕
  1. 添加一条图文混合弹幕
    请参考:DanmuKuDemo

  2. 展示隐藏弹幕

// 实现了IDanmakuView接口的弹幕View,调用下面2个方法展示或隐藏弹幕
void show();
void hide();
  1. 如何监听每一个弹幕的移动的x,y位置

查看一下BaseDanmaku.layout方法的实现就明白了,弹幕库每次绘制前都会调用这个方法

集成B站弹幕
  1. 集成配置
repositories {
    jcenter()
}

// 如果你不需要兼容x86和armv5就不用添加最下面两行的内容了
dependencies {
    api 'com.github.ctiao:DanmakuFlameMaster:0.9.25'
    api 'com.github.ctiao:ndkbitmap-armv7a:0.9.21'

    // Other ABIs: optional 这个是适配多种架构的,如果你用虚拟机建议加上
    api 'com.github.ctiao:ndkbitmap-armv5:0.9.21'
    api 'com.github.ctiao:ndkbitmap-x86:0.9.21'
}
  1. DanmakuFlameMaster 使用多种方式(View / SurfaceView / TextureView)实现高效绘制!其中分别对应(DanmakuView/DanmakuSurfaceView/DanmakuTextureView)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <master.flame.danmaku.ui.widget.DanmakuView
        android:id="@+id/sv_danmaku"
        android:layout_width="match_parent"
        android:layout_height="300dp" />

</RelativeLayout>
  1. 代码配置
 		// 设置最大显示行数(滚动弹幕最大显示行数)
        HashMap<Integer, Integer> maxLinesPair = new HashMap<>();
        maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, 2);

        // 设置是否禁止重叠
        HashMap<Integer, Boolean> overlappingEnablePair = new HashMap<Integer, Boolean>();
        overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_RL, true);
        overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_TOP, true);

        mContext = DanmakuContext.create();
        mContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 3)
                .setDuplicateMergingEnabled(false)
                .setScrollSpeedFactor(1.2f)
                // 设置文字的比例
                .setScaleTextSize(1.2f)
                // 图文混排使用 SpannedCacheStuffer
                .setCacheStuffer(new SpannedCacheStuffer(), mCacheStufferAdapter) 
				// 绘制背景使用 BackgroundCacheStuffer
				.setCacheStuffer(new BackgroundCacheStuffer())  
                .setMaximumLines(maxLinesPair)
                .preventOverlapping(overlappingEnablePair).setDanmakuMargin(40);

        if (mDanmakuView != null) {
            mParser = createParser(null);
            mDanmakuView.setCallback(new master.flame.danmaku.controller.DrawHandler.Callback() {
                @Override
                public void updateTimer(DanmakuTimer timer) {
                }

                @Override
                public void drawingFinished() {
                    // 弹幕绘制完成时回掉(即弹幕展示完成移出屏幕时调用)
                    Log.i(TAG, "drawingFinished");
                }

                @Override
                public void danmakuShown(BaseDanmaku danmaku) {
                    Log.i(TAG, "danmakuShown " + danmaku.text);
                }

                @Override
                public void prepared() {
                    // 弹幕准备好的时候回掉,这里启动弹幕
                    Log.i(TAG, "prepared");
                    mDanmakuView.start();
                }
            });
            mDanmakuView.setOnDanmakuClickListener(new IDanmakuView.OnDanmakuClickListener() {

                @Override
                public boolean onDanmakuClick(IDanmakus danmakus) {
                	Log.i(TAG, "onDanmakuClick danmakus size: " + danmakus.size());
                    BaseDanmaku latest = danmakus.last();
                    if (null != latest) {
                    Log.i(TAG, "onDanmakuClick text of latest danmaku: " + latest.text);
                        return true;
                    }
                    return false;
                }

                @Override
                public boolean onDanmakuLongClick(IDanmakus danmakus) {
                    return false;
                }

                @Override
                public boolean onViewClick(IDanmakuView view) {
                    return false;
                }
            });
            mDanmakuView.prepare(mParser, mContext);
            mDanmakuView.showFPS(true);
            mDanmakuView.enableDanmakuDrawingCache(true);
        }
  1. 直播页面按Home键退后台或重新回到前台,相关的生命周期方法必须设置
// View 实现 弹幕
private IDanmakuView mDanmakuView;

@Override
    protected void onPause() {
        super.onPause();
        if (mDanmakuView != null && mDanmakuView.isPrepared()) {
            mDanmakuView.pause();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) {
            mDanmakuView.resume();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mDanmakuView != null) {
            // dont forget release!
            mDanmakuView.release();
            mDanmakuView = null;
        }
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        if (mDanmakuView != null) {
            // dont forget release!
            mDanmakuView.release();
            mDanmakuView = null;
        }
    }
  1. 添加一条普通文字弹幕
/**
* danmaku.isLive == true的情况下,请在非UI线程中使用此方法,避免可能卡住主线程
* @param item
*/
void addDanmaku(BaseDanmaku item);


private void addDanmaku(boolean isLive) {

        // 创建一个弹幕对象,这里后面的属性是设置滚动方向的!
        BaseDanmaku danmaku =
                mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
        if (danmaku == null || mDanmakuView == null) {
            return;
        }
        danmaku.text = "这是一条弹幕";

        // 设置相应的边距,这个设置的是四周的边距
        danmaku.padding = 5;

        // 可能会被各种过滤器过滤并隐藏显示,若是本机发送的弹幕,建议设置成1;
        danmaku.priority = 0;

        // 是否是直播弹幕
        danmaku.isLive = isLive;

        danmaku.setTime(mDanmakuView.getCurrentTime() + 1200);

        danmaku.textSize = 25f * (mParser.getDisplayer().getDensity() - 0.6f);

        // 设置文字颜色
        danmaku.textColor = Color.RED;

        // 设置阴影的颜色
        danmaku.textShadowColor = Color.WHITE;

        // danmaku.underlineColor = Color.GREEN;
        danmaku.borderColor = Color.RED;

        // 添加这条弹幕,也就相当于发送
        mDanmakuView.addDanmaku(danmaku);

    }
    

B站集成常见问题

参考
  1. 关于B站的弹幕集成 01
  2. 关于B站的弹幕集成 02
  3. DanmuKuDemo
  4. Android弹幕功能实现,模仿斗鱼直播的弹幕效果 (郭霖blog)
  5. 自己研究写的一个弹幕库的功能
  6. DanmuKuDemo
  7. 开源弹幕引擎·烈焰弹幕使(DanmakuFlameMaster)使用解析
  8. Android弹幕实现:基于B站弹幕开源系统(1) 重点关注一下
  9. B站弹幕集成常见问题
  10. DanmakuFlameMaster 使用小结
  11. 弹幕框架DanmakuFlameMaster简单分析
  12. EasyBarrage
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初心一点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值