今天主要聊聊GB/T28181状态信息报送这块,先回顾下协议规范相关细节,然后再针对代码实现,做个简单的说明。
状态消息报送基本要求
当源设备(包括网关、SIP设备、SIP客户端或联网系统)发现工作异常时,应立即向本 SIP监控域 的SIP服务器发送状态信息;无异常时,应定时向本SIP监控域的SIP服务器发送状态信息。
SIP设备 29 GB/T28181—2016宜在状态信息中携带故障子设备描述信息,详见 A.2.5状态信息报送。
状态信息报送采用IETFRFC 3428中定义的方法 Message实现。
通过周期性的状态信息报送,实现注册服务器与源设备之间的状态检测即心跳机制。 心跳发送方、接收方需统一配置“心跳间隔”参数,按照“心跳间隔”定时发送心跳消息,默认心跳间 隔60s。
心跳发送方、接收方需统一配置“心跳超时次数”参数,心跳消息连续超时达到“心跳超时次数” 则认为对方下线,默认心跳超时次数3次。
心跳接收方在心跳发送方上线状态下检测到心跳消息连续超时达到商定次数则认为心跳发送方离 线;心跳发送方在心跳接收方上线状态下检测到心跳消息响应消息连续超时达到商定次数则认为心跳 接收方离线。
命令流程
命令流程描述如下:
a) 1:源设备向SIP服务器发送设备状态信息报送命令。设备状态信息报送命令采用 Message方 法携带;
b) 2:SIP服务器收到命令后返回200OK。
协议接口
Message消息头 Content-type头域为 Content-type:Application/MANSCDP+xml。 状态信息报送命令采用 MANSCDP协议格式定义,详细描述见 A.2.5状态信息报送。
状态信 息 报 送 命 令 应 包 括 命 令 类 型 (CmdType)、设 备/系 统 编 码 (DeviceID)、是 否 正 常 工 作 (Status)等,采用 Message方法的消息体携带。 Message消息的成功和错误应答均无消息体。
技术实现
本文就以我们开发的Android平台GB28181设备接入模块为例,做个简单的说明,先说参数定义:
private int gb28181_heartbeat_interval_ = 20; // 心跳间隔GB28181默认是60, 目前调整到20秒 private int gb28181_heartbeat_count_ = 3; // 心跳间隔3次失败,表示和服务器断开了
在initGB28181Agent()的时候,设置下去:
// GB28181配置 gb28181_agent_.config(gb28181_reg_expired_, gb28181_heartbeat_interval_, gb28181_heartbeat_count_);
周期性的心跳,携带的关键信息如下:
MESSAGE sip:34020000002000000001@3402000000 SIP/2.0 Via: SIP/2.0/UDP 192.168.0.120:5070;rport;branch=z9hG4bK1200121904 From: <sip:34020000002000000003@3402000000>;tag=858896904 To: <sip:34020000002000000001@3402000000> Call-ID: 1700090587 CSeq: 20 MESSAGE Content-Type: Application/MANSCDP+xml Max-Forwards: 70 User-Agent: IP Camera Content-Length: 177 <?xml version="1.0" encoding="GB2312"?> <Notify> <CmdType>Keepalive</CmdType> <SN>33</SN> <DeviceID>34020000002000000003</DeviceID> <Status>OK</Status> <Info> </Info> </Notify>
异常处理如下,如果有语音广播,需要停掉GB28181的语音广播,数据发送也停掉:
/* * CameraPublishActivity.java * 心跳异常处理 * * WebSite: https://daniusdk.com * Github: https://github.com/daniulive/SmarterStreaming */ @Override public void ntsOnHeartBeatException(int exceptionCount, String lastExceptionInfo) { Log.e(TAG, "ntsOnHeartBeatException heart beat timeout count reached, count:" + exceptionCount+ ", exception info:" + (lastExceptionInfo!=null?lastExceptionInfo:"")); // 10毫秒后,停止信令, 然后重启 handler_.postDelayed(new Runnable() { @Override public void run() { Log.i(TAG, "gb28281_heart_beart_timeout"); stopAudioPlayer(); destoryRTPReceiver(); gb_broadcast_source_id_ = null; gb_broadcast_target_id_ = null; btnGB28181AudioBroadcast.setText("GB28181语音广播"); btnGB28181AudioBroadcast.setEnabled(false); stopGB28181Stream(); destoryRTPSender(); if (gb28181_agent_ != null) { Log.i(TAG, "gb28281_heart_beart_timeout sip stop"); gb28181_agent_.stop(); Log.i(TAG, "gb28281_heart_beart_timeout sip start"); gb28181_agent_.start(); } } },10); }
总的来说,状态信息报送流程比较简单,Message消息也非常容易组织,没有非常特别需要注意的地方,我们更需要关注的是心跳状态反馈,特别是异常处理。