Gb28181解析

2022-03-02
18分钟阅读时长

国标GB28181(以下简称国标或者gb)文档分析的文档我去年其实写过一版,但是由于原来的电脑炸了,文档还没来得及传上去就丢了(痛苦),所以还要重写一版做笔记。

国标目前有两个版本,2011和2016,以下以16版为准。16版本相比11版本,主要是增加了TCP的支持,11版是全部基于UDP传输的。

协议结构

截屏2022-03-02 上午10.44.52

上图是直接从文档中截取的通信协议结构图。国标基于TCP/IP网络,主要分为两个传输通道,即一般会话通道和流媒体传输通道。前者基于SIP协议(图中MANSCDPMANSRTSP是国标自行扩展的协议,仍然是基于SIP协议的),传输载体是SDP格式字符串;后者基于RTP/RTCP协议,传输载体主要是PS视频流和AAC等格式的音频数据。

SIP协议

SIP协议初衷就是用于音视频通话,VOIP等技术大量用到该协议。

RFC3261规定了SIP协议的基础内容,包括Register, Invite等一系列注册和通信机制。

SIP协议某些设计非常类似HTTP1.1,比如消息格式,method、header的设计等。但是SIP协议远比HTTP要复杂的多:

  1. SIP协议的传输层是可选的,可以基于TCP/UDP/Websocket等进行通信;在UDP这种不可靠网络上的通信,需要考虑的问题要复杂很多;
  2. 相比于HTTP的请求-应答模型,SIP是基于事务进行通信的,整个流程比较复杂;
  3. 相比于HTTP的option/get/post/put/patch/delete/head等有限几个方法,SIP的方法要多得多。除了rfc3261规定的,还有一系列扩展协议(如info, publish, notify, subscribe等);
  4. 对于HTTP而言,代理基本是透明的,只负责转发,相当于SIP的无状态代理;SIP的有状态代理工作机制比较复杂;

基础概念

  • UAC: 在事务中充当请求客户端的一方;
  • UAS:在事务中充当请求服务端的一方;
  • UA:同时充当UAC和UAS的逻辑实体;

在实际运行中,一般节点都既是客户端又是服务器。谈到UAC和UAS时指的往往是在某个事务中的角色,比如INVITE时,呼叫方是UAC,接收方是UAS;但是在BYE的时候,就可能反过来了。

Call

一次呼叫由发起方和多个接收方组成。唯一标识是Call-ID,Call由多个Dialog组成。

Call强调的是终端用户的对话,实际请求中,Call会经过很多代理服务器。

对话(Dialog)

 SIP协议Session Model

主叫方A呼叫被叫方B:

  • 步骤1:主叫方A发送INVITE请求到代理服务器
  • 步骤2:代理服务器发送100 Trying 响应主叫方A
  • 步骤3~6:代理服务器搜索被叫方B的地址,获取地址后转发INVITE请求;
  • 步骤7~9:被叫方B生成的180 振铃响应,返回给主叫方A
  • 步骤10~12:被叫方B生成的200 OK响应,返回给主叫方A
  • 步骤13~17:主叫方A收到被叫方B200 OK响应后,向被叫方B发送一个ACK,会话建立;
  • 步骤18~20:会话结束后,任何参与者(A或B)都可以发送一个BYE请求来终止会话;
  • 步骤21~23:主叫方A发送200 OK响应来确认BYE,会话终止。

以上的整个流程称之为一个Dialog,其中流媒体传输的部分(媒体通道)称为Session。RFC3261里面仅定义了invite可以建立对话,在RFC 3265/6665里面额外添加了Subscribe方法创建对话的机制。Info消息可以对对话进行设置,Bye消息可以结束对话,这些都是对话内消息。ACK属于非对话内非会话消息,直接发出即可,也没有Response。

对话其实比较容易理解,Message请求是单次请求-应答机制(类似HTTP),无法定义持续性行为。订阅就是典型的持续性行为:在取消订阅之前被订阅端需要持续NOTIFY新的事件,这就需要一个机制来定义这次订阅和相关的响应之间的关联关系,这就是对话。

按着rfc3261的规定,当UAC发出INVITE请求,收到的2xx和1xx(除了100之外)的响应中To字段包含tag时,视为对话建立。Call-ID, From tagTo tag,共同构成本次会话的唯一ID,即Dialog-ID. 一次呼叫中可能存在多个对话,因此对话中的消息,如BYE, ACK显然需要明确的Dialog-ID才能明确操作的对象。

对话外的SIP消息最初发出的时候,发送端(UAC)会在From后面生成tag,但是To后面没有,消息接收端(UAS)会在回复的时候在To后面加上tag。对话中的所有消息同时携带Dialog-ID的所有组成元素,同一个对话内的消息这些元素必须保持不变。

举例来说,对应于国标场景,前端通过HTTP请求一次打开多个IPC的某个时间段的历史流,UAC发送这些请求时可以共用同一个call-ID,但是每个请求的from-tag不一致,各相机返回的to-tag肯定也不一致,这就建立了多个Dialog。你可以在不结束这次请求的情况下,单独关掉或者暂停某个摄像头的流,而不影响其他摄像头继续播放。一个简单的设计是call-id使用请求的ReqId(前端请求时生成这个uuid),from tag使用设备id,设备端生成to-tag. 而流本身的标记可以用DialogID或其hash值。由于浏览器可以直接关掉,所以服务端需要设计流保活机制,当心跳停止后自动关闭Dialog.

对话中的CSeq头用于保证消息的顺序性,在同一个Dialog中标识及排序事务(transaction)以及区分新的请求与请求的重发。对于ACK而言,CSeq的序号必须与其所对应的request相同。对于CANCEL而言,CSeq的序号也必须与其cancel掉的request相同。其他情况下,Dialog里面每个新的Request,Cseq+1。需要注意的是:在同一个对话中的UAC和UAS分别维护自己的CSeq序号,他们发出请求的CSeq序号是不相关的。

对话外消息

除了Invite/Subscribe消息建立的对话外,其他消息都是对话外消息。比如Message, Register, Options等。这种消息类似HTTP的单次请求应答机制:

  • 步骤1:User1发送MESSAGE请求到代理服务器
  • 步骤2:代理服务器转发User1的MESSAGE请求给USER2
  • 步骤3:User2收到User1的消息后,回复200 OK给代理服务器
  • 步骤7~9:代理服务器转发200 OK回复给User1

显然这个流程比较简单。对话外消息的Call-IDFrom-Tag一般是随机生成的,且即用即抛。

对话外消息的CSeq值可以随意使用,一般多次发送同一个请求会递增CSeq。响应的大部分header必须和请求一致(From/Call-ID/CSeq/Via/To),如果请求的To没有tag,响应需要生成tag,并保证多次响应的tag一致(参考RFC3261 8.2.6节)。这个设计主要是为了请求可能有多次响应的场景,以及后续需要建立对话的需求,但是标准并没有明确的说明如何生成这个To-Tag.

简单总结一下就是UAS如果发现To不带Tag,就要生成一个Tag,并且对于同样的call-id+from-tag,应该始终使用相同的to-tag。不过对于一次性的请求,这个To tag可以即用即抛。考虑视频/语音会议的场景,当一个人呼叫你的微信时,PC端微信和手机端微信都会收到呼叫,此时两者收到的INVITE拥有同样的call-ID和同样的From-Tag,但是PC端和手机端可以生成不同的To-Tag,这样就建立了多个对话(当然,实际上微信会直接挂掉另外一个终端)。

Transaction

事务=请求(UAC)+最终响应(相邻的UAS),SIP基于事务。所谓相邻就是说transaction存在于相邻的SIP实体,而不是存在于两个UA之间。

一个事务中包含一个请求消息、0个或多个临时响应消息、1个或多个最终响应消息(2xx~6xx)。事务的区分通过Via字段栈顶的Branch的值来确定。对于请求消息,每经过一个有事务状态的Proxy的时候,该Proxy需要为这个事务创建一个服务器端事务和一个客户端事务,并且将自己的URI添加到Via的栈顶,并生成一个Global ID做为Branch的值,以此值来表示一个与之相对应的事务。SIP在事务层面定义了状态机和定时器来实现重传

事务中的请求是 INVITE(称为 INVITE 事务)时,只有最终响应不是 2xx 响应时这个事务才包括 ACK。如果最终响应是 2xx,那么不应将 ACK 视为事务的一部分。这是因为200的ACK是比较重要的响应,由UAS负责重传,所以可以视为一个单独的事务。

从事务的定义可以看出,事务发生于一个请求和一组应答之间。所以事务是SIP核心层处理的,一般在业务层无须关心,当然如果手动生成响应的话,记得把request的branch copy过来。

消息体格式

类似HTTP,SIP消息分为三个部分:

  • 请求行(request-line) or 状态行(status-line)
  • 消息头(header)
  • 正文(body)

请求行

请求行格式:Method Request-URI SIP-Version CRLF 请求行举例:INVITE sip:bob@zte.com SIP/2.0 /r/n

Method分为:

Method 方法说明
REGISTER 注册联系信息
INVITE 发起会话请求
ACK INVITE 请求的响应的确认
CANCEL 取消请求
BYE 终结会话
OPTIONS 查询服务器能力
MESSAGE RFC3428,上文已有描述

状态行

状态行格式: SIP-Version Status-Code Reason-Phrase CRLF 状态行举例:SIP/2.0 200 OK /r/n

状态码的设计也和HTTP非常类似:

状态码 含义
1xx: 临时响应、表示请求消息正在被处理
2xx 成功响应、表示请求已被成功接收完全理解并接收
3xx 重定向响应、表示需采取进一步完成请求
4xx 客户机错误、表示请求消息中包含语法错误信息或服务器无法完成客户机的请求
5xx 服务器错误、表示服务器无法合法完成请求
6xx 全局故障 、表示任何服务器都无法完成该请求

常用的状态码举例:

状态码 msg 含义
100 Trying 试呼叫
180 Ringing 振铃
181 Call is Being Forwarded 呼叫正在前转
200 OK 成功响应
302 Moved Temporarily 临时迁移
400 Bad Request 错误请求
401 Unauthorized 未授权
403 Forbidden 禁止
404 Not Found 用户不存在
408 Request Timeout 请求超时
480 Temporarily Unavailable 暂时无人接听
486 Busy Here 线路忙
504 Server Time-out 服务器超时
600 Busy Everywhere 全忙

可以说和HTTP的状态码含义非常接近。

中间响应消息1xx的使用则是为了节省网络开销设计的,一旦 UC 收到任何一个中间响应消息,则UC必须停止消息重发定时器,不再从发这个请求消息,反之则直到收到最终响应消息或重发定时器超时。

格式和HTTP的header也保持一致,典型的k:v结构。常用header包括:

Header 含义说明 举例
Call-ID 由本地设备(Client)生成,全局唯一,每次呼叫这个值唯一不变。 Call-ID: asd88asd77a@1.2.3.4
From 表示请求的发起者 From: sip:user1@domain.com;tag=49583
To 表示请求的接收者 To: sip:user2@domain.com
Via Via头域是被服务器插入request中,用来检查路由环的,并且可以使response根据via找到返回的路 Via: SIP/2.0/TCP user1pc.domain.com;branch=z9hG4bK776sgdkse
Max-Forwards 用于表示这个包最多可以传送多少跳,每经过一跳都会减一,当Max-Forwards==0系统会返回483。默认为70 Max-Forwards: 70
Contact 包含源的URI信息,用来给响应方直接和源建立连接用 Contact: sip:192.168.100.1:1111
Content-Type 指明消息体的类型 Content-Type: text/plain;Content-Type: application/sdp; Content-Type: application/cpim;
Content-Length 指明消息体的字节大小 Content-Length: 18
Authorization 鉴权凭证消息 Authorization: Digest username=“01062237493”, realm=“192.168.2.89”, qop=auth, algorithm=MD5, uri=“sip:192.168.2.89”, nonce=“e17d377c3d2d9c343e26576a7fd04738481dfc10”, nc=00000001, cnonce=“12660455546344082314666316435946”, response=“f57e47ce03162293b9ced07362ce2b79”

MANSCDP

在国标附录A里面有描述,主要是一系列对设备的查询、控制等指令,并规定了请求/应答的数据结构。

MANSCDP使用xml进行通信,作为SIP MESSAGE类型消息的负载,Content-Type为Application/MANSCDP+xml

消息大致分为以下几类,名称就是xml的根标签。

类型 名称 说明
Request Control 控制设备指令
Request Query 查询设备信息指令
Request Notify 通知,主要用于设备向云端发送告警。
Response Response 应答

基础数据结构,写代码的话这些就是类或者alias:

截屏2022-03-02 下午2.07.31

再下面是定义的请求/应答格式,这里不再详述。

举个例子,设备远程启动,请求格式:

MESSAGE sip:34020000001320000001@3402000000 SIP/2.0
Call-ID: fac5d9915d7b4b64e46054bbbda3f29f@0.0.0.0
CSeq: 1 MESSAGE
From: <sip:34020000002000000001@3402000000>;tag=58726327_53173353_c2be9be1-4121-42af-b4e2-e4896ab42120
To: <sip:34020000001320000001@3402000000>
Max-Forwards: 70
Content-Type: Application/MANSCDP+xml
Route: <sip:34020000001320000001@192.168.10.177:5061;line=9a1ca01668b5778;lr>
Via: SIP/2.0/UDP 192.168.10.177:5060;branch=z9hG4bKc2be9be1-4121-42af-b4e2-e4896ab42120_53173353_18042259617961
Content-Length: 164
<?xml version="1.0"?>
<Control>
<CmdType>DeviceControl</CmdType>
<SN>17298</SN>
<DeviceID>34020000001320000001</DeviceID>
<TeleBoot>Boot</TeleBoot>
</Control>

应答是先给200,然后异步回应:

MESSAGE sip:34020000001320000001@192.168.10.177 SIP/2.0
Via: SIP/2.0/UDP 192.168.10.177:5061;rport=5061;branch=z9hG4bK3569164341;received=192.168.10.177
From: <sip:192.168.10.177:5060>;tag=3889968117
To: <sip:34020000001320000001@192.168.10.177>
Call-ID: 2825372510
CSeq: 20 MESSAGE
Content-Type: Application/MANSCDP+xml
Max-Forwards: 70
User-Agent: eXosip/3.6.0
Content-Length: 162
<?xml version="1.0"?>
<Response>
<CmdType>DeviceControl</CmdType>
<SN>17298</SN>
<DeviceID>34020000001320000001</DeviceID>
<Result>OK</Result>
</Response>

服务端收到响应之后仍然会回复一个200.

MANSRTSP

类似的,国标扩展了SIP协议以支持媒体控制。MANSRTSP采用RTSP协议(RTC2326)规定的格式,所以算是缝合和SIP和RTSP。Method使用INFOContent-Type设为 Application/MANSRTSP.

示例格式:

message=start-line message header CRLF [message body]

四个部分:起始行,消息头,分隔行,消息体。

起始行

包括请求和应答两种。

请求格式:Method SP RTSP-Version CRLF,Method包括:PLAY,PAUSE,TEARDOWN,这是RTSP中规定的控制子集,即播放、暂停和停止。

例:

PLAY MANSRTSP/1.0
CSeq:2
Range:npt=now-

Scale表示倍速播放;Pause Time表示暂停位置(now表示当前位置);随机拖放使用Range定位。

Scale至少支持: 0 .25 、0 .5 、1 、2 、4 ,Range表示播放录像起点的相对值,取值范围为0到播放录像的终点时间,参数以s为单位。

应答格式:Status-Line = RTSP-Version SP Status-Code SP Reason-PhraseCRLF.

例:RTSP/1.0 200 OK

请求和应答的CSeq应该相等。

RTP协议

类似RTSP协议栈,国标的媒体传输通过RTP(RFC 3550)进行,并辅以RTCP控制(RFC 3550)。国标允许RTP传输基于PS封装的视音频数据或视音频基本流数据。即PS流或者H264/H265裸流。

需要注意的是2016版本的流封装里面还是没有H.265相关内容的,只有MPEG-4/H.264/SVAC这三种格式支持。

RTP的流媒体封装参考RFC即可,这里不再赘述。

编码标准

国标对各类设备的编码做了统一规定,需要保证全局唯一性。一般设备的国标编码是留空的,需要使用者自己设计。

SIP的URI格式是sip[s]:username@domain;uri-parameters,名称一般就是国标编码,注意服务器一般也是使用国标编码命名的。RFC3261中对该格式进行详细规定。

domain一般就是ip+port,也可以使用域名,推荐端口是5060.

特别注意:国标规定SIP信令字符集是GB2312,而不是UTF-8.

通信流程

首先是设备注册(SIP register),如果设备或系统注册不成功,宜延迟一定的随机时间后重新注册。设备注册完成后设备与服务端之间就会建立长连接。注册完毕后设备会自动与服务器进行NTP对时,后续操作即可由服务端主动发起。

注册完成后,服务端即可主动发起请求。主要包括:

  • 音视频实时点播(直播)
  • 设备控制(PTZ、录像、报警设备的布防/撤防)
  • 设备信息查询(包括设备ID、设备名、设备厂家名称、设备型号、设备地址、设备口令、设备类型、设备状态、设备安装地址、设备归属单位、父设备ID等信息)
  • 历史音视频数据检索、回放、下载
  • 语音广播、对讲

设备主动报送的信息包括:

  • 告警信息
  • 设备状态

注册/注销

设备注册/注销都要进行认证,支持口令和数字证书两种安全级别的认证。

在高安全级别下,使用sip+TLS进行加密传输。

SIP本身还要通过数字摘要做信令认证,算法如下:

在SIP的header中增加Date字段和Note字段,Note= (Digestnonce="",algorithm = ),算法包括MD5、SHA-1、SHA-256等常见算法。nonce 的 值 为 algorithm [From + to + CallId + Date+seed+ 消息体 ]数字摘要经过BASE64编码后的值,algorithm 的值为数字摘要的算法名称,“+”为字符串连接运算。Date应在校时精度范围之内,精度为秒,偏离值一般不超过10分钟。

具体的算法流程这里不再列出,就是常见的保证数据完整性的验证。

注册类似DHCP,是有时限的(默认1d,可以最短调到1h),到期之前需要设备自行重新注册。注册成功后设备就显示在线了。

基本注册(口令注册)流程如下:

  1. SIP代理向SIP服务器发送Register请求;
  2. SIP服务器向SIP代理发送响应401,并在响应的消息头WWW_Authenticate字段中给出适合SIP代理的认证体制和参数;
  3. SIP代理重新向SIP服务器发送Register请求,在请求的Authorization字段给出信任书,包含认证信息;
  4. SIP服务器对请求进行验证,如果检查出SIP代理身份合法,向SIP代理发送成功响应200 OK,如果身份不合法则发送拒绝服务应答;

基于数字证书的认证流程会复杂许多,这里不再详述。

注销和注册的流程其实类似,都用Register方法,只是Expires设为0.

直播

服务端Invite之后,设备才开始推流,所以可以实现按需取流。

平台、设备媒体流保活机制规定如下:

  1. 链路建立后,码流经过的各级平台应具备媒体流丢失监测能力,若监测到媒体流丢失,应释放该条媒体链路,并通过会话内Bye消息通知上下级平台;

  2. 上下级平台之间、平台与设备之间、平台与客户端之间应通过注册、状态信息报送等进行状态监测,若监测到媒体流接收方或媒体流发送方故障或离线,应主动释放媒体链路,停止媒体流的发送;

  3. 通过Subject标识进行已发送流的清理判断。

    • 上级平台向下级平台、平台向设备发送呼叫请求时,应携带Subject头域,Subject头域的“媒体流发送者ID:发送方媒体流序列号”用于对媒体源标识,此标识与请求的码流具有对应关系。

    • 下级平台、设备在接收到呼叫请求后,应判断是否在发送以此媒体源标识的码流,若已经在发送,则应释放现有媒体流发送链路并按照请求建立新的媒体流发送链路。

点播流程可以由最终收流观看的客户端主动发起,也可以由SIP信令服务器自行发起。由于BS架构下,浏览器不支持RTP流直播,所以实际使用中。一般Invite请求是由SIP服务器发出,流推送到流媒体服务器之后,需要转协议才能给客户端进行观看。

设备控制

参考上述MANSCDP部分,通过MESSAGE消息实现。

部分命令不需要应答操作结果,只回答一个200 OK表示已经收到。包括:

  • 相机、云台控制(PTZ)
  • 远程启动
  • 强制关键帧(IDR)
  • 拉框放大/缩小

部分命令需要有明确的Response,包括:

  • 录像控制
  • 报警布防/撤防
  • 报警复位
  • 看守位控制
  • 设备配置命令

注意,无论何种命令,对端都要有一个应答表示已收到(因为通信可能基于UDP)

报警事件

同样基于MANSCDP扩展协议。

按着标准规定,报警事件需要服务端进行处理结果响应。

设备查询

标准提供了灵活的设备组织形式,允许按系统编码(级联时),行政区划,业务分组、虚拟组织等组织方式进行设备目录查询。设备目录查询的响应数据包括:命令类型(CmdType)、命令序列号(SN)、设备/区域/系统编码(De-viceID)、设备/区域/系统名称(Name)、设备状态(Status)、经度(Longitude)、纬度(Latitude)等。注意这里的SN不是设备序列号,而是命令序列号;用来异步映射请求和响应。

得到设备目录之后,可以对某个具体的设备进行消息查询。响应内容主要包括:命令类型(CmdType)、设备编码(DeviceID)、设备名称(DeviceName)、查询结果标志(Result)、厂商信息(Manufacturer)、设备型号(Model)、固件版本(Firm-ware)、最大支持摄像机个数(Channel)等。

设备状态可以单独进行查询,可以单独获取设备的状态标志位,包括:目标设备或区域或系统编码、命令类型(CmdType)、查询结果标志(Result)、是否在线(Online)、是否正常工作(Status)、不正常原因(Reason)、是否编码(Encode)、是否录像(Record)、设备时间和日期(DeviceTime)、报警设备状态列表(Alarmstatus)等,报警设备状态列表应包括报警设备或区域或系统编码(DeviceID)、报警设备状态(DutyStatus)等。

设备配置信息也可以单独查询,这里没有严格规定返回值内容。

设备预置位(云台)也可以单独查询,响应结果主要包括预置位编码(PresetID)、预置位名称(Pr-esetName)。

状态报送

类似报警事件,如果设备(网关、SIP设备、SIP客户端或联网系统)的状态异常,需要立即向SIP监控域的SIP服务器发送状态信息。即使无异常,也需要定时上报,即状态心跳(默认时间60s)。连续心跳丢失(默认超过3次)之后认为设备离线。

音视频文件检索

即回放检索功能,其检索条件包括区域、设备、录像时间段、录像地点、录像内容等。

响应内容可以分页返回,为了保证可靠,需要进行串行发送。即收到上一条的ack再进行下一页返回,服务端可以批量进行ack。

历史音视频回放

类似直播,也是Invite发起。不同的是,必须支持快进、暂停功能,相关控制见MANSRTSP部分。

媒体播放完毕之后,发送端需要使用MESSAGE消息通知服务器发送已经结束。如果服务端主动不看了,需要发送BYE通知设备端停止推流。

媒体下载

该功能类似回放,但是需要响应端给出文件大小相关参数方便计算下载进度。如果无法计算出大小,则需要根据码流中的时间来计算。

下载可以指定倍速,下载一旦开始就不能修改速度等参数,也不能暂停。

对时

如上所述,对时是在注册的时候完成的。为了避免时钟偏移,注册过期时间不宜过长。

订阅和发布

事件订阅包括报警事件、移动设备位置通知事件等。

设备目录订阅(对NVR)可以及时获取设备变更通知。

语音广播和语音对讲

这里假设终端设备(如IPC)具有语音广播能力,则可以利用gb协议进行双向语音广播。

Avatar

个人介绍

兴趣使然的程序员,博而不精,乐学不倦