格式为google protocol buffer (protobuf)
地址格式为{cid}-{part}.pb,每part包含3分钟的弹幕内容,即边播放边加载模式,如-3.pb包含第6-9分弹幕
在bilibiliPlayer.min.js中有大部分field id的定义,但文件中出现了id12-14
pb格式弹幕 Field常量
1 row (Row)
2 chat_server (string)
3 chat_id (varint32)
4 mission (varint32)
5 max_limit (varint32)
6 source (string)
7 ds (varint32)
8 de (varint32)
9 max_count (varint32)
10 realname (varint32)
11 sectionlen (varint32)(一般为180)
//猜测id
12 duration (float)
13 total_count (varint32)
14 pb_version (varint32)(目前常量1)
pb格式弹幕row Field常量
1 playtime (float)
2 mode (varint32)
3 fontsize (varint32)
4 color (varint32)
5 times (varint32)
6 poolid (varint32)
7 hash (string)
8 dmid (varint32)
9 msg (string)
10 uid (varint32)(暂未出现)
11 uname (string)(暂未出现)
其中varint32为变长度int32格式,具体为每字节只有后7位存储数据,第1位为指示位,为1时继续读取下一字节
function readVarInt32($fp){
$b=0;
$result=0;
do{
$b=unpack('C',fread($fp,1))[1];
$result = $b & 0x7f;
if(!( $b & 0x80 ))
break;
$b=unpack('C',fread($fp,1))[1];
$result |= ($b & 0x7f)<<7;
if(!( $b & 0x80 ))
break;
$b=unpack('C',fread($fp,1))[1];
$result |= ($b & 0x7f)<<14;
if(!( $b & 0x80 ))
break;
$b=unpack('C',fread($fp,1))[1];
$result |= ($b & 0x7f)<<21;
if(!( $b & 0x80 ))
break;
$b=unpack('C',fread($fp,1))[1];
$result |= ($b & 0x7f)<<28;
if(!( $b & 0x80 ))
break;
for($i=0;$i<5;$i++){
$b=unpack('C',fread($fp,1))[1];
if(!( $b & 0x80 ))
break;
}
}while(false);
return $result;
}
文件读取代码样本
<?php
/*
BiliBili pb danmaku reader
@author esterTion(esterTionCN@gmail.com)
*/
$pb=fopen('http://comment.bilibili.com/14328539-1.pb','r');
function readVarInt32($fp){
$b=0;
$result=0;
do{
$b=unpack('C',fread($fp,1))[1];
$result = $b & 0x7f;
if(!( $b & 0x80 ))
break;
$b=unpack('C',fread($fp,1))[1];
$result |= ($b & 0x7f)<<7;
if(!( $b & 0x80 ))
break;
$b=unpack('C',fread($fp,1))[1];
$result |= ($b & 0x7f)<<14;
if(!( $b & 0x80 ))
break;
$b=unpack('C',fread($fp,1))[1];
$result |= ($b & 0x7f)<<21;
if(!( $b & 0x80 ))
break;
$b=unpack('C',fread($fp,1))[1];
$result |= ($b & 0x7f)<<28;
if(!( $b & 0x80 ))
break;
for($i=0;$i<5;$i++){
$b=unpack('C',fread($fp,1))[1];
if(!( $b & 0x80 ))
break;
}
}while(false);
return $result;
}
function readString($fp){
$strLength=readVarInt32($fp);
return fread($fp,$strLength);
}
$types=array(
'VARINT'=>0,
'FIXED64'=>1,
'DELIMITED'=>2,
'START_GROUP'=>3,
'END_GROUP'=>4,
'FIXED32'=>5
);
$rowCount=0;
while(true){
$id=fread($pb,1);
if($id==='')
break;
$id=unpack('C',$id)[1];
$type = $id & 7;
$id = $id >> 3;
switch($id){
case 1:
$rowLength=readVarInt32($pb);
echo 'row length:'.$rowLength.' ';
$end=ftell($pb)+$rowLength;
$row=array();
while(ftell($pb) < $end){
$id=unpack('C',fread($pb,1))[1];
$id = $id >> 3;
switch($id){
case 1:
$row['playtime']=unpack('f',fread($pb,4))[1];
break;
case 2:
$row['mode']=readVarInt32($pb);
break;
case 3:
$row['fontsize']=readVarInt32($pb);
break;
case 4:
$row['color']=substr(pack('N', readVarInt32($pb) ),2);
break;
case 5:
$row['timestamp']=readVarInt32($pb);
break;
case 6:
$row['poolid']=readVarInt32($pb);
break;
case 7:
$row['hash']=readString($pb);
break;
case 8:
$row['dmid']=readVarInt32($pb);
break;
case 9:
$row['msg']=readString($pb);
break;
case 10:
$row['uid']=readVarInt32($pb);
break;
case 11:
$row['uname']=readString($pb);
break;
}
}
print_r($row);
$rowCount++;
break;
case 2:
echo '[chat_server] => '.readString($pb)."\n";
break;
case 3:
echo '[chat_id] => '.readVarInt32($pb)."\n";
break;
case 4:
echo '[mission] => '.readVarInt32($pb)."\n";
break;
case 5:
echo '[max_limit] => '.readVarInt32($pb)."\n";
break;
case 6:
echo '[source] => '.readString($pb)."\n";
break;
case 7:
echo '[ds] => '.readVarInt32($pb)."\n";
break;
case 8:
echo '[de] => '.readVarInt32($pb)."\n";
break;
case 9:
echo '[max_count] => '.readVarInt32($pb)."\n";
break;
case 10:
echo '[realname] => '.readVarInt32($pb)."\n";
break;
case 11:
echo '[sectionlen] => '.readVarInt32($pb)."\n";
break;
default:
switch($type){
case 0:
echo '--Unknown ID '.$id.' with varint32 value '.readVarInt32($pb)."\n";
break;
case 1:
echo '--Unknown ID '.$id.' with fixed 64bit data hex '.bin2hex(fread($pb,8))."\n";
break;
case 5:
echo '--Unknown ID '.$id.' with fixed 32bit data hex '.bin2hex(fread($pb,4))."\n";
break;
default:
echo '--Unknown ID '.$id.' with type '.$type."\n";
}
}
}
echo $rowCount.' rows in file'."\n";