GB/T28181-2011 《安全防范视频监控联网系统信息传输、交换、控制技术要求》是由公安部科技信息化局提出,由全国安全防范报警系统标准化技术委员会(SAC/TC100)归口,公安部一所等多家单位共同起草的一部国家标准。
http://www.gb688.cn/bzgk/gb/newGbInfo?hcno=469659DC56B9B8187671FF08748CEC89
通过仔细学习《GBT 28181-2016 公共安全视频监控联网系统信息传输、交换、控制技术要求》技术规范文档。整篇主要是基于SIP协议对视频监控系统互联中涉及的消息内容格式进行了规范,由于GB28181是适用于各个领域各种情况下的视频监控系统信令交互,涉及的信令太多,在实际应用中往往只使用其中一部份信令,今天我主要讲几个最常用的信令应用。
设备注册
当有新的监控设备要接入视频监控平台时,首先要在服务端进行注册操作,其中涉及用户注册时身份认证是本信令的难点。
1、SIP代理终端(视频监控设备)向 SIP服务器发送 Register请求并携带设备信息,表明有新设备要接入监控平台。
2、SIP服务器向SIP代理终端回401,增加响应消息头域 WWW-Authenticate信息,要求终端进行身份验证。
3、SIP代理终端收到401响应后,取出nonce字段,根据服务端身份认证要求对设备信息进行加密处理(一般是MD5),携带认证信息重新发起Register请求。
4、SIP服务器对请求进行验证,如果检查新注册终端身份合法,向终端发送200成功响应。
@Slf4j
public class SipServerLayer implements SipListener {
public SipSrymyMessageProcessor getMessageProcessor() {
return messageProcessor;
}
public void setMessageProcessor(SipSrymyMessageProcessor messageProcessor) {
this.messageProcessor = messageProcessor;
}
private SipSrymyMessageProcessor messageProcessor;
private SipStack sipStack;
private SipFactory sipFactory;
private AddressFactory addressFactory;
private HeaderFactory headerFactory;
private MessageFactory messageFactory;
private SipProvider sipProvider;
private ListeningPoint tcp;
private ListeningPoint udp;
/**
* Here we initialize the SIP stack.
*/
@SuppressWarnings("deprecation")
public SipServerLayer(String ip, int port) throws PeerUnavailableException,
TransportNotSupportedException, InvalidArgumentException, ObjectInUseException, TooManyListenersException {
sipFactory = SipFactory.getInstance();
sipFactory.setPathName("gov.nist");
Properties properties = new Properties();
properties.setProperty("javax.sip.STACK_NAME", "srymy-GB28181");
properties.setProperty("javax.sip.IP_ADDRESS", ip);
/**
* sip_server_log.log 和 sip_debug_log.log
* public static final int TRACE_NONE = 0;
public static final int TRACE_MESSAGES = 16;
public static final int TRACE_EXCEPTION = 17;
public static final int TRACE_DEBUG = 32;
*/
properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "0");
properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "sip_server_log");
properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sip_debug_log");
sipStack = sipFactory.createSipStack(properties);
headerFactory = sipFactory.createHeaderFactory();
addressFactory = sipFactory.createAddressFactory();
messageFactory = sipFactory.createMessageFactory();
tcp = sipStack.createListeningPoint(ip, port, "tcp");
udp = sipStack.createListeningPoint(ip, port, "udp");
createListeningPoint(ip,port);
}
/**
* 停止监听
* @throws ObjectInUseException
*/
public void deleteListeningPoint() throws ObjectInUseException {
if (tcp != null) {
sipStack.deleteListeningPoint(tcp);
sipStack.deleteListeningPoint(udp);
}
}
/**
* 创建SIP端口监听,同时监听UDP和TCP
* @param ip
* @param port
* @throws InvalidArgumentException
* @throws TransportNotSupportedException
* @throws ObjectInUseException
* @throws TooManyListenersException
*/
public void createListeningPoint(String ip, int port) throws InvalidArgumentException, TransportNotSupportedException, ObjectInUseException, TooManyListenersException {
tcp = sipStack.createListeningPoint(ip, port, "tcp");
udp = sipStack.createListeningPoint(ip, port, "udp");
sipProvider = sipStack.createSipProvider(tcp);
sipProvider.addSipListener(this);
sipProvider = sipStack.createSipProvider(udp);
sipProvider.addSipListener(this);
}
/**
* This method is called by the SIP stack when a response arrives.
*/
@Override
public void processResponse(ResponseEvent responseEvent) {
try {
messageProcessor.processResponse(responseEvent, addressFactory, messageFactory, headerFactory, sipProvider);
} catch (InvalidArgumentException e) {
e.printStackTrace();
}
}
/**
* SIP服务端接收消息的方法
* Content 里面是GBK编码
* This method is called by the SIP stack when a new request arrives.
*/
@Override
public void processRequest(RequestEvent requestEvent) {
messageProcessor.processRequest(requestEvent, addressFactory, messageFactory, headerFactory, sipProvider);
}
/**
* This method is called by the SIP stack when there's no answer to a
* message. Note that this is treated differently from an error message.
*/
@Override
public void processTimeout(TimeoutEvent evt) {
messageProcessor.processError("Previous message not sent: " + "timeout");
}
/**
* This method is called by the SIP stack when there's an asynchronous
* message transmission error.
*/
@Override
public void processIOException(IOExceptionEvent evt) {
messageProcessor.processError("Previous message not sent: " + "I/O Exception");
}
/**
* This method is called by the SIP stack when a dialog (session) ends.
*/
@Override
public void processDialogTerminated(DialogTerminatedEvent evt) {
log.info("会话结束:CallId: {}, DialogId: {}",evt.getDialog().getCallId(), evt.getDialog().getDialogId());
}
/**
* This method is called by the SIP stack when a transaction ends.
*/
@Override
public void processTransactionTerminated(TransactionTerminatedEvent evt) {
log.info("Transaction结束,evt: {}",evt);
}
}
spring boot 框架搭建项目基础配置步骤:
1、pom.xml配置如下
<!-- SPI协议相关的包 -->
<dependency>
<groupId>javax.sip</groupId>
<artifactId>jain-sip-api</artifactId>
<version>1.2.1.4</version>
</dependency>
<dependency>
<groupId>javax.sip</groupId>
<artifactId>jain-sip-ri</artifactId>
<version>1.2.1.4</version>
</dependency>
2、application.properties配置如下
server.port=10000
logging.level.org.springframework.boot.autoconfigure=ERROR
gb28181.deviceId=34020000002000000001
gb28181.ip=172.16.1.65(自己本地地址)
gb28181.port=5060
gb28181.password=12345678
3、编写SIP监听实现类,并集成到springboot项目
package com.srymy.gb28181.part1.config;
import com.srymy.gb28181.part1.sip.SipSrymyMessageProcessor;
import com.srymy.gb28181.part1.sip.SipServerLayer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sip.PeerUnavailableException;
/**
* 监听实现类
* @Author 授人以鳗鱼
* @Date 9/25/2019
*/
@Slf4j
@Configuration
public class SipSrymyConfig {
@Value("${gb28181.deviceId:34020000002000000001}")
String deviceId;
@Value("${gb28181.ip:172.18.99.169}")
String ip;
@Value("${gb28181.port:5060}")
Integer port;
@Bean
public SipServerLayer sipLayer(SipSrymyMessageProcessor messageProcessor) {
try {
SipServerLayer sipServerLayer = new SipServerLayer(ip, port);
sipServerLayer.setMessageProcessor(messageProcessor);
log.info("SIP服务启动完毕, 已经在[{}:{}]端口监听SIP国标消息", ip, port);
return sipServerLayer;
} catch (PeerUnavailableException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
4、服务启动后,你可以用设备注册到这边,并抓包看看,你会发现SIP服务已经启动。
5、抓包工具推荐使用:wireshark,过滤信令类型 SIP