论B站直播流到底有多少特性🤔
一、直播流
1.1 视频流
1.1.1 分辨率(FMP4)
实际观测:如果不切换fmp4流中的
#EXT-X-MAP:URI="h1xxxxxxxx.m4s"
文件,并且持续接收m4s流,如果遇到主播切换视频分辨率,那么录制出的视频会有多个分辨率。但是元信息中显示的分辨率会是第一次接收#EXT-X-MAP:URI="h1xxxxxxxx.m4s"
时返回的分辨率。依据:
在B站直播中确实可以随时切换分辨率,流不断开。
B站直播时可以与其他主播连麦,合并两路视频流。此时的分辨率会变成近似正方形的分辨率。
1.1.2 mdat中的BVCLIVESTREAMHOP
实际观测:只要是以原始流(指不二次压缩转码)录制的视频文件,在数据块(此处以MPEG-4的mdat为例)中,会看到:
BVCLIVESTREAMHOP[{"author":"pc_link","author_ver":"6.24.0.8301","clock_max_error_ms":52,"curr_ms":1742864076071},{"author":"mg","author_ver":"v1.8.34","host":"edge-computing-bvc-self-cn-jsyz-ct-03-07-585fff6d76-8shft","cluster":"bvc-self-cn-jsyz-ct-03","curr_ms":1742864076699}]
类似的ASCII字符串。
依据:
目前未知这东西的具体用途,但是貌似包含了B站直播流编码服务器的一些元数据。
目前发现,所有的直播都会有这个,并且基本上是每一个(或者隔几个)m4s分片(flv是隔一段时间)之后都会掺杂这个字符串。
后果:数据位于mdat数据块中,所以除了ASCII编辑器以外,没有软件能读到这个信息。
1.1.3 mdat中的其他不常见数据
实际观测:部分直播由于使用了B站的某些功能,导致使用功能时会在视频流中有记录。
一些未知用途的数据如下(采集于视频中,未改动):
IRCA_SEI_PC_LINK{"MULIT_VIDEO_LINK":{"data":[],"invoking_time":2,"is_deprecated":1,"layout":"left1_right1","room_start_at_ts":643,"version":1742788235942}}
BVCLIVETIMESTAMP{"author":"nginx","curr_ms":1742817624296}
// 这个看名字是记录直播的unix时间
IRCA_SEI_PC_LINK{"MULIT_VIDEO_LINK":{"data":[{"avatar":"https://i0.hdslb.com/bfs/face/aa90d66509d2525a874c40190d886e062bb01703.jpg","gender":-1,"nickname":"\u5C0F\u96EA\u597D\u8FD0\u8FDE\u8FDE","pos_index":1,"price":0,"price_text":"0","room_id":1920035951,"uid":3546714107545875,"volume":0},{"avatar":"https://i2.hdslb.com/bfs/face/b69f844ffe78b042c5bd750d1eec53c17e578ec6.jpg","gender":-1,"nickname":"\u5C0F\u4E5D28\u53F7\u5468\u5E74\u5566","pos_index":2,"price":0,"price_text":"0","room_id":32309229,"uid":3546649739659622,"volume":19335}],"invoking_time":2,"is_deprecated":1,"layout":"left1_right1","room_start_at_ts":1051,"version":1742734541531}}
//这个是在双屏连麦时候会出现,此时会改变视频分辨率。
后果:数据位于mdat数据块中,所以除了ASCII编辑器以外,没有软件能读到这个信息。但是好处是多了一些和直播相关的数据以便分析。
1.1.4 HLS流的M3U8自定义参数(FMP4)
实际观测:目前获取fmp4格式的m3u8索引文件,会发现这个文件是被B站加了自定义参数的,并且有一些含义。
m3u8样例:
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-START:TIME-OFFSET=0
#EXT-X-MEDIA-SEQUENCE:6743230
#EXT-X-TARGETDURATION:1
#EXT-X-MAP:URI="h1742371280.m4s"
#EXT-BILI-AUX:1f3b9f1|K|e19ac|b1c64b4f
#EXTINF:1.00,e19ac|b1c64b4f
6743230.m4s
#EXT-BILI-AUX:1f3bdb8|N|96232|9ebe7f7f
#EXTINF:1.00,96232|9ebe7f7f
6743231.m4s
#EXT-BILI-AUX:1f3c1c1|K|10703d|5d47057f
#EXTINF:1.00,10703d|5d47057f
6743232.m4s
#EXT-BILI-AUX:1f3c588|N|90f1e|70dc1956
#EXTINF:1.00,90f1e|70dc1956
6743233.m4s
#EXT-BILI-AUX:1f3c991|K|ddf41|50726259
#EXTINF:1.00,ddf41|50726259
6743234.m4s
#EXT-BILI-AUX:1f3cd58|N|963a5|23b3c9fe
#EXTINF:1.00,963a5|23b3c9fe
6743235.m4s
其中,#EXT-X-MEDIA-SEQUENCE:6743230
代表该m3u8的起始分片序号,但是当实际序号超过9999999时,这个序号就不可靠了。
#EXT-BILI-AUX:1f3b9f1|K|e19ac|b1c64b4f
的解析:未知|应该是分片类型|未知|CRC32校验码
后果:可以通过这些信息来判断当前m3u8中的流是否正确,以及校验分片完整性。
1.1.5 视频文件头中的直播推流客户端标识符
实际观测:无论是flv还是fmp4原始流,文件头中都会有关于主播使用的推流软件的基本参数。
目前用的最多的是:BVC-SRT LiveHime/6.24.0
(B站官方直播姬)和(libobs version 31.0.2)
(OBS),版本号各有不同,此处只举例。
依据:
每个直播都会有这个标识。
如果流被压缩(不是原画),那么这个标识符就不再是原始的了,而是B站的直播视频编码器。可以通过这个特性判断下载的是不是原始画质的流。
后果:可以知道主播在用什么(版本的)软件🤔。
1.1.6 高效压缩
实际观测:一旦视频中有空白画面/无声音,那么对应的分片会很高效的压缩,压缩后的分片只有几十KB。
依据:🤔B站肯定不想浪费流量(
后果:节省部分存储空间。其余无影响(但是BVCxxx的数据仍然会有)
1.1.7 视频流TimeStamp offset(FMP4)
实际观测:下载fmp4流时,无论以什么方法下载,存储在
"h1xxxxxxxx.m4s"
(也有可能是在m4s的元数据部分)中的时间偏移量永远等于客户端中显示的持续直播时长。
在视频下载完后,使用MPV播放时,进度条会从视频文件录制时获取的那个直播时长为起始,所以会看到一打开视频就显示在几个小时,并且这之前的进度条无法拖动(因为对应的分片根本没下载,如果补上之前的分片则可以拖动到对应位置。)
后果:部分播放器的进度条组件会彻底失效。但是使用FFmpeg复制一次流就不再有这个问题。
二、直播流API
2.1 getRoomPlayInfo
API的返回值
2.1.1 不返回真原画地址,但可手动拼接字符串
实际观测:在使用https://api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo?room_id=5050&protocol=0,1&format=0,1,2&codec=0,1,2,3获取视频流URL时,会发现这种高人气的直播间根本不返回原画的URL,而是被压缩处理过的:
(部分JSON)
"format_name": "fmp4",
"codec": [
{
"codec_name": "avc",
"current_qn": 10000,
"accept_qn": [10000, 400, 250, 150],
"base_url": "/live-bvc/544181/live_392836434_52698792_bluray/index.m3u8?",
"url_info": [
{
"host": "https://cn-sxxa-cm-01-03.bilivideo.com",
"extra": "expires=1742921243&len=0&oi=0x24098a7002b33b3115e31bf02e960edf&pt=web&qn=10000&trid=10070275ba6278f6d722085c00632867e2d0&sigparams=cdn,expires,len,oi,pt,qn,trid&cdn=cn-gotcha01&sign=d3863355762738a257464c66380e94c7&site=7e9ea46673c899115640c608c2355a4a&free_type=0&mid=14120472&sche=ban&bvchls=1&sid=cn-sxxa-cm-01-03&chash=0&bmt=1&sg=lr&trace=65&isp=cm&rg=NorthWest&pv=Shaanxi&score=100&long_ab_flag_value=test&sl=3&strategy_id=24&hot_cdn=909701&source=puv3_onetier&pp=rtmp&suffix=origin&flvsk=1174fda3e37dc7adc04251e0e9785a49&deploy_env=prod&ld=gjz&strategy_version=latest&p2p_type=1&info_source=cache&long_ab_flag=live_default_longitudinal&origin_bitrate=692365&sk=a6a3deaaf651addb739bfb48c2f5bde0&long_ab_id=45&vd=nc&zoneid_l=151437315&sid_l=live_392836434_52698792&src=puv3&order=1",
"stream_ttl": 0
}
],
"hdr_qn": null,
"dolby_type": 0,
"attr_name": "",
"hdr_type": 0
},
从中发现,直播流的base_url
地址中会有_bluray
后缀。类似的后缀还有:_prohevc _2500 _1500
(可能还有更多但是并未发现)。
但是,如果把这些后缀删去,再访问URL,仍然可以获得原画的URL并且可以正常录制。
依据:
多个开发者早就发现了此问题,API根本不返回原画,导致画面糊,码率低。
部分录播软件开发者可能不知道解决办法,就不再更新这方面的解决办法,导致录播软件无法正常使用。
后果:无法正常获取原画,导致下载变得麻烦。
三、信息流
3.1 弹幕信息流
3.1.1 使用短链ID无法获取完整的数据
实际观测:如果使用短链ID(比如
5050
)发送数据包并验证WebSocket连接,那么后续将不会获得完整的信息流数据包。
但是可以通过https://api.live.bilibili.com/room/v1/Room/get_info?room_id={roomid}
获取到room_id
这个值,是完整的ID。
这块神奇特性,短链ID和完整ID之间的不完全互通,估计是B站史山代码导致的,各个API行为不一致。
后果:获取信息流的代码需要多加一步获取完整ID的方法。