lyyyuna 的小花园

动静中之动, by

RSS

B站直播弹幕协议详解

发表于 2016-03

前言

B站直播弹幕是在 flash 里做的,所以浏览器的开发工具抓不到包。去年我根据 Wireshark 抓的 TCP 包硬写了一个直播终端版,不过当时对里面很多二进制位不知所云。

上个星期我发现B站官方有专门为 UP 主准备的直播弹幕姬,且是用 C# 写的,于是就逆向了。。

这里是最新的 B 站直播弹幕姬 Python 版

本篇将对直播协议做一个完整介绍。

总体过程

直播协议首先需要获取 RoomId,获取服务器地址,然后与 B 站服务器建立 TCP 链接并保持,之后就处于监听状态,B 站会连续地将弹幕消息推送过来。

Get RoomId (HTTP) --> Get server address (HTTP) --> Open TCP & wait --> parse json
                                                           |                |
                                                           \ -- /

其中获取 RoomId 有些费解,一般我们认为一个 UP 主的房间 url 若为 http://live.bilibili.com/44515,则其 RoomId 就应该为 44515,但其实部分 UP 主的 url 和其 RoomId 并不对应。比如神奇陆夫人,其 url 显示为 115,但实际上 RoomId 为 1016。真正的 RoomId 需要去 http://live.bilibili.com/115 的 html 中寻找。

<script>
    var ROOMID = 1016;
    var DANMU_RND = 1457957537;
    var NEED_VIDEO = 1;
    var ROOMURL = 115;
</script>

这些特别的 UP 主经测试 url 都在 100 左右,猜测他们可能是 B 站老用户,或者他们向 B 站申请过特殊 url。

房间连接过程

获取 RoomId 之后,紧接一个 HTTP 请求 'http://live.bilibili.com/api/player?id=cid:RoomId',获取 xml 数据,

<uid>0</uid>
<uname></uname>
<login>false</login>
<isadmin>false</isadmin>
<time>1457954601</time>
<rank></rank>
<level></level>
<chatid>1016</chatid>
<server>livecmt-1.bilibili.com</server>
<user_sheid_keyword></user_sheid_keyword>
<sheid_user></sheid_user>
<block_time>0</block_time>
<block_type>0</block_type>
<state>LIVE</state>

其中 livecmt-1.bilibili.com 为直播弹幕地址。

然后向 livecmt-1.bilibili.com:788 开一个 TCP 链接,之后所有的弹幕和心跳包都会发生在此链接上。

进入直播间需要发送如下的数据包:

00000000  00 00 00 35 00 10 00 01  00 00 00 07 00 00 00 01 ...5.... ........
00000010  7b 22 72 6f 6f 6d 69 64  22 3a 31 30 31 36 2c 22 {"roomid ":1016,"
00000020  75 69 64 22 3a 31 35 35  39 37 33 36 38 35 37 32 uid":155 97368572
00000030  38 31 36 30 7d                                   8160}

0x35 是一次数据包的长度,0x00100001 不详,0x07 代表请求进入直播间,0x00000001 不详。 后面跟了一串 json 数据,uid 为客户端随机生成,算法如下:

(int)(100000000000000.0 + 200000000000000.0*random.random())

如果进入房间成功,则会返回

00 00 00 10 00 10 00 01  00 00 00 08 00 00 00 01

目前看来并没有其他特殊含义。

消息种类

如果接收到的数据包为

00000010  00 00 00 14 00 10 00 01  00 00 00 03 00 00 00 01 ........ ........
00000020  00 00 3c 49                                      ..<I

其中 0x14 为一次数据包的长度,0x0010001 不详,0x03 代表这是一个在线人数数据包,0x00000001 不详,0x3c49 = 15433 为在线人数。根据逆向的源码显示,0x03,0x02,0x01 都代表在线人数数据包,不过我只抓到了 0x03 这一种。

如果接收到的数据包为

00000825  00 00 00 d4 00 10 00 00  00 00 00 05 00 00 00 00 ........ ........
00000835  7b 22 69 6e 66 6f 22 3a  5b 5b 30 2c 31 2c 32 35 {"info": [[0,1,25
00000845  2c 31 36 37 37 37 32 31  35 2c 31 34 35 37 39 35 ,1677721 5,145795
00000855  38 33 37 34 2c 22 31 34  35 37 39 35 35 36 35 35 8374,"14 57955655
00000865  22 2c 30 2c 22 61 66 61  39 66 37 32 64 22 2c 30 ",0,"afa 9f72d",0
00000875  5d 2c 22 e7 81 ab e6 8a  8a e8 80 90 e4 b9 85 ef ],"..... ........
00000885  bc 88 30 32 ef bc 89 22  2c 5b 36 31 34 39 34 33 ..02..." ,[614943
00000895  2c 22 e8 8d 92 e5 b7 9d  e5 90 b9 e6 b0 b4 22 2c ,"...... ......",
000008A5  30 2c 30 2c 30 5d 2c 5b  39 2c 22 e7 b2 be e8 8b 0,0,0],[ 9,".....
000008B5  b1 22 2c 22 e7 a5 9e e5  a5 87 e9 99 86 e5 a4 ab .",".... ........
000008C5  e4 ba ba 22 2c 31 31 35  2c 31 32 32 32 35 32 35 ...",115 ,1222525
000008D5  35 5d 2c 5b 32 31 2c 32  35 38 32 37 5d 2c 5b 5d 5],[21,2 5827],[]
000008E5  5d 2c 22 63 6d 64 22 3a  22 44 41 4e 4d 55 5f 4d ],"cmd": "DANMU_M
000008F5  53 47 22 7d                                      SG"}

这是一个弹幕数据包,0xd4 为数据包的长度,0x00100000 不详,0x05 代表这是弹幕,0x00000000 不详。之后就是 json 格式的弹幕消息。

弹幕种类

跟据 json['cmd'] 的值,可分为:

对于 DANMU_MSG,

对于 SEND_GIFT,

对于 WELCOME,

心跳包

心跳包和进入直播间所发送的包仅有些许不同

00 00 00 10 00 10 00 01  00 00 00 02 00 00 00 01

把 0x07 变为 0x02 即可。心跳间隔为 30s。

结论

以下是我登陆 C菌 直播间抓取的弹幕消息。

效果图