GB28181
支持GB28181是正确的事情,可能也是困难的事情,因为困难所以有趣。
重要说明:SRS 5.0已经是beta或更稳定的版本,推荐使用SRS 5.0的GB,而不要使用4.0的GB,因为虽然4.0是稳定发布版本,但是4.0的GB是实验性的和不稳定的。
研发的详细过程请参考#3176。
Usage
首先,编译和启动SRS,请确认版本为5.0.74+
:
./configure --gb28181=on
make
./objs/srs -c conf/gb28181.conf
Note: 如果你是公网服务器,一定要配置对Candidate,请参考Candidate的说明。
然后,在摄像头配置中,选择AAC编码,然后在平台中配置SIP服务器为SRS,如下图所示:
- 必须是
AAC
编码,在音频编码中,选择AAC
,采样率44100HZ
。 - 必须是
GB-2016
标准,否则不支持TCP
,在协议版本中选择GB/T28181-2016
。 - 必须是
TCP
协议,不支持UDP
,在传输协议中选择TCP
,并使用GB-2016
标准。
摄像头注册后,SRS会自动邀请摄像头推流,可以打开下面的链接播放:
- http://localhost:8080/live/34020000001320000001.flv
- http://localhost:8080/live/34020000001320000001.m3u8
- webrtc://localhost/live/34020000001320000001
Note: 请把流名称换成你的设备名称,然后点播放。
Candidate
如果服务器IP不是内网IP,比如部署在公网,则SRS无法获取自己的出口IP,需要配置:
stream_caster {
enabled on;
caster gb28181;
listen 9000;
sip {
enabled on;
listen 5060;
candidate a.b.c.d;
}
}
Note: 请将
stream_caster.sip.candidate
换成摄像头能访问到你的服务器的IP,不管是内网还是公网IP,摄像头 能访问到就可以。
GB的Candidate定义和WebRTC: Candidate概念上一致,都是需要暴露一个客户端能访问的IP地址,在SDP中传递给客户端。比如:
- 在SRS配置中设置
stream_caster.sip.candidate
,SRS启动会读取这个配置,比如192.168.1.100
。 - GB设备通过SIP注册到SRS,SRS发起INVITE消息,消息的Body就是SDP,SDP会指定这个IP地址,比如
IN IP4 192.168.1.100
。 - GB设备连接这个IP地址
tcp://192.168.1.100:9000
,并发起媒体请求。
Note: 媒体的端口是配置在
stream_caster.listen
中的,目前只支持TCP端口。
这个CANDIDATE
就是媒体服务器的IP,它和SIP的服务器地址可以是不同的,SIP服务器地址是在Usage中配置在客户端的。
Note: 由于GB的SIP协议,在REGISTER时To字段并没有带服务器的地址,所以导致服务器无法从SIP中发现自己的地址,只能依靠服务器配置。
当然,如果网卡配置了客户端可以访问的地址,可以把CANDIDATE
配置为*
,让SRS自己发现。
Latency
与普遍认知相反,安全摄像头并非天生就是低延迟系统。它们的主要目的是长期存储。在平移-倾斜-缩放(PTZ)场景中,可能存在一定的延迟要求, 但这并不是我们通常所说的低延迟。PTZ摄像头的延迟通常在1秒左右,这被认为是可以接受的,而实时通信(RTC)中的低延迟通常在200ms左右。
延迟不仅与服务器有关,还涉及到整个链路。关于详细的延迟优化,请参考Low Latency。 我使用默认配置测试了海康威视摄像头的延迟,如下所示:
- 流 类型:
子流
- 视频类型:
复合流
- 分辨率:
640x480
- 码率类型:
可变码率
- 图像质量:
中等
- 视频帧率:
25
fps - 码率限制:
1024
Kbps - 视频编码:
H.264
- 编码复杂度:
中等
- I帧间隔:
50
- SVC:
禁用
- 平台接入协议:
GB/T28181-2016
- 平台传输协议:
TCP
,即通过GB/TCP将流推送到SRS - 观看:WebRTC(UDP)
- SRS版本:
v5.0.100
- 摄像头和服务器:内网
- 延迟:358ms
Features
目前SRS支持的GB的功能清单:
- 摄像头通过SIP注册。srs-gb28181支持。SRS 5.0 支持。
- 自动邀请摄像头推流。srs-gb28181支持。SRS 5.0 支持。
- GB/2016转RTMP协议。srs-gb28181支持。SRS 5.0 支持。
- 基于TCP的SIP信令。srs-gb28181支持。SRS 5.0 支持。
- TCP单端口传输媒体。srs-gb28181支持。SRS 5.0 支持。
目前还没有支持的GB功能:
- 基于UDP的SIP信令。srs-gb28181支持。SRS 5.0 不支持。
- UDP单端口传输媒体。srs-gb28181支持。SRS 5.0 不支持。
- GB/2011转RTMP协议。srs-gb28181支持。SRS 5.0 不支持。
- UDP/TCP多端口传输媒体。srs-gb28181支持。SRS 5.0 不支持。
- HTTP API查询GB流。srs-gb28181支持。SRS 5.0 不支持。
- HTTP API云台摄像头。srs-gb28181支持。SRS 5.0 不支持。
- Web管理页面。srs-gb28181支持。SRS 5.0 不支持。
- GB下级服务器。srs-gb28181不支持。SRS 5.0 不支持。
- GB语音对讲。srs-gb28181不支持。SRS 5.0 不支持。
- GB回看。srs-gb28181不支持。SRS 5.0 不支持。
- GB加密传输。srs-gb28181不支持。SRS 5.0 不支持。
希望大家降低期望,GB的坑太难填了,希望不要期待SRS能做多好。
Protocols
GB相关的协议如下:
- RFC3261: SIP: Session Initiation Protocol
- RFC4566: SDP: Session Description Protocol
- RFC4571: RTP & RTCP over Connection-Oriented Transport
- GB28181-2016: 公共安全视频监控联网系统信息传输、交换、控制技术要求
- ISO13818-1-2000: MPEGPS(Program Stream), PS媒体流规范。
External SIP
Note: SRS 6.0.144+支持外部SIP,若需要使用这个功能,请升级到这个版本。
目前SRS内置的SIP服务器仅实现了简单的Register
、Invite
指令,而要实现GB/T-28181的全部功能,势必会引入复杂的上层业务逻辑。
因此,我们开发了一个独立的外置的SIP服务器。而SRS,只需开放几个简单的API接口,这样既保证了其媒体转发服务器的单一属性,
又兼顾了与第三方SIP信令服务器对接的需求。
播放器请求SRS-SIP,SRS-SIP向SRS Server申请媒体端口,然后邀请GB28181 Device设备推流。设备推流到SRS后,播放器直接从SRS播放流。 下面是几个组件的关系图,详细的交互时序图参考srs-sip
+-------------API/Media--------------------+
| |
+----------+ +------------+ +------+------+ +----------------+
| Player +--API--+ SRS-SIP +---API--+ SRS Server +----Media----+ GB28181 Device +
+----------+ +-----+------+ +-------------+ +-------+--------+
| |
+------------------SIP-------------------------+
Note: 暂时没有实现鉴权功能,敬请期待。
摄像头上面的配置方法同上,仅需将SIP服务器地址从SRS改成SRS-SIP。
首先启动SRS,请确认版本为6.0.144+
,使用配置conf/gb28181-without-sip.conf
,参考Usage。
./objs/srs -c conf/gb28181-without-sip.conf
然后启动SRS-SIP,参考srs-sip。
./bin/srs-sip -sip-port 5060 -media-addr 127.0.0.1:1985 -api-port 2020 -http-server-port 8888
-sip-port
是SIP服务器的端口,默认是5060。GB摄像头和这个SIP服务器通信,完成设备注册等能力。-media-addr
是SRS的媒体服务器地址,SIP服务器返回这个地址给GB摄像头,GB摄像头推流到这个地址。-api-port
是SIP服务器的API端口,默认是2020。这个API是给Player和用户使用的,比如查询设备列表、要求摄像头推流等。-http-server-port
是SIP服务器的Web端口,默认是8888。这个HTTP服务器是提供网页的web服务器,用户通过网页访问摄像头。
启动GB28181设备,将SIP服务器地址改成SRS-SIP的地址,端口为5060。
现在,可以通过SRS-SIP内置的网页播放器测试 http://localhost:8888
SIP Parser
SRS本身也内嵌了一个简单的SIP服务器,支持部分SIP协 议的解析;不过C++没有特别好的SIP库,这也是之前SIP处理不稳定的一个原因。
调研发现,SIP协议和HTTP协议结构非常一致,因此SRS采用http-parser解析SIP,这个库是nodejs维护的,之前好像是NGINX中扒出来的,所以稳定性还是非常高的。
当然用HTTP解析SIP,需要有些修改,主要是以下修改:
- Method:需要新增几个方法,比如
REGISTER
、INVITE
、ACK
、MESSAGE
和BYE
,这是GB常用的几个消息。 - RequestLine:解析path时需要修改,SIP是
sip:xxx
格式,会被认为是HTTP完整URL格式导致解析失败。 - ResponseLine:生成Response时需要修改,主要是协议头,从
HTTP/1.1
改成SIP.2.0
。
基本上改变非常小,所以协议稳定性是可以保障,可以算是解决了一个难题。
SIP和HTTP不同的是,在同一个TCP通道中,并不一定就是一个Request对应一个Response,比如INVITE之后,可能会有100和200两个响应,而SRS也不固定就是Server,也有可能是Client。而这些情况,http-parser可以设置为BOTH方式,这样可以解析出Request和Response:
SrsHttpParser* parser = new SrsHttpParser();
SrsAutoFree(SrsHttpParser, parser);
// We might get SIP request or response message.
if ((err = parser->initialize(HTTP_BOTH)) != srs_success) {
return srs_error_wrap(err, "init parser");
}
Note: 从HTTP消息来看,并没有规定只能一个Request对应一个Response,因此这个也不会带来额外问题。
在实际解析中,发现有时候发送的头有空格,比如:
Content-Length: 142\r\n
这实际上是符合规范的,但如果手动解析可能会有问题,而HTTP-Parser 能正确处理这种情况。
REGISTER
GB的注册流程:
- 在设备设置好SIP服务器为SRS。
- 设备发送SIP格式的REGISTER消息。
- SRS回应200/OK,注册成功。
GB的心跳:
- 设备后面会不断发送MESSAGE作为心跳消息。
- SRS回应200/OK,心跳成功。
SRS若重启后,由于没有保存任何状态,所以收到的可能是设备的MESSAGE消息,而没有REGISTER消息,所以希望设备能重新注册。向各位同学以及SIP和GB的专家请教后,重新注册的可能方法包括:
- 不回应MESSAGE消息,一般3次心跳超时(在设备配置上有设置)。验证发现,海康设备心跳周期默认60秒,所以大概在3分钟左右会重新注册。
- 对MESSAGE回应403或者其他消息。验证发现,效果和不回应一样,设备并不会特别处理。
- 给设备发送重启指令,参考
A.2.3 控制命令
,远程启动是<TeleBoot>Boot</TeleBoot>
,尝试重启设备后会重新注册。这个还没验证。 - 回应REGISTER消息时,将EXPIRE设置短一些,缩短注册的间隔,比如改成30秒。验证发现,尽管设置为30秒,但还是会在一个心跳时间才会重新注册,也就是60秒。
- 在设备的配置上,将心跳周期改短一些,默认60秒,最小是5秒,这样超时会更快。验证发现,心跳改5秒,最短可以26秒左右重新注册。
- 加一层SIP Proxy,让Proxy来保存相关的信息,将状态转移到Proxy。这个方案应该可行,不过SRS不太合适,引入额外组件会让开源很复杂,大家自己的实现中可以尝试。
- 重启前发消息。这个方案在SRS Gracefully Quit时有效,但有时候会
kill -9
或者系统OOM,不会给程序机会清理,所以这个不能适应所有场景。不过在主动升级时,一般会用Gracefully Quit ,这时可以有机会处理这个问题,大家可以尝试。
总之,是没有特别可靠的办法能让摄像头立刻重新注册,SRS必须在逻辑上处理这个问题:SRS启动或重启后,摄像头还在已经注册,甚至在传输流的状态。
Note: 由于很多问题都是持续长时间运行,而系统的某一方重启了,导致状态不一致,引起各种问题。因此,在SRS重启或者启动时,若发现有摄像头是在注册或传输流的状态,那么应该尝试让摄像头重新走一次流程,比如重新注册和重新推流,这样让双方的状态一致,可靠性会更高。
Note: 验证发现,重新注册,对正在传输的媒体流不影响。设备会探测端口可达性,如果TCP断开,或者UDP端口不可达,则会停止流传输。
TCP or UDP
在使用TCP或UDP协议上,我们选择先支持TCP协议,包括SIP信令和PS媒体。
根据SIP协议的规定,TCP是必须要支持的,也是RFC3261比RFC2543一个重要的更新,参考RFC3261: Transport。
至于媒体协议,GB由于使用了PS格式,其实PS一般是用于存储格式,而TS是网络传输格式,或者说TS考虑了更多的网络传输问题,而PS则更多假设像磁盘读写文件一样可靠,因此,PS基于TCP传输也会更加简单。
GB 2016中对于TCP的描述在附录L
,即基于TCP协议的视音频媒体传输
:
实时视频点播、历史视频回放与下载的TCP媒体传输应支持基于RTP封装的视音频PS流,封装格式参照IETF RFC 4571。
在实际应用中,大部分也是使用TCP,而不是用UDP,特别在公网上UDP会有丢包,而GB没有设计重传或FEC。使用TCP的好处:
- UDP由于无状态,在服务器重启时,设备感知 不到服务重启,可能还能继续传输数据,导致两边状态不同步,长久持续这样可能会导致问题,比如设备会提示请求超过上限。
- GB的信令和媒体分离,如果使用TCP则可以很好的同步状态,比如信令可用媒体不可用或断开,媒体可用信令不可用,这些最终都反应到连接的断开。具体请参考Protocol Notes。
- 服务器重启后,可以使用缩短REGISTER的Expires,缩短心跳间隔,让设备重新注册,重新进入推流状态。服务器重启后,设备可以快速感知到媒体链路断开。
- 传输过程中,若出现网络抖动导致链接断开,服务器和设备都可以很快感知到,进入异常处理流程。
因此,SRS先支持TCP,而不支持UDP。也就是先支持GB28181 2016,而不是支持GB28181 2011。
Note: 需要显式开启GB28181-2016,并开启TCP协议才可以。
Protocol Notes
SIP协议上特别需要注意的地方:
- Via的branch必须是
z9hG4bK
开头,参考Via的说明。 - INVITE的200(OK)的ACK消息,ACK的Via的branch必须是新的,ACK并不是INVITE的transaction,参考Via和Example。
- INVITE的Contact是自己的地址,而不是GB设备的,也就是Contact应该由From生成而不是To,参考Contact和Example。
- INVITE的Subject,定义为
媒体流发送者ID:发送方媒体流序列号,媒体流接收者ID:接收方媒体流序列号
,参考附录K。对于s=Play
实时观看的场景,接收方媒体流序列号(SSRC)其实没有定义;根据各位同学反馈,一般这个字段填0。
SDP协议上特别注意的地方:
- y字段: 为十进制整数字符串, 表示SSRC值。格式如下:
dddddddddd
。其中, 第1位为历史或实时媒体流的标识位, 0为实时, 1为历史;第2位至第6位取20位SIP监控域ID之中的4到8位作为域标识, 例如13010000002000000001
中取数字10000
; 第7位至第10位作为域内媒体流标识, 是一个与当前域内产生的媒体流SSRC值后4位不重复的四位十进制整数。
Note: SDP中的
y=
字段,是GB扩展的字段,在WebRTC中是用a=ssrc:xxxx
表达的SSRC。
信令和媒体配合:
- 信令注册、INVITE、TRYING、200、ACK后,媒体开始传输。参考 gb-media-ps-normal.pcapng.zip
- 媒体正常传输过程中,信令重新注册,不影响媒体,继续正常传输。 参考 gb-media-ps-sip-register-loop.pcapng.zip
- 信令正常完成INVITE,媒体TCP端口若不打开,设备尝试连接一次后放弃。参考 gb-media-disabled-sip-ok.pcapng.zip
- 媒体正常传输过程中,信令断开,一定时间后,媒体断开。参考 gb-media-ps-sip-disconnect.pcapng.zip
- 媒体正常传输过程中,TCP连接断开,客户端不会重试。参考 gb-media-disconnect-sip-ok.pcapng.zip