1、简介
根据《GBT 28181-2016 公共安全视频监控联网系统信息传输、交换、控制技术要求》9.4节的《报警事件通知和分发基本要求》描述:
发生报警事件时, 源设备应将报警信息发送给 SIP 服务器;SIP 服务器接收到报警事件后, 将报警信息分发给目标设备。 报警事件通知和分发使用IETF RFC 3428 中定义的方法 Message 传送报警信息。源设备包括SIP 设备、 网关、SIP 客户端、 联网系统或者综合接处警系统以及卡口系统等, 目标设备包括具有接警功能的SIP 客户端、 联网系统或者综合接处警系统以及卡口系统等。
2、流程
报警事件通知和分发流程见下图:
命令流程描述如下:
- (1): 报警事件产生后, 源设备向SIP 服务器发送报警通知命令, 报警通知命令采用 Message 方法携带;
- (2): SIP 服务器收到命令后返回200 OK;
- (3):SIP 服务器接收到报警事件后, 向源设备发送报警事件通知响应命令, 报警通知响应命令采用 Message 方法携带;
- (4): 源设备收到命令后返回200 OK;
- (5):SIP 服务器接收到报警事件后, 确定需要转发的目标设备,SIP 服务器向目标设备发送报警事件通知命令, 报警通知命令采用 Message 方法携带;
- (6): 目标设备收到命令后返回200 OK;
- (7): 目标设备接收到报警事件后, 向SIP 服务器发送报警事件通知响应命令, 报警通知响应命令采用 Message 方法携带;
- (8):SIP 服务器收到命令后返回200 OK
3、协议接口
-
(1)请求命令消息体
消息头 Content-type 字段为 Content-type: Application/ MANSCDP+xml。
报警事件通知和分发流程中的请求命令采用 MANSCDP 协议格式定义, 详细描述见 A.2.5 报警通知。
源设备向SIP 服务器通知报警、SIP 服务器向目标设备发送报警的通知命令均采用 Message 方法的消息体携 带。 报 警 事 件 通 知 命 令 应 包 括 命 令 类 型 (CmdType) 、 命 令 序 列 号 (SN) 、 设 备 编 码(DeviceID) 、 报警级别(AlarmPriority) 等。 可选项: 报警时间(AlarmTime) 、 报警方式(AlarmMethod) 、经度(Longitude) 、 纬度(Latitude) 、 扩展报警类型(AlarmType) 、 报警类型参数(AlarmTypeParam) 。相关设备在收到 Message 消息后, 应立即返回200 OK 应答,200 OK 应答均无消息体。 -
(2)应答命令消息体
消息头 Content-type 字段为 Content-type: Application/ MANSCDP+xml。
报警事件通知和分发流程中的应答命令采用 MANSCDP 协议格式定义, 详细描述见 A.2.6 报警通知应答。SIP 服务器向源设备、 目标设备向SIP 服务器发送报警通知应答命令均采用 Message方法的消息体携带。报警事件通知应答命令应包括命令类型(CmdType) 、 命令序列号(SN) 、 设备编码(DeviceID) 、执行结果(Result) 。
相关设备在收到 Message 消息后, 应立即返回200 OK 应答,200 OK 应答均无消息体。
4、软件代码
报警发送接口:
int SipSendAlarm(GB28181Param_t *pGB28181Param, AlarmHander_t *pHander, int sn);
int SipSendAlarm(GB28181Param_t *pGB28181Param, AlarmHander_t *pHander, int sn)
{
int ret = 0;
char from[128] = {
0,};
char proxy[128] = {
0,};
char xmlBody[1024] = {
0,};
osip_message_t *rqt_msg = NULL;
// sip还未注册,那么不发送数据
if (!SipGetRegStatus() || !pGB28181Param || !pHander)
{
return -1;
}
if (!IS_ALARM_CHN_VALID(pHander->alarmChn))
{
return -1;
}
snprintf(from, sizeof(from), "sip:%s@%s:%s",
pGB28181Param->userParam.devSipID,
pGB28181Param->userParam.devSipIP,
pGB28181Param->userParam.devSipPort);
snprintf(proxy, sizeof(proxy), "sip:%s@%s:%s",
pGB28181Param->userParam.sipServerID,
pGB28181Param->userParam.sipServerIP,
pGB28181Param->userParam.sipServerPort);
/* 构建"MESSAGE"请求 */
if (eXosip_message_build_request(&rqt_msg, "MESSAGE", proxy, from, NULL)!=OSIP_SUCCESS)
{
return -1;
}
if (MakeAlarmBody(xmlBody, sizeof(xmlBody), sn, pGB28181Param, pHander))
{
return -1;
}
if (osip_message_set_content_type(rqt_msg, "Application/MANSCDP+xml")!=OSIP_SUCCESS)
{
osip_message_free(rqt_msg);
return -1;
}
if (osip_message_set_body(rqt_msg, xmlBody, strlen(xmlBody))!=OSIP_SUCCESS)
{
osip_message_free(rqt_msg);
return -1;
}
/* 发送消息 */
eXosip_lock();
ret = eXosip_message_send_request(rqt_msg);
eXosip_unlock();
return (ret==OSIP_SUCCESS)?0:-1;
}
报警消息构建接口:
static int MakeAlarmBody(char *xmlBody, int xmlBodyLen, int sn, GB28181Param_t *pGB28181Param, AlarmHander_t *pHander);
static int MakeAlarmBody(char *xmlBody, int xmlBodyLen, int sn, GB28181Param_t *pGB28181Param, AlarmHander_t *pHander)
{
if (!xmlBody ||!pGB28181Param || !pHander)
{
return -1;
}
snprintf(xmlBody, xmlBodyLen, "<?xml version=\"1.0\"?>\r\n"
"<Notify>\r\n"
"<CmdType>Alarm</CmdType>\r\n" /*命令类型*/
"<SN>%d</SN>\r\n" /*命令序列号*/
"<DeviceID>%s</DeviceID>\r\n" /*设备编码*/
"<AlarmPriority>%d</AlarmPriority>\r\n" /*报警等级*/
"<AlarmTime>%s</AlarmTime>\r\n" /*报警时间*/
"<AlarmMethod>%d</AlarmMethod>\r\n" /*报警方式*/
"<AlarmDescription>%s</AlarmDescription>\r\n" /*警情描述*/
"<Longitude>0.000</Longitude>\r\n"
"<Latitude>0.000</Latitude>\r\n"
"<Info>\r\n"
"<AlarmType>%d</AlarmType>\r\n" /*报警类型*/
"<AlarmTypeParam>\r\n"
"</AlarmTypeParam>\r\n"
"</Info>\r\n"
"</Notify>\r\n",
sn,
pGB28181Param->userParam.iAlarmChn[pHander->alarmChn],
pGB28181Param->userParam.iAlarmPriority[pHander->alarmChn],
pHander->alarmTime,
pHander->alarmMethod,
pHander->alarmDescri,
pHander->alarmType
);
return 0;
}
报警应答解析:
接收sip数据接口:
int SipEventProcess(GB28181Param_t *pGB28181Param)
int SipEventProcess(GB28181Param_t *pGB28181Param)
{
int ret = 0;
int len = 0;
char *msg = NULL;
osip_header_t *dest = NULL;
eXosip_event_t *sipEvent = NULL;
sipEvent = eXosip_event_wait( 0, 100);
if (!sipEvent)
{
eXosip_lock();
eXosip_execute();
eXosip_automatic_action();
eXosip_unlock();
return -1;
}
eXosip_lock();
eXosip_execute();
eXosip_automatic_action();
eXosip_unlock();
switch(sipEvent->type)
{
case EXOSIP_MESSAGE_ANSWERED:
{
// 更新保活时间
pthread_mutex_lock(&g_SipState.mutex);
g_SipState.keepliveAckTime = GetSysSec();
pthread_mutex_unlock(&g_SipState.mutex);
if(MSG_IS_MESSAGE(sipEvent->response))
{
ret = SipResponsMsgProcess(pGB28181Param, sipEvent);
}
}
break;
default:
GB_PrintWarn("error Event, Event type: %d\n", sipEvent->type);
break;
}
eXosip_event_free(sipEvent);
return ret;
}
解析应答接口:
static int SipResponsMsgProcess(GB28181Param_t *pGB28181Param, eXosip_event_t *sipEvent)
{
char xmlSN[32] = {
0,};
char deviceID[32] = {
0,};
char cmdType[32] = {
0,};
char rspXmlBody[2048] = {
0,};
osip_body_t *rspBody = NULL;
mxml_node_t *xml = NULL;
mxml_node_t *node = NULL;
osip_message_t *rsqMsg = NULL;
if (!pGB28181Param || !sipEvent)
{
return -1;
}
eXosip_lock();
/*获取接收到请求的XML消息体*/
int ret = osip_message_get_body(sipEvent->response, 0, &rspBody);
if((NULL == rspBody) || (NULL == rspBody->body))
{
eXosip_unlock();
return -1;
}
eXosip_unlock();
xml = mxmlLoadString(NULL,rspBody->body, MXML_TEXT_CALLBACK);
if (!xml)
{
return -1;
}
// 查找CmdType
node = mxmlFindElement(xml, xml, "CmdType", NULL, NULL, MXML_DESCEND);
if (!node)
{
return -1;
}
strncpy(cmdType, mxmlGetText(node, NULL), sizeof(cmdType));
if (strlen(cmdType) <=0 )
{
return -1;
}
node = mxmlFindElement(xml, xml, "DeviceID", NULL, NULL, MXML_DESCEND);
if (!node)
{
return -1;
}
strncpy(deviceID, mxmlGetText(node, NULL), sizeof(deviceID));
if (strlen(deviceID) <=0)
{
return -1;
}
// 对这个SN需要做一下防重复处理,后面再考虑这个功能
node = mxmlFindElement(xml, xml, "SN", NULL, NULL, MXML_DESCEND);
if (!node)
{
return -1;
}
// 应答时,需要使用设备发过来的SN
strcpy(xmlSN, mxmlGetText(node, NULL));
if (strlen(xmlSN) <=0 )
{
return -1;
}
eXosip_lock();
if ((ret = eXosip_message_build_answer( sipEvent->tid, 200, &rsqMsg)) != OSIP_SUCCESS)
{
eXosip_unlock();
return -1;
}
if ((ret = eXosip_message_send_answer( sipEvent->tid, 200, rsqMsg)) != OSIP_SUCCESS)
{
eXosip_unlock();
return -1;
}
eXosip_unlock();
if (!strcmp(cmdType, "Alarm"))
{
AlarmMsgParase(xml, rspXmlBody, sizeof(rspXmlBody), xmlSN, deviceID, pGB28181Param);
}
else
{
mxmlDelete(xml);
return -1;
}
mxmlDelete(xml);
return 0;
}
解析应答命令:
static int AlarmMsgParase(mxml_node_t *xml, char *msg, int msgLen, char *sn, char *devID, GB28181Param_t *pGB28181Param)
{
char result[32] = {
0,};
if ( !pGB28181Param || !msg || msgLen<= 0 || !xml )
{
return -1;
}
memset(msg, 0, msgLen);
uint8_t iChn = GetChannelID(devID, pGB28181Param, GET_ALARM_TYPE);
if (!IS_ALARM_CHN_VALID(iChn))
{
return -1;
}
// 查找CmdType
mxml_node_t *node = mxmlFindElement(xml, xml, "Result", NULL, NULL, MXML_DESCEND);
if (!node)
{
return -1;
}
strncpy(result, mxmlGetText(node, NULL), sizeof(result));
if (!strcmp(result, "OK"))
{
}
return 0;
}
建议阅读:
GB28181协议–设备注册和注销
参考资料:
《GBT 28181-2016 公共安全视频监控联网系统信息传输、交换、控制技术要求》