BiliBili直播弹幕WS协议浅析

相关接口

  • GET https://api.live.bilibili.com/room/v1/Room/room_init

    参数:id 直播间号,可以是短号

    用于获取短号直播间的真实直播间号,以及主播uid

  • GET https://api.live.bilibili.com/room/v1/Danmu/getConf

    参数:

    ​ room_id 直播间号

    ​ platform=pc 观看平台

    ​ player=web 播放方式

    用于获取弹幕服务ws地址和token,有个通用的地址:broadcastlv.chat.bilibili.com

WS

ws地址:wss://broadcastlv.chat.bilibili.com/sub

数据由两部分组成,Header和Body

在连接后5s内需要发送认证数据包(即Body为认证数据),否则会被服务器断开连接

完成认证后,进行正常的交互

/**
 * @author mashirot
 */
data class DataHeader(
    val totalLength: Int,
    val headerLength: Short,
    val protocolVersion: Short,
    val dataType: Int,
    val fixed: Int,
)

给出如上类定义

totalLength: 为Header+Body的总bit数

headerLength: 固定为16,即用2个Byte存头部

protocolVersion: 协议版本

dataType: 数据类型

fixed: 固定位,无意义

/**
 * @author mashirot
 */
object DataHeaderConsts {
    const val TOTAL_LENGTH_IDX = 0
    const val HEADER_LENGTH_IDX = 4
    const val PROTOCOL_VERSION_IDX = 6
    const val DATA_TYPE_IDX = 8
    const val FIXED_IDX = 12

    const val HEADER_LENGTH: Short = 16
    const val HEADER_LENGTH_INT = 16
    const val UNCOMPRESSED_PROTOCOL: Short = 0
    const val HEARTBEAT_PROTOCOL: Short = 1
    const val COMPRESS_PROTOCOL: Short = 3
    const val FIXED_VAL = 1

    const val CLIENT_HEARTBEAT = 2
    const val SERVER_HEARTBEAT = 3
    const val SERVER_ADVICE = 5
    const val CLIENT_AUTHORIZE = 7
    const val SERVER_AUTHORIZE = 8
}

给出如上常量定义,意义不再解释,由如下几点说明

UNCOMPRESSED_PROTOCOL通常用来传递高能榜人数,直播间在线人数等数据,dataType通常为SERVER_ADVICE

HEARTBEAT_PROTOCOL指的是本数据包为心跳包,除了认证心跳包外,其余均无Body,即totalLength = headerLength = 16

COMPRESS_PROTOCOL通常就是弹幕数据,Body中包含多条弹幕

AuthorizeBody

/**
 * @author mashirot
 */
data class AuthorizeBody(
    val uid: Long?,
    val roomid: Long,
    val protover: Int,
    val buvid: String?,
    val platform: String,
    val type: Int,
    val key: String?,
)

给出如上类定义

uid: uid,用于解除风控导致的弹幕用户名不可见

roomid: 直播间号,不能为短号,请通过相关接口获取真实直播间号

protover: 协议版本,目前为3

buvid: 未知,规则为uuid+infoc,解除风控用

platform: web/android,正常使用web

type: 未知,可以是2

key: 调用https://api.live.bilibili.com/room/v1/Danmu/getConf接口返回的token值,解除风控用

如果不需要解除风控,只需要给roomid, protover, platform, type即可

HeartbeatBag

val header = DataHeader(HEADER_LENGTH_INT, HEADER_LENGTH, HEARTBEAT_PROTOCOL, CLIENT_HEARTBEAT, FIXED_VAL)

弹幕数据

从请求头可以发现gzip, deflate, br这三种压缩算法,网上很多文章都说用zlib解压,实践中确实可以解压出数据,但会有部分乱码,因为实际采用的压缩算法是Google的brotli算法(也可能是b站改了

将数据的前16位取出,解析成DataHeader,判断是否是COMPRESS_PROTOCOL

通过brotli算法解压出弹幕的json(可能为List),并拆分成独立的json对象串,解析成弹幕数据


给出我的代码实现:bilibili-live-comet-demo


参考文章:

  1. B站直播弹幕ws协议分析
  2. 使用JavaScript中的WebSocket获取b站直播间弹幕
  3. Bilibili_Danmuji

ねぇ,あなたは何色になりたい