一、Message信令流程
在GB28181规定中,源设备(对我们来说就是sip服务器)向目标设备发送设备控制命令, 控制命令的类型包括球机/云台控制、 远程启动、 录像控制、报警布防/ 撤防、 报警复位、 强制关键帧、 拉框放大、 拉框缩小、 看守位控制、 设备配置等, 设备控制采用IETFRFC3428 中的Message 方法实现。
命令流程分为:
1.无应答命令流程
命令流程描述如下:
a ) 1 : 源设备向SIP 服务器发送设备控制命令, 设备控制命令采用Message 方法携带;
b ) 2 : SIP 服务器收到命令后返回200OK ;
c ) 3 : SIP 服务器向目标设备发送设备控制命令, 设备控制命令采用Message 方法携带;
d ) 4 : 目标设备收到命令后返回200OK 。
2.有应答命令流程
命令流程描述如下:
a ) 1 : 源设备向SIP 服务器发送设备控制命令, 设备控制命令采用Message 方法携带;
b ) 2 : SIP 服务器收到命令后返回200OK ;
c ) 3 : SIP 服务器向目标设备发送设备控制命令, 设备控制命令采用Message 方法携带;
d ) 4 : 目标设备收到命令后返回200OK ;
e ) 5 : 目标设备向SIP 服务器发送设备控制响应命令, 设备控制响应命令采用Message 方法携带;
f ) 6 : SIP 服务器收到命令后返回200OK ;
g ) 7 : SIP 服务器向源设备转发设备控制响应命令, 设备控制响应命令采用Message 方法携带;
h ) 8 : 源设备收到命令后返回200OK 。
看上去挺复杂,但实际上只有3、4、5、6是在和设备端通信的,4是由pjsip自动完成的的,我们只用关心3和5就行。
二、设备目录信息查询命令流程
接下来以设备目录信息查询(Catalog)为例,介绍一下Message信令的收发流程。之所以以Catalog为例进行讲解,是因为在向SIP服务器完成注册后,服务器会立马向设备发送一条Catalog的Message。Catalog是一条有应答命令,放上服务器记录的log,大家可以当做抓包来看,这里的设备端是我搞到的一台支持GB28181的网络摄像头。
3 : SIP 服务器向目标设备发送设备控制命令, 设备控制命令采用Message 方法携带;
MESSAGE sip:34020000002000000113@192.168.31.113 SIP/2.0
Via: SIP/2.0/UDP 192.168.31.103:15060;rport;branch=z9hG4bK792117068
From: <sip:34020000002000000001@3402000000>;tag=622117068
To: <sip:34020000002000000113@192.168.31.113>
Call-ID: 325117068
CSeq: 5 MESSAGE
Content-Type: Application/MANSCDP+xml
Max-Forwards: 70
User-Agent: LiveGBS v201123
Content-Length: 157
<?xml version="1.0" encoding="GB2312"?>
<Query>
<CmdType>Catalog</CmdType>
<SN>354117068</SN>
<DeviceID>34020000002000000113</DeviceID>
</Query>
4 : 目标设备收到命令后返回200 OK ;
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.31.103:15060;rport=15060;received=192.168.31.103;branch=z9hG4bK792117068
Call-ID: 325117068
From: <sip:34020000002000000001@3402000000>;tag=622117068
To: <sip:34020000002000000113@192.168.31.113>;tag=z9hG4bK792117068
CSeq: 5 MESSAGE
Content-Length: 0
5 : 目标设备向SIP 服务器发送设备控制响应命令, 设备控制响应命令采用Message 方法携带;
MESSAGE sip:34020000002000000001@192.168.31.103:15060 SIP/2.0
Via: SIP/2.0/UDP 192.168.31.113:5060;rport;branch=z9hG4bKPj78yEWRJSrGA8PnksHhS2OHW4xuf29LZJ
Max-Forwards: 70
From: <sip:34020000002000000113@192.168.31.113>;tag=aMet8ZHT6HSTG.QdNpNGSTXxklKFuZth
To: <sip:34020000002000000001@192.168.31.103:15060>
Call-ID: gd98PDdY2I4lgYixMWrblWG14b9lRs.N
CSeq: 38630 MESSAGE
Contact: <sip:34020000002000000001@192.168.31.113:5060>
Content-Type: Application/MANSCDP+xml
Content-Length: 802
<?xml version="1.0"?>
<Response>
<CmdType>Catalog</CmdType>
<SN>354117068</SN>
<DeviceID>34020000002000000113</DeviceID>
<SumNum>1</SumNum>
<DeviceList Num="1">
<Item>
<DeviceID>34020000002000000113</DeviceID>
<Name>Camera</Name>
<Manufacturer>IPC_Company</Manufacturer>
<Model>HI351X</Model>
<Owner>General</Owner>
<CivilCode>3402000000</CivilCode>
<Block>General</Block>
<Address>China</Address>
<Parental>0</Parental>
<ParentID>34020000002000000001</ParentID>
<SafetyWay>0</SafetyWay>
<RegisterWay>1</RegisterWay>
<Secrecy>0</Secrecy>
<IPAddress>192.168.31.113</IPAddress>
<Port>5060</Port>
<Status>ON</Status>
<Longitude>0.0</Longitude>
<Latitude>0.0</Latitude>
<Info>
<PTZType>3</PTZType>
<Resolution>6/5/4/2</Resolution>
</Info>
</Item>
</DeviceList>
</Response>
6 : SIP 服务器收到命令后返回200 OK ;
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.31.113:5060;rport=5060;received=192.168.31.113;branch=z9hG4bKPj78yEWRJSrGA8PnksHhS2OHW4xuf29LZJ
From: <sip:34020000002000000113@192.168.31.113>;tag=aMet8ZHT6HSTG.QdNpNGSTXxklKFuZth
To: <sip:34020000002000000001@192.168.31.103:15060>;tag=894117081
CSeq: 38630 MESSAGE
Call-ID: gd98PDdY2I4lgYixMWrblWG14b9lRs.N
User-Agent: LiveGBS v201123
Contact: <sip:34020000002000000001@192.168.31.103:15060>
Content-Length: 0
3、解析和生成xml格式信息
可以看出,message信令的body是xml格式的,为了解析和生成xml信息,我们需要一个xml的库,我用的是mini-xml。安装和使用方法可以参考下面链接。
响应命令里DeviceList 元素的属性对应的就是青柿服务器里通道的个数了。
其他元素的含义在《GBT 28181-2016 公共安全视频监控联网系统信息传输、交换、控制技术要求》的附录A里都有详细的说明。
4、pjsip实现接收和发送Message信令
pjsip接收到message消息时可通过on_pager回调函数读取接收到的内容
函数原型:
https://www.pjsip.org/pjsip/docs/html/structpjsua__callback.htm
通过pjsua_im_send函数发送message消息
函数原型:
https://www.pjsip.org/pjsip/docs/html/group__PJSUA__LIB__BUDDY.htm
使用方法:
//在pjsua_init时修改回调函数
pjsua_config cfg;
pjsua_config_default(&cfg);
cfg.cb.on_pager=&on_pager;
void on_pager (pjsua_call_id call_id, const pj_str_t *from, const pj_str_t *to, const pj_str_t *contact, const pj_str_t *mime_type, const pj_str_t *body)
{
mxml_node_t *tree; //新建xml节点
int space;
tree = mxmlLoadString(NULL,body->ptr,MXML_TEXT_CALLBACK); //从字符串读取xml信息
mxml_node_t *CmdType = mxmlFindElement(tree, tree, "CmdType",NULL, NULL,MXML_DESCEND); //查找CmdType元素
if(strcmp("Catalog",mxmlGetText(CmdType, &space)) == 0)
{
mxml_node_t *xml, *data;
xml = mxmlNewXML("1.0");
mxml_node_t *Response = mxmlNewElement(xml, "Response");
/************
*处理接收到的信息,安装格式要求生成设备目录信息
*************/
#if 1 //通过whitespace_cb回调函数修改输出的格式
char *buff = mxmlSaveAllocString ( xml, whitespace_cb );
#else //不调用回调函数(不插人任何空格,制表符,回车符和换行符)
mxmlSetWrapMargin(0); //控制输出的边距,默认为75以方便阅读
char *buff = mxmlSaveAllocString ( xml, MXML_NO_CALLBACK );
#endif
if(buff==NULL)
perror("mxmlSaveFile err:");
pj_str_t to = pj_str("sip:34020000002000000001@3402000000");
pj_str_t content = pj_str(buff);
pj_str_t type = pj_str("Application/MANSCDP+xml");
//响应catalog请求
int status = pjsua_im_send(acc_id, &to, &type, &content , NULL, NULL);//acc_id为上一章设置SIP用户信息时生成的
if (status != PJ_SUCCESS)
{
PJ_LOG(2, (__FILE__, "发送消息失败 %d",status));
}
free(buff);
mxmlDelete(xml); //释放内存
}
mxmlDelete(tree); //释放内存
}