Conflicts:
	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java
This commit is contained in:
swwheihei 2020-07-16 16:09:48 +08:00
commit 49cfc55f46
49 changed files with 461 additions and 343 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 swwhaha
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -39,7 +39,7 @@
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -127,7 +127,7 @@
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>

View File

@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.common;
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: swwheihei
* @author: songww
* @date: 2019年5月30日 下午3:04:04
*
*/

View File

@ -13,7 +13,7 @@ import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer;
/**
* @Description:Redis中间件配置类使用spring-data-redis集成自动从application.yml中加载redis配置
* @author: swwheihei
* @author: songww
* @date: 2019年5月30日 上午10:58:25
*
*/

View File

@ -5,7 +5,7 @@ import org.springframework.context.annotation.Configuration;
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 下午2:46:00
*/
@Configuration("vmConfig")

View File

@ -1,250 +1,262 @@
package com.genersoft.iot.vmp.gb28181;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
import javax.sip.ListeningPoint;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipFactory;
import javax.sip.SipListener;
import javax.sip.SipProvider;
import javax.sip.SipStack;
import javax.sip.TimeoutEvent;
import javax.sip.TransactionAlreadyExistsException;
import javax.sip.TransactionTerminatedEvent;
import javax.sip.TransactionUnavailableException;
import javax.sip.address.AddressFactory;
import javax.sip.header.HeaderFactory;
import javax.sip.header.ViaHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
import gov.nist.javax.sip.SipStackImpl;
@Component
public class SipLayer implements SipListener, Runnable {
private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);
@Autowired
private SipConfig sipConfig;
private SipProvider tcpSipProvider;
private SipProvider udpSipProvider;
@Autowired
private SIPProcessorFactory processorFactory;
private SipStack sipStack;
private AddressFactory addressFactory;
private HeaderFactory headerFactory;
private MessageFactory messageFactory;
@PostConstruct
private void initSipServer() {
Thread thread=new Thread(this);
thread.setDaemon(true);
thread.setName("sip server thread start");
thread.start();
}
@Override
public void run() {
SipFactory sipFactory = SipFactory.getInstance();
sipFactory.setPathName("gov.nist");
try {
headerFactory = sipFactory.createHeaderFactory();
addressFactory = sipFactory.createAddressFactory();
messageFactory = sipFactory.createMessageFactory();
Properties properties = new Properties();
properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP");
properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.getSipIp());
properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "false");
/**
* 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 = (SipStackImpl) sipFactory.createSipStack(properties);
startTcpListener();
startUdpListener();
} catch (Exception e) {
logger.error("Sip Server 启动失败! port {" + sipConfig.getSipPort() + "}");
e.printStackTrace();
}
logger.info("Sip Server 启动成功 port {" + sipConfig.getSipPort() + "}");
}
private void startTcpListener() throws Exception {
ListeningPoint tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "TCP");
tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint);
tcpSipProvider.addSipListener(this);
}
private void startUdpListener() throws Exception {
ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "UDP");
udpSipProvider = sipStack.createSipProvider(udpListeningPoint);
udpSipProvider.addSipListener(this);
}
/**
* SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a
* new request arrives.
*/
@Override
public void processRequest(RequestEvent evt) {
ISIPRequestProcessor processor = processorFactory.createRequestProcessor(evt);
processor.process(evt, this);
}
@Override
public void processResponse(ResponseEvent evt) {
Response response = evt.getResponse();
int status = response.getStatusCode();
if ((status >= 200) && (status < 300)) { // Success!
ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);
processor.process(evt, this, sipConfig);
} else {
logger.warn("接收到失败的response响应status" + status + ",message:" + response.getContent().toString());
}
// trying不会回复
if (status == Response.TRYING) {
}
}
/**
* <p>
* Title: processTimeout
* </p>
* <p>
* Description:
* </p>
*
* @param timeoutEvent
*/
@Override
public void processTimeout(TimeoutEvent timeoutEvent) {
// TODO Auto-generated method stub
}
/**
* <p>
* Title: processIOException
* </p>
* <p>
* Description:
* </p>
*
* @param exceptionEvent
*/
@Override
public void processIOException(IOExceptionEvent exceptionEvent) {
// TODO Auto-generated method stub
}
/**
* <p>
* Title: processTransactionTerminated
* </p>
* <p>
* Description:
* </p>
*
* @param transactionTerminatedEvent
*/
@Override
public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
// TODO Auto-generated method stub
}
/**
* <p>
* Title: processDialogTerminated
* </p>
* <p>
* Description:
* </p>
*
* @param dialogTerminatedEvent
*/
@Override
public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
// TODO Auto-generated method stub
}
public ServerTransaction getServerTransaction(RequestEvent evt) {
Request request = evt.getRequest();
ServerTransaction serverTransaction = evt.getServerTransaction();
// 判断TCP还是UDP
boolean isTcp = false;
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
String transport = reqViaHeader.getTransport();
if (transport.equals("TCP")) {
isTcp = true;
}
if (serverTransaction == null) {
try {
if (isTcp) {
serverTransaction = tcpSipProvider.getNewServerTransaction(request);
} else {
serverTransaction = udpSipProvider.getNewServerTransaction(request);
}
} catch (TransactionAlreadyExistsException e) {
e.printStackTrace();
} catch (TransactionUnavailableException e) {
e.printStackTrace();
}
}
return serverTransaction;
}
public AddressFactory getAddressFactory() {
return addressFactory;
}
public HeaderFactory getHeaderFactory() {
return headerFactory;
}
public MessageFactory getMessageFactory() {
return messageFactory;
}
public SipProvider getTcpSipProvider() {
return tcpSipProvider;
}
public SipProvider getUdpSipProvider() {
return udpSipProvider;
}
}
package com.genersoft.iot.vmp.gb28181;
import java.text.ParseException;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
import javax.sip.ListeningPoint;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipFactory;
import javax.sip.SipListener;
import javax.sip.SipProvider;
import javax.sip.SipStack;
import javax.sip.TimeoutEvent;
import javax.sip.TransactionAlreadyExistsException;
import javax.sip.TransactionTerminatedEvent;
import javax.sip.TransactionUnavailableException;
import javax.sip.address.AddressFactory;
import javax.sip.header.HeaderFactory;
import javax.sip.header.ViaHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
import gov.nist.javax.sip.SipStackImpl;
@Component
public class SipLayer implements SipListener, Runnable {
private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);
@Autowired
private SipConfig sipConfig;
private SipProvider tcpSipProvider;
private SipProvider udpSipProvider;
@Autowired
private SIPProcessorFactory processorFactory;
private SipStack sipStack;
private AddressFactory addressFactory;
private HeaderFactory headerFactory;
private MessageFactory messageFactory;
@PostConstruct
private void initSipServer() {
Thread thread = new Thread(this);
thread.setDaemon(true);
thread.setName("sip server thread start");
thread.start();
}
@Override
public void run() {
SipFactory sipFactory = SipFactory.getInstance();
sipFactory.setPathName("gov.nist");
try {
headerFactory = sipFactory.createHeaderFactory();
addressFactory = sipFactory.createAddressFactory();
messageFactory = sipFactory.createMessageFactory();
Properties properties = new Properties();
properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP");
properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.getSipIp());
properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "false");
/**
* 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", "32");
properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "sip_server_log");
properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sip_debug_log");
sipStack = (SipStackImpl) sipFactory.createSipStack(properties);
startTcpListener();
startUdpListener();
} catch (Exception e) {
logger.error("Sip Server 启动失败! port {" + sipConfig.getSipPort() + "}");
e.printStackTrace();
}
logger.info("Sip Server 启动成功 port {" + sipConfig.getSipPort() + "}");
}
private void startTcpListener() throws Exception {
ListeningPoint tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(),
"TCP");
tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint);
tcpSipProvider.addSipListener(this);
}
private void startUdpListener() throws Exception {
ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(),
"UDP");
udpSipProvider = sipStack.createSipProvider(udpListeningPoint);
udpSipProvider.addSipListener(this);
}
/**
* SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a
* new request arrives.
*/
@Override
public void processRequest(RequestEvent evt) {
ISIPRequestProcessor processor = processorFactory.createRequestProcessor(evt);
processor.process(evt, this);
}
@Override
public void processResponse(ResponseEvent evt) {
Response response = evt.getResponse();
int status = response.getStatusCode();
if ((status >= 200) && (status < 300)) { // Success!
ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);
try {
processor.process(evt, this, sipConfig);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// } else if (status == Response.TRYING) {
// trying不会回复
} else if ((status >= 100) && (status < 200)) {
// 增加其它无需回复的响应如101180等
} else {
logger.warn("接收到失败的response响应status" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/);
}
// trying不会回复
// if (status == Response.TRYING) {
// }
}
/**
* <p>
* Title: processTimeout
* </p>
* <p>
* Description:
* </p>
*
* @param timeoutEvent
*/
@Override
public void processTimeout(TimeoutEvent timeoutEvent) {
// TODO Auto-generated method stub
}
/**
* <p>
* Title: processIOException
* </p>
* <p>
* Description:
* </p>
*
* @param exceptionEvent
*/
@Override
public void processIOException(IOExceptionEvent exceptionEvent) {
// TODO Auto-generated method stub
}
/**
* <p>
* Title: processTransactionTerminated
* </p>
* <p>
* Description:
* </p>
*
* @param transactionTerminatedEvent
*/
@Override
public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
// TODO Auto-generated method stub
}
/**
* <p>
* Title: processDialogTerminated
* </p>
* <p>
* Description:
* </p>
*
* @param dialogTerminatedEvent
*/
@Override
public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
// TODO Auto-generated method stub
}
public ServerTransaction getServerTransaction(RequestEvent evt) {
Request request = evt.getRequest();
ServerTransaction serverTransaction = evt.getServerTransaction();
// 判断TCP还是UDP
boolean isTcp = false;
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
String transport = reqViaHeader.getTransport();
if (transport.equals("TCP")) {
isTcp = true;
}
if (serverTransaction == null) {
try {
if (isTcp) {
serverTransaction = tcpSipProvider.getNewServerTransaction(request);
} else {
serverTransaction = udpSipProvider.getNewServerTransaction(request);
}
} catch (TransactionAlreadyExistsException e) {
e.printStackTrace();
} catch (TransactionUnavailableException e) {
e.printStackTrace();
}
}
return serverTransaction;
}
public AddressFactory getAddressFactory() {
return addressFactory;
}
public HeaderFactory getHeaderFactory() {
return headerFactory;
}
public MessageFactory getMessageFactory() {
return messageFactory;
}
public SipProvider getTcpSipProvider() {
return tcpSipProvider;
}
public SipProvider getUdpSipProvider() {
return udpSipProvider;
}
}

View File

@ -8,7 +8,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
/**
* @Description:注册逻辑处理当设备注册后触发逻辑
* @author: swwheihei
* @author: songww
* @date: 2020年5月8日 下午9:41:46
*/
@Component

View File

@ -4,7 +4,7 @@ import java.util.List;
/**
* @Description:设备录像信息bean
* @author: swwheihei
* @author: songww
* @date: 2020年5月8日 下午2:05:56
*/
public class RecordInfo {

View File

@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.gb28181.bean;
/**
* @Description:设备录像bean
* @author: swwheihei
* @author: songww
* @date: 2020年5月8日 下午2:06:54
*/
public class RecordItem {

View File

@ -8,7 +8,7 @@ import com.genersoft.iot.vmp.utils.redis.RedisUtil;
/**
* @Description:设备离在线状态检测器用于检测设备状态
* @author: swwheihei
* @author: songww
* @date: 2020年5月13日 下午2:40:29
*/
@Component

View File

@ -9,7 +9,7 @@ import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent;
/**
* @Description:Event事件通知推送器支持推送在线事件离线事件
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 上午11:30:50
*/
@Component

View File

@ -11,7 +11,7 @@ import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
/**
* @Description:设备心跳超时监听,借助redis过期特性进行监听监听到说明设备心跳超时发送离线事件
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 上午11:35:46
*/
@Component

View File

@ -4,7 +4,7 @@ import org.springframework.context.ApplicationEvent;
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 上午11:33:13
*/
public class OfflineEvent extends ApplicationEvent {

View File

@ -14,7 +14,7 @@ import com.genersoft.iot.vmp.utils.redis.RedisUtil;
* @Description: 离线事件监听器监听到离线后修改设备离在线状态 设备离线有两个来源
* 1设备主动注销发送注销指令{@link com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor}
* 2设备未知原因离线心跳超时,{@link com.genersoft.iot.vmp.gb28181.event.offline.OfflineEventListener}
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 下午1:51:23
*/
@Component

View File

@ -4,7 +4,7 @@ import org.springframework.context.ApplicationEvent;
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 上午11:32:56
*/
public class OnlineEvent extends ApplicationEvent {

View File

@ -14,7 +14,7 @@ import com.genersoft.iot.vmp.utils.redis.RedisUtil;
* @Description: 在线事件监听器监听到离线后修改设备离在线状态 设备在线有两个来源
* 1设备主动注销发送注销指令{@link com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor}
* 2设备未知原因离线心跳超时,{@link com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor}
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 下午1:51:23
*/
@Component

View File

@ -9,7 +9,7 @@ import com.genersoft.iot.vmp.utils.SpringBeanFactory;
/**
* @Description:SIP信令中的SSRC工具类SSRC值由10位十进制整数组成的字符串第一位为0代表实况为1则代表回放第二位至第六位由监控域ID的第4位到第8位组成最后4位为不重复的4个整数
* @author: swwheihei
* @author: songww
* @date: 2020年5月10日 上午11:57:57
*/
public class SsrcUtil {

View File

@ -8,7 +8,7 @@ import org.springframework.stereotype.Component;
/**
* @Description:视频流session管理器管理视频预览预览回放的通信句柄
* @author: swwheihei
* @author: songww
* @date: 2020年5月13日 下午4:03:02
*/
@Component

View File

@ -26,7 +26,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.response.impl.OtherResponseProcess
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午4:24:37
*/
@Component

View File

@ -10,7 +10,7 @@ import org.springframework.web.context.request.async.DeferredResult;
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: swwheihei
* @author: songww
* @date: 2020年5月8日 下午7:59:05
*/
@Component

View File

@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.callback;
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: swwheihei
* @author: songww
* @date: 2020年5月8日 下午1:09:18
*/
public class RequestMessage {

View File

@ -4,7 +4,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
/**
* @Description:设备能力接口用于定义设备的控制查询能力
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午9:16:34
*/
public interface ISIPCommander {

View File

@ -25,7 +25,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Host;
/**
* @Description:摄像头命令request创造器 TODO 冗余代码太多待优化
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 上午9:29:02
*/
@Component
@ -79,7 +79,8 @@ public class SIPRequestHeaderProvider {
SipURI requestLine = layer.getAddressFactory().createSipURI(channelId, host.getAddress());
//via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), device.getTransport(), viaTag);
// ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), device.getTransport(), viaTag);
ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(device.getHost().getIp(), device.getHost().getPort(), device.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
//from
@ -108,6 +109,7 @@ public class SIPRequestHeaderProvider {
request = layer.getMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
Address concatAddress = layer.getAddressFactory().createAddress(layer.getAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
// Address concatAddress = layer.getAddressFactory().createAddress(layer.getAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort()));
request.addHeader(layer.getHeaderFactory().createContactHeader(concatAddress));
ContentTypeHeader contentTypeHeader = layer.getHeaderFactory().createContentTypeHeader("Application", "SDP");
@ -122,7 +124,8 @@ public class SIPRequestHeaderProvider {
SipURI requestLine = layer.getAddressFactory().createSipURI(device.getDeviceId(), host.getAddress());
//via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), device.getTransport(), viaTag);
// ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), device.getTransport(), viaTag);
ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(device.getHost().getIp(), device.getHost().getPort(), device.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
//from
@ -151,6 +154,7 @@ public class SIPRequestHeaderProvider {
request = layer.getMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
Address concatAddress = layer.getAddressFactory().createAddress(layer.getAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
// Address concatAddress = layer.getAddressFactory().createAddress(layer.getAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort()));
request.addHeader(layer.getHeaderFactory().createContactHeader(concatAddress));
ContentTypeHeader contentTypeHeader = layer.getHeaderFactory().createContentTypeHeader("Application", "SDP");

View File

@ -7,10 +7,13 @@ import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import javax.sip.TransactionDoesNotExistException;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties.Headers;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig;
@ -21,9 +24,12 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* @Description:设备能力接口用于定义设备的控制查询能力
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午9:22:48
*/
@Component
@ -94,6 +100,49 @@ public class SIPCommander implements ISIPCommander {
return ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed);
}
/**
* 云台指令码计算
*
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
* @param moveSpeed 镜头移动速度 默认 0XFF (0-255)
* @param zoomSpeed 镜头缩放速度 默认 0X1 (0-255)
*/
public static String cmdString(int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) {
int cmdCode = 0;
if (leftRight == 2) {
cmdCode|=0x01; // 右移
} else if(leftRight == 1) {
cmdCode|=0x02; // 左移
}
if (upDown == 2) {
cmdCode|=0x04; // 下移
} else if(upDown == 1) {
cmdCode|=0x08; // 上移
}
if (inOut == 2) {
cmdCode |= 0x10; // 放大
} else if(inOut == 1) {
cmdCode |= 0x20; // 缩小
}
StringBuilder builder = new StringBuilder("A50F01");
String strTmp;
strTmp = String.format("%02X", cmdCode);
builder.append(strTmp, 0, 2);
strTmp = String.format("%02X", moveSpeed);
builder.append(strTmp, 0, 2);
builder.append(strTmp, 0, 2);
strTmp = String.format("%X", zoomSpeed);
builder.append(strTmp, 0, 1).append("0");
//计算校验码
int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + moveSpeed + moveSpeed + (zoomSpeed /*<< 4*/ & 0XF0)) % 0X100;
strTmp = String.format("%02X", checkCode);
builder.append(strTmp, 0, 2);
return builder.toString();
}
/**
* 云台控制支持方向与缩放控制
*
@ -109,13 +158,14 @@ public class SIPCommander implements ISIPCommander {
public boolean ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,
int zoomSpeed) {
try {
String cmdStr= cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed);
StringBuffer ptzXml = new StringBuffer(200);
ptzXml.append("<?xml version=\"1.0\" ?>");
ptzXml.append("<Control>");
ptzXml.append("<CmdType>DeviceControl</CmdType>");
ptzXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>");
ptzXml.append("<DeviceID>" + channelId + "</DeviceID>");
ptzXml.append("<PTZCmd>" + "</PTZCmd>");
ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>");
ptzXml.append("<Info>");
ptzXml.append("</Info>");
ptzXml.append("</Control>");
@ -123,7 +173,6 @@ public class SIPCommander implements ISIPCommander {
Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtzTag", "ToPtzTag");
transmitRequest(device, request);
return true;
} catch (SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
@ -245,6 +294,13 @@ public class SIPCommander implements ISIPCommander {
return;
}
Request byeRequest = dialog.createRequest(Request.BYE);
SipURI byeURI = (SipURI) byeRequest.getRequestURI();
String vh = transaction.getRequest().getHeader(ViaHeader.NAME).toString();
Pattern p = Pattern.compile("(\\d+\\.\\d+\\.\\d+\\.\\d+)\\:(\\d+)");
Matcher matcher = p.matcher(vh);
if (matcher.find()) {
byeURI.setHost(matcher.group(1));
}
ViaHeader viaHeader = (ViaHeader) byeRequest.getHeader(ViaHeader.NAME);
String protocol = viaHeader.getTransport().toUpperCase();
ClientTransaction clientTransaction = null;
@ -258,6 +314,8 @@ public class SIPCommander implements ISIPCommander {
e.printStackTrace();
} catch (SipException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}

View File

@ -6,7 +6,7 @@ import com.genersoft.iot.vmp.gb28181.SipLayer;
/**
* @Description:处理接收IPCamera发来的SIP协议请求消息
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午4:42:22
*/
public interface ISIPRequestProcessor {

View File

@ -16,7 +16,7 @@ import gov.nist.javax.sip.header.CSeq;
/**
* @Description:ACK请求处理器
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午5:31:45
*/
@Component

View File

@ -10,7 +10,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
/**
* @Description: BYE请求处理器
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午5:32:05
*/
@Component

View File

@ -10,7 +10,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
/**
* @Description:CANCEL请求处理器
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午5:32:23
*/
@Component

View File

@ -10,7 +10,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
/**
* @Description:处理INVITE请求
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午4:43:52
*/
@Component

View File

@ -43,7 +43,7 @@ import com.genersoft.iot.vmp.utils.redis.RedisUtil;
/**
* @Description:MESSAGE请求处理器
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午5:32:41
*/
@Component
@ -100,6 +100,7 @@ public class MessageRequestProcessor implements ISIPRequestProcessor {
Request request = evt.getRequest();
SAXReader reader = new SAXReader();
reader.setEncoding("gbk");
Document xml;
try {
xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
@ -375,7 +376,7 @@ public class MessageRequestProcessor implements ISIPRequestProcessor {
private Element getRootElement(RequestEvent evt) throws DocumentException {
Request request = evt.getRequest();
SAXReader reader = new SAXReader();
reader.setEncoding("GB2312");
reader.setEncoding("gbk");
Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
return xml.getRootElement();
}

View File

@ -10,7 +10,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
/**
* @Description:暂不支持的消息请求处理器
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午5:32:59
*/
@Component

View File

@ -38,7 +38,7 @@ import gov.nist.javax.sip.header.Expires;
/**
* @Description:收到注册请求 处理
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午4:47:25
*/
@Component

View File

@ -17,7 +17,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
/**
* @Description:SUBSCRIBE请求处理器
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午5:31:20
*/
@Component

View File

@ -1,5 +1,7 @@
package com.genersoft.iot.vmp.gb28181.transmit.response;
import java.text.ParseException;
import javax.sip.ResponseEvent;
import com.genersoft.iot.vmp.conf.SipConfig;
@ -7,11 +9,11 @@ import com.genersoft.iot.vmp.gb28181.SipLayer;
/**
* @Description:处理接收IPCamera发来的SIP协议响应消息
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午4:42:22
*/
public interface ISIPResponseProcessor {
public void process(ResponseEvent evt, SipLayer layer, SipConfig config);
public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException;
}

View File

@ -10,7 +10,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
/**
* @Description: BYE请求响应器
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午5:32:05
*/
@Component

View File

@ -10,7 +10,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
/**
* @Description:CANCEL响应处理器
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午5:32:23
*/
@Component

View File

@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.gb28181.transmit.response.impl;
import java.text.ParseException;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.ResponseEvent;
@ -22,57 +21,76 @@ import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory;
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
/**
* @Description:处理INVITE响应
* @author: swwheihei
* @date: 2020年5月3日 下午4:43:52
/**
* @Description:处理INVITE响应
* @author: songww
* @date: 2020年5月3日 下午4:43:52
*/
@Component
public class InviteResponseProcessor implements ISIPResponseProcessor {
private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class);
/**
* 处理invite响应
*
* @param evt
* 响应消息
*/
* @param evt 响应消息
* @throws ParseException
*/
@Override
public void process(ResponseEvent evt, SipLayer layer, SipConfig config) {
public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException {
try {
Response response = evt.getResponse();
int statusCode = response.getStatusCode();
//trying不会回复
if(statusCode == Response.TRYING){
// trying不会回复
if (statusCode == Response.TRYING) {
}
//成功响应
//下发ack
if(statusCode == Response.OK){
// ClientTransaction clientTransaction = evt.getClientTransaction();
// if(clientTransaction == null){
// logger.error("回复ACK时clientTransaction为null >>> {}",response);
// return;
// }
// Dialog clientDialog = clientTransaction.getDialog();
//
// CSeqHeader clientCSeqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
// long cseqId = clientCSeqHeader.getSeqNumber();
// /*
// createAck函数创建的ackRequest会采用Invite响应的200OK中的contact字段中的地址作为目标地址
// 有的终端传上来的可能还是内网地址会造成ack发送不出去接受不到音视频流
// 所以在此处统一替换地址和响应消息的Via头中的地址保持一致
// */
// Request ackRequest = clientDialog.createAck(cseqId);
// SipURI requestURI = (SipURI) ackRequest.getRequestURI();
// ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME);
// requestURI.setHost(viaHeader.getHost());
// requestURI.setPort(viaHeader.getPort());
// clientDialog.sendAck(ackRequest);
// 成功响应
// 下发ack
if (statusCode == Response.OK) {
// ClientTransaction clientTransaction = evt.getClientTransaction();
// if(clientTransaction == null){
// logger.error("回复ACK时clientTransaction为null >>> {}",response);
// return;
// }
// Dialog clientDialog = clientTransaction.getDialog();
// CSeqHeader clientCSeqHeader = (CSeqHeader)
// response.getHeader(CSeqHeader.NAME);
// long cseqId = clientCSeqHeader.getSeqNumber();
// /*
// createAck函数创建的ackRequest会采用Invite响应的200OK中的contact字段中的地址作为目标地址
// 有的终端传上来的可能还是内网地址会造成ack发送不出去接受不到音视频流
// 所以在此处统一替换地址和响应消息的Via头中的地址保持一致
// */
// Request ackRequest = clientDialog.createAck(cseqId);
// SipURI requestURI = (SipURI) ackRequest.getRequestURI();
// ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME);
// try {
// requestURI.setHost(viaHeader.getHost());
// } catch (Exception e) {
// e.printStackTrace();
// }
// requestURI.setPort(viaHeader.getPort());
// clientDialog.sendAck(ackRequest);
Dialog dialog = evt.getDialog();
Request reqAck =dialog.createAck(1L);
CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
Request reqAck = dialog.createAck(cseq.getSeqNumber());
SipURI requestURI = (SipURI) reqAck.getRequestURI();
ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME);
// String viaHost =viaHeader.getHost();
//getHost()函数取回的IP地址是[xxx.xxx.xxx.xxx:yyyy]的格式需用正则表达式截取为xxx.xxx.xxx.xxx"格式
// Pattern p = Pattern.compile("(?<=//|)((\\w)+\\.)+\\w+");
// Matcher matcher = p.matcher(viaHeader.getHost());
// if (matcher.find()) {
// requestURI.setHost(matcher.group());
// }
requestURI.setHost(viaHeader.getHost());
requestURI.setPort(viaHeader.getPort());
reqAck.setRequestURI(requestURI);
dialog.sendAck(reqAck);
}
} catch (InvalidArgumentException | SipException e) {

View File

@ -10,7 +10,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
/**
* @Description:暂不支持的消息响应处理器
* @author: swwheihei
* @author: songww
* @date: 2020年5月3日 下午5:32:59
*/
@Component

View File

@ -7,7 +7,7 @@ import java.util.Locale;
/**
* @Description:时间工具类主要处理ISO 8601格式转换
* @author: swwheihei
* @author: songww
* @date: 2020年5月8日 下午3:24:42
*/
public class DateUtil {

View File

@ -18,11 +18,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
/**
* @Description:针对 ZLMediaServer的hook事件监听
* @author: swwheihei
* @author: songww
* @date: 2020年5月8日 上午10:46:48
*/
@RestController
@RequestMapping("/hook/zlm")
@RequestMapping("/index/hook")
public class ZLMHttpHookListener {
private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class);

View File

@ -6,7 +6,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
/**
* @Description:视频设备数据存储接口
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 下午2:14:31
*/
public interface IVideoManagerStorager {

View File

@ -8,7 +8,7 @@ import com.genersoft.iot.vmp.conf.VManagerConfig;
/**
* @Description:视频设备数据存储工厂根据存储策略返回对应的存储器
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 下午2:15:16
*/
@Component

View File

@ -11,7 +11,7 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
/**
* @Description:视频设备数据存储-jdbc实现
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 下午2:28:12
*/
@Component("jdbcStorager")

View File

@ -13,7 +13,7 @@ import com.genersoft.iot.vmp.utils.redis.RedisUtil;
/**
* @Description:视频设备数据存储-redis实现
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 下午2:31:42
*/
@Component("redisStorager")

View File

@ -7,7 +7,7 @@ import org.springframework.stereotype.Component;
/**
* @Description:spring bean获取工厂获取spring中的已初始化的bean
* @author: swwheihei
* @author: songww
* @date: 2019年6月25日 下午4:51:52
*
*/

View File

@ -10,7 +10,7 @@ import com.alibaba.fastjson.serializer.SerializerFeature;
/**
* @Description:使用fastjson实现redis的序列化
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 下午8:40:11
*/
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {

View File

@ -13,7 +13,7 @@ import org.springframework.util.CollectionUtils;
/**
* @Description:Redis工具类
* @author: swwheihei
* @author: songww
* @date: 2020年5月6日 下午8:27:29
*/
@Component

View File

@ -26,7 +26,8 @@ spring:
server:
port: 8080
sip:
ip: 10.200.64.63
# ip: 10.200.64.63
ip: 192.168.0.102
port: 5060
# 根据国标6.1.2中规定domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码由省级、市级、区级、基层编号组成参照GB/T 2260-2007
# 后两位为行业编码定义参照附录D.3
@ -34,7 +35,8 @@ sip:
domain: 3701020049
id: 37010200492000000001
# 默认设备认证密码,后续扩展使用设备单独密码
password: admin
password: admin123
media:
ip: 10.200.64.88
# ip: 10.200.64.88
ip: 192.168.0.102
port: 10000

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 169 KiB