戴兜

戴兜的小屋

Coding the world.
github
bilibili
twitter

Analysis of Bilibili Live Danmaku WebSocket Protocol

image

Chrome DevTools can now directly view binary data packets!

Connection#

Through Chrome DevTools, it can be seen that the barrage of the web version of Bilibili's live broadcast is transmitted through wss://tx-sh3-live-comet-04.chat.bilibili.com/sub, and this address is different each time. The wss address needs to be obtained through https://api.live.bilibili.com/room/v1/Danmu/getConf?room_id=room_number&platform=pc&player=web to get the wss address (probably due to load balancing, any wss address can be used to obtain the barrage normally during actual testing).

The first 16 bytes of the packet header are used to identify the length and type of the data packet. The format of the data packet is detailed in the table below. The byte order is big-endian. Reference: https://blog.csdn.net/xfgryujk/article/details/80306776

OffsetLengthTypeMeaning
04intData packet length
42intData packet header length, fixed at 16
62intData packet protocol version (see below)
84intData packet type (see below)
124intFixed at 1
16-byte[]Data body
Data packet protocol versionMeaning
0Data packet payload is uncompressed JSON format data
1Client heartbeat packet, or server heartbeat response (with popularity value)
2Data packet payload is JSON format data compressed with zlib

After the client establishes a connection, it needs to send a data packet to join the room (authentication) within 5 seconds, otherwise the server will forcibly disconnect the connection. The content of the key field in the payload can be obtained from the previous https://api.live.bilibili.com/room/v1/Danmu/getConf?room_id=room_number&platform=pc&player=web. If the format of the authentication packet sent is incorrect, the server will immediately force the disconnection. The detailed explanation of the JSON fields is shown in the table below.

image

FieldTypeRequiredMeaning
uidnumber×User UID
roomidnumberRoom number
protovernumber×Protocol version, currently 2
platformstring×Platform, can be web
clientverstring×Client version, can be "1.8.5"
typenumber×Unknown, can be 2
keystring×User identifier, obtained through the mentioned interface

Heartbeat#

It is recommended to send a heartbeat packet every 30 seconds. The first 16 bytes of the header follow the rules mentioned above, and the payload content can be arbitrary. (Bilibili generates a heartbeat packet by passing an empty object, which explains the content of the heartbeat packet that I couldn't understand before)

image

Notification (Barrage, Announcement, Gift, etc.)#

When there are new barrages, gifts, or other announcements, the server will send data packets similar to the one shown below. First, you need to use zlib.inflate to decompress the data body part (excluding the first 16 bytes of the header). The decompressed data has the same first 16 bytes of the header, and the JSON format data is obtained by removing the header.

Here we take a barrage data packet as an example.

image

image

It can be seen that the decompressed data still contains the header (16 bytes), and the JSON format data is obtained by removing the header. The cmd field in the data describes the type of the data packet in more detail. One obvious example is that ['info'][1] represents the barrage content, ['info'][2][1] represents the sender, and ['info'][9]['ts'] represents the sending timestamp. The known formats of cmd are listed in the table below.

cmd fieldMeaning
DANMU_MSGReceived barrage
SEND_GIFTSomeone sent a gift
WELCOMEWelcome to the room
WELCOME_GUARDWelcome the room guard
SYS_MSGSystem message
PREPARINGHost is preparing
LIVELive broadcast starts

Below, I show the JSON format of several common cmd data packets. There are many other cmd types for activity notifications, which you can capture by yourself (for example, ACTIVITY_BANNER_UPDATE_V2 below is the activity notification cmd type that appeared on the day I wrote this article).

{
  "cmd": "SEND_GIFT",
  "data": {
    "giftName": "Spicy Strips",
    "num": 5,
    "uname": "Didomaso",
    "face": "http://i2.hdslb.com/bfs/face/1a3b795aafc5887f3f33909c7e66876d23911979.jpg",
    "guard_level": 0,
    "rcost": 42593386,
    "uid": 415822879,
    "top_list": [],
    "timestamp": 1570368091,
    "giftId": 1,
    "giftType": 0,
    "action": "Feed",
    "super": 0,
    "super_gift_num": 0,
    "price": 100,
    "rnd": "EF27025C-4C20-440F-B36F-64CCFABBF68E",
    "newMedal": 0,
    "newTitle": 0,
    "medal": [],
    "title": "",
    "beatId": "",
    "biz_source": "live",
    "metadata": "",
    "remain": 0,
    "gold": 0,
    "silver": 0,
    "eventScore": 0,
    "eventNum": 0,
    "smalltv_msg": [],
    "specialGift": null,
    "notice_msg": [],
    "capsule": null,
    "addFollow": 0,
    "effect_block": 1,
    "coin_type": "silver",
    "total_coin": 500,
    "effect": 0,
    "broadcast_id": 0,
    "draw": 0,
    "crit_prob": 0,
    "tag_image": "",
    "user_count": 0,
    "send_master": null
  }
}
{
  "cmd": "ACTIVITY_BANNER_UPDATE_V2",
  "data": {
    "id": 378,
    "title": "6th Place",
    "cover": "",
    "background": "https://i0.hdslb.com/bfs/activity-plat/static/20190904/b5e210ef68e55c042f407870de28894b/14vZu7h9pK.png",
    "jump_url": "https://live.bilibili.com/p/html/live-app-rankcurrent/index.html?is_live_half_webview=1&hybrid_half_ui=1,5,85p,70p,FFE293,0,30,100,10;2,2,320,100p,FFE293,0,30,100,0;4,2,320,100p,FFE293,0,30,100,0;6,5,65p,60p,FFE293,0,30,100,10;5,5,55p,60p,FFE293,0,30,100,10;3,5,85p,70p,FFE293,0,30,100,10;7,5,65p,60p,FFE293,0,30,100,10;&anchor_uid=22550271&is_new_rank_container=1&area_v2_id=163&area_v2_parent_id=3&rank_type=master_realtime_area_hour&area_hour=1",
    "title_color": "#8B5817",
    "closeable": 1,
    "banner_type": 4,
    "weight": 18,
    "add_banner": 0
  }
}
{
  "cmd": "ROOM_REAL_TIME_MESSAGE_UPDATE",
  "data": {
    "roomid": 101526,
    "fans": 294665,
    "red_notice": -1
  }
}

Afterword: Chrome's ability to directly display WebSocket binary packets really saves a lot of time. Chrome is awesome!

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.