Skip to content

💻作者 : SoEasy 📅时间 : 2024年4月5日 🪢个人公众号 : SoEasy同学

一、弹幕原理与开发

1.1 简介

在使用TVBox观看剧集时,可以通过弹幕开关打开,弹幕会随着视频的播放而滚动。如图:

image-20240405232234661

本文带你了解TVBox的FongMi版,是如何开发接口源的时候带上弹幕效果的。

1.2 弹幕原理

影视(FongMi)壳是支持播放时带弹幕的,需要在开发接口源时,向壳子提供弹幕接口,弹幕接口和解析后的播放直链(.m3u8)接口同步返回。

以java开发的接口源为例:

在接口源的playContent方法中,通常最终只返回url和header,例如:

java
return Result.get().url(realUrl).header(getHeader()).string();

其返回结构如下:

json
{
    “url”: "https://xxx.com/xxx.m3u8",
    "header": {xxx}
}

若要加弹幕接口,需要写成:

java
return Result.get().url(realUrl).danmaku(danmuUrl).header(getHeader()).string();

其返回接口如下:

json
{
    “url”: "https://xxx.com/xxx.m3u8",
    "danmaku": "http://xxx.com/danmuxxx",
    "header": {xxx}
}

这里的key为danmaku,是FongMi壳子支持的弹幕url的key。值danmuUrl必须是一个在线地址,不是直接返回弹幕文件。

因此弹幕接口,需要有服务器进行托管(相当于T4接口弹幕),当然,你也可以通过爬虫jar包中,启动本地代理,通过本地服务提供弹幕接口。

1.2 弹幕接口规范

FongMi壳对弹幕接口有一定规范,具体如下:

接口响应码:200

接口响应头:Content-Type: application/xml

接口响应结果:

xml
<?xml
version="1.0" encoding="UTF-8"?>
<i>
    <chatserver>chat.bilibili.com</chatserver>
    <chatid>52175602</chatid>
    <mission>0</mission>
    <maxlimit>1000</maxlimit>
    <state>0</state>
    <real_name>0</real_name>
    <source>k-v</source>
    <d p="1,5,24,16777215">有 6515 条弹幕列队来袭~做好准备吧!</d>
    <d p="0,1,24,16777215">这是我的第一条弹幕</d>
    <d p="0,1,24,16777215">这是我的第一条弹幕</d>
</i>

其中的字段解释如下:

  • chatserver 和 chatid 分别表示对话的服务和id,壳子不做要求,自己标记即可
  • mission 和 maxlimit 可按照示例指定
  • state real_name 均为0即可
  • source 固定k-v格式

d标签就是真正的弹幕数据,格式如下:

1)d标签的内容即弹幕内容

2)p属性有四个值,分别表示:

  • 弹幕随视频出现的时间(单位秒)
  • 弹幕的出现方式,1表示从右向左滚动,5表示固定在顶部居中
  • 弹幕的字体大小,表示24px
  • 弹幕的颜色,例如#FFFFFF是黑色,将16进制的FFFFFF转成10静止的结果

1.3 弹幕源

弹幕源就是弹幕网站的数据,有一些网站会自建弹幕源(例如玩偶),也有一些会采集爱优腾官方的弹幕。

推荐使用圣城影视的弹幕源接口,其接口规则如下:

https://dmku.thefilehosting.com/?ac=dm&url=https://www.iqiyi.com/v_22wkjrcd5fc.html

格式为: https://dmku.thefilehosting.com/?ac=dm&url= 参数url为视频的官网播放页面地址(爱奇艺),例如:https://www.iqiyi.com/v_22wkjrcd5fc.html

案例是仙剑四第一季的播放页面,弹幕源接口返回的数据为json格式,结果如下:

json
{
    "code": 23,
    "name": "94ae89d19150ee41ee2e P",
    "danum": 13121,
    "danmuku": [
        [
            1,
            "top",
            "#FFFFFF",
            "",
            "有 13121 条弹幕列队来袭~做好准备吧!"
        ],
        [
            "6",
            "top",
            "#fb7299",
            "",
            "请不要相信视频中任何广告,有任何问题到留言进行反馈!"
        ],
        [
            1,
            "right",
            "#FFFFFF",
            "",
            "鞠宝出场  黄金万两",
            "",
            "",
            "24px"
        ]
    ]
}

danmuku对应的值为弹幕数据,格式为:

  • 第1个表示类型1-滚动,6-固定
  • 第2个“top”或"right",表示方向,在顶部或者从右向左
  • 第3个表示字体颜色
  • 第5个为弹幕内容
  • 第8个为弹幕字体大小

1.4 弹幕接口

通过以上步骤大概能够开发出一个在FongMi壳播放的弹幕demo。你只需要部署一个服务,提供一个在线的弹幕接口,通过入参(例如剧名、集数、分类等)去调用圣城的弹幕库的数据,将json格式的数据转换成FongMi壳需要的xml格式,弹幕接口url地址提供给接口源开发的playContent方法里的danmaku字段即可。壳子会去调用弹幕url获取弹幕数据。

二、本地代理

2.1 本地代理原理

上面的步骤需要服务器托管弹幕接口,一般开发接口源时并没有服务器可托管,因此,可以利用FongMi壳提供的本地代理接口实现弹幕接口托管。

本地代理的本质是:FongMi壳在软件启动时,会提供一个本地http服务,默认ip为127.0.0.1,端口9978。默认提供了如推送url,设置地址等功能。但是你可以通过开发jar包时,提供一个proxy的类来拓展本地接口的功能。

你可以在FongMi的爬虫仓库获取到proxy.java代码。提供了一下方法:

java
public static Object[] proxy(Map<String, String> params) throws Exception {
        switch (params.get("do")) {
            case "ck":
                return new Object[]{200, "text/plain; charset=utf-8", new ByteArrayInputStream("ok".getBytes("UTF-8"))};
            case "ali":
                return Ali.proxy(params);
            case "bili":
                return Bili.proxy(params);
            case "webdav":
                return WebDAV.vod(params);
            default:
                return null;
        }
    }

使用FongMi的爬虫仓库打包后,你可以通过设备的ip,访问上面的ck即测试接口:check

接口url如下:

http://127.0.0.1:9978/proxy?do=ck

将ip替换成设备的ip进行测试时,会返回字符串ok。参数params可以在url后面加上&xxx=xxx提供。

image-20240406002705083

2.2 本地代理开发弹幕接口

基于以上原理,你可以拓展开发出一个本地弹幕接口,用于将圣城弹幕源数据转成壳子需要的xml格式,再将本地弹幕接口作为壳子需要的弹幕接口提供出去即可。

改造逻辑如下:

  • 拓展proxy里的方法,添加一个case,
case "danmu":
    return Danmu.proxy(params);
  • 在Danmu.java文件里,提供proxy静态方法(demo)
java
public static Object[] proxy(Map<String, String> params) {
        // 弹幕库 https://dmku.thefilehosting.com/?ac=dm&url=https://www.iqiyi.com/v_28zgfdyu0ns.html
        Object[] result = new Object[3];
        result[0] = 200;
        result[1] = "application/xml";
        String test = "<?xml\n" +
                "version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<i>\n" +
                "    <chatserver>chat.bilibili.com</chatserver>\n" +
                "    <chatid>52175602</chatid>\n" +
                "    <mission>0</mission>\n" +
                "    <maxlimit>1000</maxlimit>\n" +
                "    <state>0</state>\n" +
                "    <real_name>0</real_name>\n" +
                "    <source>k-v</source>\n" +
                "    <d p=\"1,5,24,16777215\">有 无数 条弹幕列队来袭~做好准备吧!</d>\n" +
                "    <d p=\"0,1,24,16777215\">SoEasy的弹幕测试0</d>\n" +
                "    <d p=\"1,1,24,16777215\">SoEasy的弹幕测试1</d>\n" +
                "    <d p=\"3,1,24,16777215\">SoEasy的弹幕测试3</d>\n" +
                "    <d p=\"6,1,24,16777215\">SoEasy的弹幕测试6</d>\n" +
                "    <d p=\"9,1,24,16777215\">SoEasy的弹幕测试9</d>\n" +
                "    <d p=\"15,1,24,16777215\">SoEasy的弹幕测试15</d>\n" +
                "</i>";
        result[2] = new ByteArrayInputStream(test.getBytes());
        return result;
    }
  • 在你开发的接口源里,playcontent方法改造成:
java
{
    // xxxx
    String danmuUrl = Proxy.getUrl() + "?do=danmu"; //getUrl()的结果就是 127.0.0.1:9978
    return Result.get().url(realUrl).danmaku(danmuUrl).header(getHeader()).string();
}

ps:

注意本地代理接口proxy的写法:

public static Object[] proxy(Map<String, String> params)

入参接受一个map,可以传递剧名、集数等参数

返回值是一个对象数据。开发时参照案例开发即可。规则如下:

  • 第一个对象,返回响应码
  • 第二个对象,返回响应头Content-Type的值
  • 第三个对象,返回响应内容的字节数组输入流

2.3 抓包测试

以ok的线路中南瓜弹幕为例,播放时抓包,可以看到弹幕接口:

以ok的线路进行弹幕测试: url: 192.168.1.6:9978/proxy?do=danmu&type_name=悬疑,谍战,国产,电视剧&vod_year=&vod_name=追击者&ep_name=1

以本地demo案例测试效果如下:

image-20240406004310171