Browse Source

初始提交

pull/1/head
songww 4 years ago
commit
70091f29f2
  1. 173
      pom.xml
  2. 20
      src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
  3. 22
      src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
  4. 79
      src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
  5. 83
      src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
  6. 25
      src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java
  7. 223
      src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
  8. 211
      src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
  9. 126
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
  10. 110
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java
  11. 309
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java
  12. 30
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/Host.java
  13. 34
      src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
  14. 42
      src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java
  15. 66
      src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java
  16. 44
      src/main/java/com/genersoft/iot/vmp/gb28181/event/outline/KeepliveTimeoutListener.java
  17. 41
      src/main/java/com/genersoft/iot/vmp/gb28181/event/outline/OutlineEvent.java
  18. 58
      src/main/java/com/genersoft/iot/vmp/gb28181/event/outline/OutlineEventListener.java
  19. 110
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
  20. 183
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
  21. 111
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
  22. 48
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/PtzCmdHelper.java
  23. 387
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
  24. 17
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/ISIPRequestProcessor.java
  25. 51
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java
  26. 33
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java
  27. 33
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/CancelRequestProcessor.java
  28. 77
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
  29. 239
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
  30. 32
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java
  31. 163
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java
  32. 64
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/SubscribeRequestProcessor.java
  33. 17
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/ISIPResponseProcessor.java
  34. 33
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/ByeResponseProcessor.java
  35. 33
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/CancelResponseProcessor.java
  36. 40
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java
  37. 32
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/OtherResponseProcessor.java
  38. 121
      src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
  39. 77
      src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
  40. 36
      src/main/java/com/genersoft/iot/vmp/storager/VideoManagerStoragerFactory.java
  41. 116
      src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java
  42. 129
      src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java
  43. 43
      src/main/java/com/genersoft/iot/vmp/utils/redis/FastJsonRedisSerializer.java
  44. 557
      src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java
  45. 50
      src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java
  46. 41
      src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
  47. 45
      src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java
  48. 36
      src/main/resources/application.yml

173
pom.xml

@ -0,0 +1,173 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
</parent>
<groupId>com.genersoft</groupId>
<artifactId>wvp</artifactId>
<name>web video platform</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hystrix.version>1.5.12</hystrix.version>
<!-- 依赖版本 -->
<mapper.version>3.4.0</mapper.version>
<mybatis.version>3.3.1</mybatis.version>
<mybatis.spring.version>1.2.4</mybatis.spring.version>
<pagehelper.version>4.1.1</pagehelper.version>
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
<asciidoctor.input.directory>${project.basedir}/docs/asciidoc</asciidoctor.input.directory>
<generated.asciidoc.directory>${project.build.directory}/asciidoc</generated.asciidoc.directory>
<asciidoctor.html.output.directory>${project.build.directory}/asciidoc/html</asciidoctor.html.output.directory>
<asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>
<gson.version>2.8.0</gson.version>
<jedis.version>3.0.1</jedis.version>
<jsonlib.version>2.4</jsonlib.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.11</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<!--Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.spring.version}</version>
</dependency>
<!--分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${pagehelper.version}</version>
</dependency>
<!--通用Mapper -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>${mapper.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.33</version>
</dependency>
<!--Swagger2 -->
<!--在线文档 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
<!-- 日志相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>javax.sip</groupId>
<artifactId>jain-sip-ri</artifactId>
<version>1.3.0-91</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.4.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

20
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java

@ -0,0 +1,20 @@
package com.genersoft.iot.vmp;
import java.util.logging.LogManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
//@EnableEurekaClient
//@EnableTransactionManagement
//@EnableFeignClients(basePackages = { "com.genersoft.iot.vmp", "org.integrain" })
//@ServletComponentScan("com.genersoft.iot.vmp")
@EnableAutoConfiguration
public class VManageBootstrap extends LogManager {
public static void main(String[] args) {
SpringApplication.run(VManageBootstrap.class, args);
}
}

22
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java

@ -0,0 +1,22 @@
package com.genersoft.iot.vmp.common;
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: songww
* @date: 2019年5月30日 下午3:04:04
*
*/
public class VideoManagerConstants {
public static final String CACHEKEY_PREFIX = "VMP_deviceId_";
public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_";
public static final String EVENT_ONLINE_REGISTER = "1";
public static final String EVENT_ONLINE_KEEPLIVE = "2";
public static final String EVENT_OUTLINE_UNREGISTER = "1";
public static final String EVENT_OUTLINE_TIMEOUT = "2";
}

79
src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java

@ -0,0 +1,79 @@
package com.genersoft.iot.vmp.conf;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.alibaba.fastjson.parser.ParserConfig;
import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer;
/**
* @Description:Redis中间件配置类
* @author: songww
* @date: 2019年5月30日 上午10:58:25
*
*/
@Configuration
// @EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Bean("redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<Object>(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(serializer);
template.setHashValueSerializer(serializer);
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
/**
* redis消息监听器容器 可以添加多个监听不同话题的redis监听器只需要把消息监听器和相应的消息订阅处理器绑定该消息监听器
* 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
*
* @param connectionFactory
* @param listenerAdapter
* @return
*/
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
// @Bean
// RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
// MessageListenerAdapter listenerAdapter) {
//
// RedisMessageListenerContainer container = new RedisMessageListenerContainer();
// container.setConnectionFactory(connectionFactory);
// // 订阅了一个叫通道
// container.addMessageListener(listenerAdapter, new PatternTopic(VideoManagerConstants.KEEPLIVEKEY_PREFIX+"*"));
// // 这个container 可以添加多个 messageListener
// return container;
// }
// /**
// * 消息监听器适配器,绑定消息处理器,利用反射技术调用消息处理器的业务方法
// * @param receiver
// * @return
// */
// @Bean
// MessageListenerAdapter listenerAdapter(MessageReceiver receiver) {
// //这个地方 是给messageListenerAdapter 传入一个消息接受的处理器,利用反射的方法调用“receiveMessage”
// //也有好几个重载方法,这边默认调用处理器的方法 叫handleMessage 可以自己到源码里面看
// return new MessageListenerAdapter(receiver, "receiveMessage");
// }
}

83
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java

@ -0,0 +1,83 @@
package com.genersoft.iot.vmp.conf;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SipConfig {
@Value("${sip.ip}")
String sipIp;
@Value("${sip.port}")
Integer sipPort;
@Value("${sip.domain}")
String sipDomain;
@Value("${sip.password}")
String sipPassword;
@Value("${media.ip}")
String mediaIp;
@Value("${media.port}")
Integer mediaPort;
@Value("${sip.ptz.speed:50}")
Integer speed;
public String getSipIp() {
return sipIp;
}
public void setSipIp(String sipIp) {
this.sipIp = sipIp;
}
public Integer getSipPort() {
return sipPort;
}
public void setSipPort(Integer sipPort) {
this.sipPort = sipPort;
}
public String getSipDomain() {
return sipDomain;
}
public void setSipDomain(String sipDomain) {
this.sipDomain = sipDomain;
}
public String getSipPassword() {
return sipPassword;
}
public void setSipPassword(String sipPassword) {
this.sipPassword = sipPassword;
}
public String getMediaIp() {
return mediaIp;
}
public void setMediaIp(String mediaIp) {
this.mediaIp = mediaIp;
}
public Integer getMediaPort() {
return mediaPort;
}
public void setMediaPort(Integer mediaPort) {
this.mediaPort = mediaPort;
}
public Integer getSpeed() {
return speed;
}
public void setSpeed(Integer speed) {
this.speed = speed;
}
}

25
src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java

@ -0,0 +1,25 @@
package com.genersoft.iot.vmp.conf;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: songww
* @date: 2020年5月6日 下午2:46:00
*/
@Configuration("vmConfig")
public class VManagerConfig {
@Value("${spring.application.database:redis}")
private String database;
public String getDatabase() {
return database;
}
public void setDatabase(String database) {
this.database = database;
}
}

223
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java

@ -0,0 +1,223 @@
package com.genersoft.iot.vmp.gb28181;
import java.util.Properties;
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.context.annotation.Bean;
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{
private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);
@Autowired
private SipConfig config;
private SipProvider tcpSipProvider;
private SipProvider udpSipProvider;
@Autowired
private SIPProcessorFactory processorFactory;
private SipStack sipStack;
private AddressFactory addressFactory;
private HeaderFactory headerFactory;
private MessageFactory messageFactory;
@Bean
private boolean initSipServer() throws Exception {
SipFactory sipFactory = SipFactory.getInstance();
sipFactory.setPathName("gov.nist");
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", config.getSipIp());
/**
* 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", "16");
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);
try {
startTcpListener();
startUdpListener();
} catch (Exception e) {
logger.error("Sip Server 启动失败! port {"+config.getSipPort()+"}");
e.printStackTrace();
throw e;
}
logger.info("Sip Server 启动成功 port {"+config.getSipPort()+"}");
return true;
}
private void startTcpListener() throws Exception {
ListeningPoint tcpListeningPoint = sipStack.createListeningPoint(config.getSipIp(), config.getSipPort(), "TCP");
tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint);
tcpSipProvider.addSipListener(this);
}
private void startUdpListener() throws Exception {
ListeningPoint udpListeningPoint = sipStack.createListeningPoint(config.getSipIp(), config.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, getServerTransaction(evt));
}
@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,config);
} 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
}
private 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;
}
}

211
src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java

@ -0,0 +1,211 @@
/*
* Conditions Of Use
*
* This software was developed by employees of the National Institute of
* Standards and Technology (NIST), an agency of the Federal Government.
* Pursuant to title 15 Untied States Code Section 105, works of NIST
* employees are not subject to copyright protection in the United States
* and are considered to be in the public domain. As a result, a formal
* license is not needed to use the software.
*
* This software is provided by NIST as a service and is expressly
* provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
* AND DATA ACCURACY. NIST does not warrant or make any representations
* regarding the use of the software or the results thereof, including but
* not limited to the correctness, accuracy, reliability or usefulness of
* the software.
*
* Permission to use this software is contingent upon your acceptance
* of the terms of this agreement
*
* .
*
*/
package com.genersoft.iot.vmp.gb28181.auth;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Random;
import javax.sip.address.URI;
import javax.sip.header.AuthorizationHeader;
import javax.sip.header.HeaderFactory;
import javax.sip.header.WWWAuthenticateHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import gov.nist.core.InternalErrorHandler;
/**
* Implements the HTTP digest authentication method server side functionality.
*
* @author M. Ranganathan
* @author Marc Bednarek
*/
public class DigestServerAuthenticationHelper {
private MessageDigest messageDigest;
public static final String DEFAULT_ALGORITHM = "MD5";
public static final String DEFAULT_SCHEME = "Digest";
/** to hex converter */
private static final char[] toHex = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
/**
* Default constructor.
* @throws NoSuchAlgorithmException
*/
public DigestServerAuthenticationHelper()
throws NoSuchAlgorithmException {
messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
}
public static String toHexString(byte b[]) {
int pos = 0;
char[] c = new char[b.length * 2];
for (int i = 0; i < b.length; i++) {
c[pos++] = toHex[(b[i] >> 4) & 0x0F];
c[pos++] = toHex[b[i] & 0x0f];
}
return new String(c);
}
/**
* Generate the challenge string.
*
* @return a generated nonce.
*/
private String generateNonce() {
// Get the time of day and run MD5 over it.
Date date = new Date();
long time = date.getTime();
Random rand = new Random();
long pad = rand.nextLong();
String nonceString = (new Long(time)).toString()
+ (new Long(pad)).toString();
byte mdbytes[] = messageDigest.digest(nonceString.getBytes());
// Convert the mdbytes array into a hex string.
return toHexString(mdbytes);
}
public Response generateChallenge(HeaderFactory headerFactory, Response response, String realm) {
try {
WWWAuthenticateHeader proxyAuthenticate = headerFactory
.createWWWAuthenticateHeader(DEFAULT_SCHEME);
proxyAuthenticate.setParameter("realm", realm);
proxyAuthenticate.setParameter("nonce", generateNonce());
proxyAuthenticate.setParameter("opaque", "");
proxyAuthenticate.setParameter("stale", "FALSE");
proxyAuthenticate.setParameter("algorithm", DEFAULT_ALGORITHM);
response.setHeader(proxyAuthenticate);
} catch (Exception ex) {
InternalErrorHandler.handleException(ex);
}
return response;
}
/**
* Authenticate the inbound request.
*
* @param request - the request to authenticate.
* @param hashedPassword -- the MD5 hashed string of username:realm:plaintext password.
*
* @return true if authentication succeded and false otherwise.
*/
public boolean doAuthenticateHashedPassword(Request request, String hashedPassword) {
AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
if ( authHeader == null ) return false;
String realm = authHeader.getRealm();
String username = authHeader.getUsername();
if ( username == null || realm == null ) {
return false;
}
String nonce = authHeader.getNonce();
URI uri = authHeader.getURI();
if (uri == null) {
return false;
}
String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
String HA1 = hashedPassword;
byte[] mdbytes = messageDigest.digest(A2.getBytes());
String HA2 = toHexString(mdbytes);
String cnonce = authHeader.getCNonce();
String KD = HA1 + ":" + nonce;
if (cnonce != null) {
KD += ":" + cnonce;
}
KD += ":" + HA2;
mdbytes = messageDigest.digest(KD.getBytes());
String mdString = toHexString(mdbytes);
String response = authHeader.getResponse();
return mdString.equals(response);
}
/**
* Authenticate the inbound request given plain text password.
*
* @param request - the request to authenticate.
* @param pass -- the plain text password.
*
* @return true if authentication succeded and false otherwise.
*/
public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
if ( authHeader == null ) return false;
String realm = authHeader.getRealm();
String username = authHeader.getUsername();
if ( username == null || realm == null ) {
return false;
}
String nonce = authHeader.getNonce();
URI uri = authHeader.getURI();
if (uri == null) {
return false;
}
String A1 = username + ":" + realm + ":" + pass;
String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
byte mdbytes[] = messageDigest.digest(A1.getBytes());
String HA1 = toHexString(mdbytes);
mdbytes = messageDigest.digest(A2.getBytes());
String HA2 = toHexString(mdbytes);
String cnonce = authHeader.getCNonce();
String KD = HA1 + ":" + nonce;
if (cnonce != null) {
KD += ":" + cnonce;
}
KD += ":" + HA2;
mdbytes = messageDigest.digest(KD.getBytes());
String mdString = toHexString(mdbytes);
String response = authHeader.getResponse();
return mdString.equals(response);
}
}

126
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java

@ -0,0 +1,126 @@
package com.genersoft.iot.vmp.gb28181.bean;
import java.util.Map;
public class Device {
/**
* 设备Id
*/
private String deviceId;
/**
* 设备名
*/
private String name;
/**
* 生产厂商
*/
private String manufacturer;
/**
* 型号
*/
private String model;
/**
* 固件版本
*/
private String firmware;
/**
* 传输协议
* UDP/TCP
*/
private String transport;
/**
* wan地址
*/
private Host host;
/**
* 在线
*/
private int online;
/**
* 通道列表
*/
private Map<String,DeviceChannel> channelMap;
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTransport() {
return transport;
}
public void setTransport(String transport) {
this.transport = transport;
}
public Host getHost() {
return host;
}
public void setHost(Host host) {
this.host = host;
}
public Map<String, DeviceChannel> getChannelMap() {
return channelMap;
}
public void setChannelMap(Map<String, DeviceChannel> channelMap) {
this.channelMap = channelMap;
}
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getFirmware() {
return firmware;
}
public void setFirmware(String firmware) {
this.firmware = firmware;
}
public int getOnline() {
return online;
}
public void setOnline(int online) {
this.online = online;
}
}

110
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java

@ -0,0 +1,110 @@
package com.genersoft.iot.vmp.gb28181.bean;
public class DeviceAlarm {
/**
* 设备Id
*/
private String deviceId;
/**
* 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情-
*/
private String alarmPriorit;
/**
* 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警,
* 7其他报警;可以为直接组合如12为电话报警或 设备报警-
*/
private String alarmMethod;
/**
* 报警时间
*/
private String alarmTime;
/**
* 报警内容描述
*/
private String alarmDescription;
/**
* 经度
*/
private double longitude;
/**
* 纬度
*/
private double latitude;
/**
* 报警类型
*/
private String alarmType;
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getAlarmPriorit() {
return alarmPriorit;
}
public void setAlarmPriorit(String alarmPriorit) {
this.alarmPriorit = alarmPriorit;
}
public String getAlarmMethod() {
return alarmMethod;
}
public void setAlarmMethod(String alarmMethod) {
this.alarmMethod = alarmMethod;
}
public String getAlarmTime() {
return alarmTime;
}
public void setAlarmTime(String alarmTime) {
this.alarmTime = alarmTime;
}
public String getAlarmDescription() {
return alarmDescription;
}
public void setAlarmDescription(String alarmDescription) {
this.alarmDescription = alarmDescription;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public String getAlarmType() {
return alarmType;
}
public void setAlarmType(String alarmType) {
this.alarmType = alarmType;
}
}

309
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java

@ -0,0 +1,309 @@
package com.genersoft.iot.vmp.gb28181.bean;
public class DeviceChannel {
/**
* 通道id
*/
private String channelId;
/**
* 通道名
*/
private String name;
/**
* 生产厂商
*/
private String manufacture;
/**
* 型号
*/
private String model;
/**
* 设备归属
*/
private String owner;
/**
* 行政区域
*/
private String civilCode;
/**
* 警区
*/
private String block;
/**
* 安装地址
*/
private String address;
/**
* 是否有子设备 1有, 0没有
*/
private int parental;
/**
* 父级id
*/
private String parentId;
/**
* 信令安全模式 缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式
*/
private int safetyWay;
/**
* 注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 ; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式
*/
private int registerWay;
/**
* 证书序列号
*/
private String certNum;
/**
* 证书有效标识 缺省为0;证书有效标识:0:无效1: 有效
*/
private int certifiable;
/**
* 证书无效原因码
*/
private int errCode;
/**
* 证书终止有效期
*/
private String endTime;
/**
* 保密属性 缺省为0; 0:不涉密, 1:涉密
*/
private String secrecy;
/**
* IP地址
*/
private String ipAddress;
/**
* 端口号
*/
private int port;
/**
* 密码
*/
private String password;
/**
* 在线/离线
* 1在线,0离线
* 默认在线
* 信令:
* <Status>ON</Status>
* <Status>OFF</Status>
* 遇到过NVR下的IPC下发信令可以推流 但是 Status 响应 OFF
*/
private int status;
/**
* 经度
*/
private double longitude;
/**
* 纬度
*/
private double latitude;
public String getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getManufacture() {
return manufacture;
}
public void setManufacture(String manufacture) {
this.manufacture = manufacture;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getCivilCode() {
return civilCode;
}
public void setCivilCode(String civilCode) {
this.civilCode = civilCode;
}
public String getBlock() {
return block;
}
public void setBlock(String block) {
this.block = block;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getParental() {
return parental;
}
public void setParental(int parental) {
this.parental = parental;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public int getSafetyWay() {
return safetyWay;
}
public void setSafetyWay(int safetyWay) {
this.safetyWay = safetyWay;
}
public int getRegisterWay() {
return registerWay;
}
public void setRegisterWay(int registerWay) {
this.registerWay = registerWay;
}
public String getCertNum() {
return certNum;
}
public void setCertNum(String certNum) {
this.certNum = certNum;
}
public int getCertifiable() {
return certifiable;
}
public void setCertifiable(int certifiable) {
this.certifiable = certifiable;
}
public int getErrCode() {
return errCode;
}
public void setErrCode(int errCode) {
this.errCode = errCode;
}
public String getEndTime() {
return endTime;
}
public void setEndTime(String endTime) {
this.endTime = endTime;
}
public String getSecrecy() {
return secrecy;
}
public void setSecrecy(String secrecy) {
this.secrecy = secrecy;
}
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
}

30
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Host.java

@ -0,0 +1,30 @@
package com.genersoft.iot.vmp.gb28181.bean;
public class Host {
private String ip;
private int port;
private String address;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}

34
src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java

@ -0,0 +1,34 @@
package com.genersoft.iot.vmp.gb28181.event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent;
import com.genersoft.iot.vmp.gb28181.event.outline.OutlineEvent;
/**
* @Description:Event事件通知推送器支持推送在线事件离线事件
* @author: songww
* @date: 2020年5月6日 上午11:30:50
*/
@Component
public class EventPublisher {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void onlineEventPublish(String deviceId, String from) {
OnlineEvent onEvent = new OnlineEvent(this);
onEvent.setDeviceId(deviceId);
onEvent.setFrom(from);
applicationEventPublisher.publishEvent(onEvent);
}
public void outlineEventPublish(String deviceId, String from){
OutlineEvent outEvent = new OutlineEvent(this);
outEvent.setDeviceId(deviceId);
outEvent.setFrom(from);
applicationEventPublisher.publishEvent(outEvent);
}
}

42
src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java

@ -0,0 +1,42 @@
package com.genersoft.iot.vmp.gb28181.event.online;
import org.springframework.context.ApplicationEvent;
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: songww
* @date: 2020年5月6日 上午11:32:56
*/
public class OnlineEvent extends ApplicationEvent {
/**
* @Title: OnlineEvent
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param: @param source
* @throws
*/
public OnlineEvent(Object source) {
super(source);
}
private String deviceId;
private String from;
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
}

66
src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java

@ -0,0 +1,66 @@
package com.genersoft.iot.vmp.gb28181.event.online;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
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: songww
* @date: 2020年5月6日 下午1:51:23
*/
@Component
public class OnlineEventListener implements ApplicationListener<OnlineEvent> {
private final static Logger logger = LoggerFactory.getLogger(OnlineEventListener.class);
@Autowired
private IVideoManagerStorager storager;
@Autowired
private RedisUtil redis;
@Override
public void onApplicationEvent(OnlineEvent event) {
if (logger.isDebugEnabled()) {
logger.debug("设备离线事件触发,deviceId:" + event.getDeviceId() + ",from:" + event.getFrom());
}
String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + event.getDeviceId();
boolean needUpdateStorager = false;
switch (event.getFrom()) {
// 注册时触发的在线事件,先在redis中增加超时超时监听
case VideoManagerConstants.EVENT_ONLINE_REGISTER:
// TODO 超时时间暂时写死为180秒
redis.set(key, event.getDeviceId(), 180);
needUpdateStorager = true;
break;
// 设备主动发送心跳触发的离线事件
case VideoManagerConstants.EVENT_ONLINE_KEEPLIVE:
boolean exist = redis.hasKey(key);
// 先判断是否还存在,当设备先心跳超时后又发送心跳时,redis没有监听,需要增加
if (!exist) {
needUpdateStorager = true;
redis.set(key, event.getDeviceId(), 180);
} else {
redis.expire(key, 180);
}
break;
}
if (needUpdateStorager) {
// 处理离线监听
storager.online(event.getDeviceId());
}
}
}

44
src/main/java/com/genersoft/iot/vmp/gb28181/event/outline/KeepliveTimeoutListener.java

@ -0,0 +1,44 @@
package com.genersoft.iot.vmp.gb28181.event.outline;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
/**
* @Description:设备心跳超时监听,借助redis过期特性进行监听监听到说明设备心跳超时发送离线事件
* @author: songww
* @date: 2020年5月6日 上午11:35:46
*/
@Component
public class KeepliveTimeoutListener extends KeyExpirationEventMessageListener {
@Autowired
private EventPublisher publisher;
public KeepliveTimeoutListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
/**
* 监听失效的keykey格式为keeplive_deviceId
* @param message
* @param pattern
*/
@Override
public void onMessage(Message message, byte[] pattern) {
// 获取失效的key
String expiredKey = message.toString();
if(!expiredKey.startsWith(VideoManagerConstants.KEEPLIVEKEY_PREFIX)){
System.out.println("收到redis过期监听,但开头不是"+VideoManagerConstants.KEEPLIVEKEY_PREFIX+",忽略");
return;
}
String deviceId = expiredKey.substring(VideoManagerConstants.KEEPLIVEKEY_PREFIX.length(),expiredKey.length());
publisher.outlineEventPublish(deviceId, VideoManagerConstants.EVENT_OUTLINE_TIMEOUT);
}
}

41
src/main/java/com/genersoft/iot/vmp/gb28181/event/outline/OutlineEvent.java

@ -0,0 +1,41 @@
package com.genersoft.iot.vmp.gb28181.event.outline;
import org.springframework.context.ApplicationEvent;
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: songww
* @date: 2020年5月6日 上午11:33:13
*/
public class OutlineEvent extends ApplicationEvent {
/**
* @Title: OutlineEvent
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param: @param source
* @throws
*/
public OutlineEvent(Object source) {
super(source);
}
private String deviceId;
private String from;
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
}

58
src/main/java/com/genersoft/iot/vmp/gb28181/event/outline/OutlineEventListener.java

@ -0,0 +1,58 @@
package com.genersoft.iot.vmp.gb28181.event.outline;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
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.outline.OutlineEventListener}
* @author: songww
* @date: 2020年5月6日 下午1:51:23
*/
@Component
public class OutlineEventListener implements ApplicationListener<OutlineEvent> {
private final static Logger logger = LoggerFactory.getLogger(OutlineEventListener.class);
@Autowired
private IVideoManagerStorager storager;
@Autowired
private RedisUtil redis;
@Override
public void onApplicationEvent(OutlineEvent event) {
if (logger.isDebugEnabled()) {
logger.debug("设备离线事件触发,deviceId:" + event.getDeviceId() + ",from:" + event.getFrom());
}
String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + event.getDeviceId();
switch (event.getFrom()) {
// 心跳超时触发的离线事件,说明redis中已删除,无需处理
case VideoManagerConstants.EVENT_OUTLINE_TIMEOUT:
break;
// 设备主动注销触发的离线事件,需要删除redis中的超时监听
case VideoManagerConstants.EVENT_OUTLINE_UNREGISTER:
redis.del(key);
break;
default:
boolean exist = redis.hasKey(key);
if (exist) {
redis.del(key);
}
}
// 处理离线监听
storager.outline(event.getDeviceId());
}
}

110
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java

@ -0,0 +1,110 @@
package com.genersoft.iot.vmp.gb28181.transmit;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.header.CSeqHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.AckRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.ByeRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.CancelRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.InviteRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.OtherRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.SubscribeRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.ByeResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.CancelResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.InviteResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.OtherResponseProcessor;
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: songww
* @date: 2020年5月3日 下午4:24:37
*/
@Component
public class SIPProcessorFactory {
@Autowired
private InviteRequestProcessor inviteRequestProcessor;
@Autowired
private RegisterRequestProcessor registerRequestProcessor;
@Autowired
private SubscribeRequestProcessor subscribeRequestProcessor;
@Autowired
private AckRequestProcessor ackRequestProcessor;
@Autowired
private ByeRequestProcessor byeRequestProcessor;
@Autowired
private CancelRequestProcessor cancelRequestProcessor;
@Autowired
private MessageRequestProcessor messageRequestProcessor;
@Autowired
private OtherRequestProcessor otherRequestProcessor;
@Autowired
private InviteResponseProcessor inviteResponseProcessor;
@Autowired
private ByeResponseProcessor byeResponseProcessor;
@Autowired
private CancelResponseProcessor cancelResponseProcessor;
@Autowired
private OtherResponseProcessor otherResponseProcessor;
public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) {
Request request = evt.getRequest();
String method = request.getMethod();
if (Request.INVITE.equals(method)) {
return inviteRequestProcessor;
} else if (Request.REGISTER.equals(method)) {
return registerRequestProcessor;
} else if (Request.SUBSCRIBE.equals(method)) {
return subscribeRequestProcessor;
} else if (Request.ACK.equals(method)) {
return ackRequestProcessor;
} else if (Request.BYE.equals(method)) {
return byeRequestProcessor;
} else if (Request.CANCEL.equals(method)) {
return cancelRequestProcessor;
} else if (Request.MESSAGE.equals(method)) {
return messageRequestProcessor;
} else {
return otherRequestProcessor;
}
}
public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) {
Response response = evt.getResponse();
CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
String method = cseqHeader.getMethod();
if(Request.INVITE.equals(method)){
return inviteResponseProcessor;
} else if (Request.BYE.equals(method)) {
return byeResponseProcessor;
} else if (Request.CANCEL.equals(method)) {
return cancelResponseProcessor;
} else {
return otherResponseProcessor;
}
}
}

183
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java

@ -0,0 +1,183 @@
package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import com.genersoft.iot.vmp.gb28181.bean.Device;
/**
* @Description:设备能力接口用于定义设备的控制查询能力
* @author: songww
* @date: 2020年5月3日 下午9:16:34
*/
public interface ISIPCommander {
/**
* 云台方向放控制使用配置文件中的默认镜头移动速度
*
* @param deviceId 控制设备
* @param channelId 预览通道
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param moveSpeed 镜头移动速度
*/
public boolean ptzdirectCmd(String deviceId,String channelId,int leftRight, int upDown);
/**
* 云台方向放控制
*
* @param deviceId 控制设备
* @param channelId 预览通道
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param moveSpeed 镜头移动速度
*/
public boolean ptzdirectCmd(String deviceId,String channelId,int leftRight, int upDown, int moveSpeed);
/**
* 云台缩放控制使用配置文件中的默认镜头缩放速度
*
* @param deviceId 控制设备
* @param channelId 预览通道
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
*/
public boolean ptzZoomCmd(String deviceId,String channelId,int inOut);
/**
* 云台缩放控制
*
* @param deviceId 控制设备
* @param channelId 预览通道
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
* @param zoomSpeed 镜头缩放速度
*/
public boolean ptzZoomCmd(String deviceId,String channelId,int inOut, int moveSpeed);
/**
* 云台控制支持方向与缩放控制
*
* @param deviceId 控制设备
* @param channelId 预览通道
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
* @param moveSpeed 镜头移动速度
* @param zoomSpeed 镜头缩放速度
*/
public boolean ptzCmd(String deviceId,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed);
/**
* 请求预览视频流
*
* @param deviceId 视频设备
* @param channelId 预览通道
*/
public String playStreamCmd(String deviceId,String channelId);
/**
* 语音广播
*
* @param deviceId 视频设备
* @param channelId 预览通道
*/
public String audioBroadcastCmd(String deviceId,String channelId);
/**
* 音视频录像控制
*
* @param deviceId 视频设备
* @param channelId 预览通道
*/
public String recordCmd(String deviceId,String channelId);
/**
* 报警布防/撤防命令
*
* @param deviceId 视频设备
*/
public String guardCmd(String deviceId);
/**
* 报警复位命令
*
* @param deviceId 视频设备
*/
public String alarmCmd(String deviceId);
/**
* 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
*
* @param deviceId 视频设备
* @param channelId 预览通道
*/
public String iFameCmd(String deviceId,String channelId);
/**
* 看守位控制命令
*
* @param deviceId 视频设备
*/
public String homePositionCmd(String deviceId);
/**
* 设备配置命令
*
* @param deviceId 视频设备
*/
public String deviceConfigCmd(String deviceId);
/**
* 查询设备状态
*
* @param device 视频设备
*/
public boolean deviceStatusQuery(Device device);
/**
* 查询设备信息
*
* @param device 视频设备
* @return
*/
public boolean deviceInfoQuery(Device device);
/**
* 查询目录列表
*
* @param device 视频设备
*/
public boolean catalogQuery(Device device);
/**
* 查询录像信息
*
* @param device 视频设备
*/
public boolean recordInfoQuery(Device device);
/**
* 查询报警信息
*
* @param device 视频设备
*/
public boolean alarmInfoQuery(Device device);
/**
* 查询设备配置
*
* @param device 视频设备
*/
public boolean configQuery(Device device);
/**
* 查询设备预置位置
*
* @param device 视频设备
*/
public boolean presetQuery(Device device);
/**
* 查询移动设备位置数据
*
* @param device 视频设备
*/
public boolean mobilePostitionQuery(Device device);
}

111
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java

@ -0,0 +1,111 @@
package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import java.text.ParseException;
import java.util.ArrayList;
import javax.sip.InvalidArgumentException;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.CSeqHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;
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.SipLayer;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.Host;
/**
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: songww
* @date: 2020年5月6日 上午9:29:02
*/
@Component
public class SIPRequestHeaderProvider {
@Autowired
private SipLayer layer;
@Autowired
private SipConfig config;
public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException {
Request request = null;
Host host = device.getHost();
// sipuri
SipURI requestURI = layer.getAddressFactory().createSipURI(device.getDeviceId(), host.getAddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(config.getSipIp(), config.getSipPort(),
device.getTransport(), viaTag);
viaHeaders.add(viaHeader);
// from
SipURI fromSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),
config.getSipIp() + ":" + config.getSipPort());
Address fromAddress = layer.getAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = layer.getHeaderFactory().createFromHeader(fromAddress, fromTag);
// to
SipURI toSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(), host.getAddress());
Address toAddress = layer.getAddressFactory().createAddress(toSipURI);
ToHeader toHeader = layer.getHeaderFactory().createToHeader(toAddress, toTag);
// callid
CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? layer.getTcpSipProvider().getNewCallId()
: layer.getUdpSipProvider().getNewCallId();
// Forwards
MaxForwardsHeader maxForwards = layer.getHeaderFactory().createMaxForwardsHeader(70);
// ceq
CSeqHeader cSeqHeader = layer.getHeaderFactory().createCSeqHeader(1L, Request.MESSAGE);
request = layer.getMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
toHeader, viaHeaders, maxForwards);
ContentTypeHeader contentTypeHeader = layer.getHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
request.setContent(content, contentTypeHeader);
return request;
}
public Request createInviteRequest(Device device, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException {
Request request = null;
Host host = device.getHost();
//请求行
SipURI requestLine = layer.getAddressFactory().createSipURI(device.getDeviceId(), host.getAddress());
//via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(config.getSipIp(), config.getSipPort(), device.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
//from
SipURI fromSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),config.getSipIp()+":"+config.getSipPort());
Address fromAddress = layer.getAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = layer.getHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
//to
SipURI toSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),host.getAddress());
Address toAddress = layer.getAddressFactory().createAddress(toSipURI);
ToHeader toHeader = layer.getHeaderFactory().createToHeader(toAddress,null);
//callid
CallIdHeader callIdHeader = null;
if(device.getTransport().equals("TCP")) {
callIdHeader = layer.getTcpSipProvider().getNewCallId();
}
if(device.getTransport().equals("UDP")) {
callIdHeader = layer.getUdpSipProvider().getNewCallId();
}
//Forwards
MaxForwardsHeader maxForwards = layer.getHeaderFactory().createMaxForwardsHeader(70);
//ceq
CSeqHeader cSeqHeader = layer.getHeaderFactory().createCSeqHeader(1L, Request.INVITE);
request = layer.getMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
ContentTypeHeader contentTypeHeader = layer.getHeaderFactory().createContentTypeHeader("Application", "SDP");
request.setContent(content, contentTypeHeader);
return request;
}
}

48
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/PtzCmdHelper.java

@ -0,0 +1,48 @@
package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
public class PtzCmdHelper {
/**
*
* @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)
* @return
*/
//云台控制发送了消息,相机会一直执行,直到其他命令或者发送了停止命令,切记要考虑这个机制
public static String create(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; // 缩小
char[] szCmd = new char[16];
String strTmp;
szCmd[0] = 'A'; //字节1 A5
szCmd[1] = '5';
szCmd[2] = '0'; //字节2 0F
szCmd[3] = 'F';
szCmd[4] = '0'; //字节3 地址的低8位
szCmd[5] = '1';
strTmp = String.format("%02X", cmdCode);
szCmd[6] = strTmp.charAt(0); //字节4 控制码
szCmd[7] = strTmp.charAt(1);
strTmp = String.format("%02X", moveSpeed);
szCmd[8] = strTmp.charAt(0); //字节5 水平控制速度
szCmd[9] = strTmp.charAt(1);
szCmd[10] = strTmp.charAt(0); //字节6 垂直控制速度
szCmd[11] = strTmp.charAt(1);
strTmp = String.format("%X", zoomSpeed);
szCmd[12] = strTmp.charAt(0); //字节7高4位 缩放控制速度
szCmd[13] = '0'; //字节7低4位 地址的高4位
//计算校验码
int nCheck = (0XA5 + 0X0F + 0X01 + cmdCode + moveSpeed + moveSpeed + (zoomSpeed << 4 & 0XF0)) % 0X100;
strTmp = String.format("%02X", nCheck);
szCmd[14] = strTmp.charAt(0); //字节8 校验码
szCmd[15] = strTmp.charAt(1);
return String.valueOf(szCmd);
}
}

387
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java

@ -0,0 +1,387 @@
package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
import java.text.ParseException;
import java.util.Random;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import javax.sip.message.Request;
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.SipLayer;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
/**
* @Description:设备能力接口用于定义设备的控制查询能力
* @author: songww
* @date: 2020年5月3日 下午9:22:48
*/
@Component
public class SIPCommander implements ISIPCommander {
@Autowired
private SipConfig config;
@Autowired
private SIPRequestHeaderProvider headerProvider;
@Autowired
private SipLayer sipLayer;
@Autowired
private IVideoManagerStorager storager;
/**
* 云台方向放控制使用配置文件中的默认镜头移动速度
*
* @param deviceId 控制设备
* @param channelId 预览通道
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param moveSpeed 镜头移动速度
*/
@Override
public boolean ptzdirectCmd(String deviceId, String channelId, int leftRight, int upDown) {
return ptzCmd(deviceId, channelId, leftRight, upDown, 0, config.getSpeed(), 0);
}
/**
* 云台方向放控制
*
* @param deviceId 控制设备
* @param channelId 预览通道
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param moveSpeed 镜头移动速度
*/
@Override
public boolean ptzdirectCmd(String deviceId, String channelId, int leftRight, int upDown, int moveSpeed) {
return ptzCmd(deviceId, channelId, leftRight, upDown, 0, moveSpeed, 0);
}
/**
* 云台缩放控制使用配置文件中的默认镜头缩放速度
*
* @param deviceId 控制设备
* @param channelId 预览通道
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
*/
@Override
public boolean ptzZoomCmd(String deviceId, String channelId, int inOut) {
return ptzCmd(deviceId, channelId, 0, 0, inOut, 0, config.getSpeed());
}
/**
* 云台缩放控制
*
* @param deviceId 控制设备
* @param channelId 预览通道
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
* @param zoomSpeed 镜头缩放速度
*/
@Override
public boolean ptzZoomCmd(String deviceId, String channelId, int inOut, int zoomSpeed) {
return ptzCmd(deviceId, channelId, 0, 0, inOut, 0, zoomSpeed);
}
/**
* 云台控制支持方向与缩放控制
*
* @param deviceId 控制设备
* @param channelId 预览通道
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
* @param moveSpeed 镜头移动速度
* @param zoomSpeed 镜头缩放速度
*/
@Override
public boolean ptzCmd(String deviceId, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,
int zoomSpeed) {
try {
Device device = storager.queryVideoDevice(deviceId);
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>" + PtzCmdHelper.create(leftRight, upDown, inOut, moveSpeed, zoomSpeed) + "</PTZCmd>");
ptzXml.append("<Info>");
ptzXml.append("</Info>");
ptzXml.append("</Control>");
Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtzTag", "ToPtzTag");
transmitRequest(device.getTransport(), request);
return true;
} catch (SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
}
return false;
}
/**
* 请求预览视频流
*
* @param deviceId 视频设备
* @param channelId 预览通道
*/
@Override
public String playStreamCmd(String deviceId, String channelId) {
try {
Device device = storager.queryVideoDevice(deviceId);
//生成ssrc标识数据流 10位数字
String ssrc = "";
Random random = new Random();
// ZLMediaServer最大识别7FFFFFFF即2147483647,所以随机数不能超过这个数
ssrc = String.valueOf(random.nextInt(2147483647));
//
StringBuffer content = new StringBuffer(200);
content.append("v=0\r\n");
content.append("o="+channelId+" 0 0 IN IP4 "+config.getSipIp()+"\r\n");
content.append("s=Play\r\n");
content.append("c=IN IP4 "+config.getMediaIp()+"\r\n");
content.append("t=0 0\r\n");
if(device.getTransport().equals("TCP")) {
content.append("m=video "+config.getMediaPort()+" TCP/RTP/AVP 96 98 97\r\n");
}
if(device.getTransport().equals("UDP")) {
content.append("m=video "+config.getMediaPort()+" RTP/AVP 96 98 97\r\n");
}
content.append("a=sendrecv\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
content.append("a=rtpmap:98 H264/90000\r\n");
content.append("a=rtpmap:97 MPEG4/90000\r\n");
if(device.getTransport().equals("TCP")){
content.append("a=setup:passive\r\n");
content.append("a=connection:new\r\n");
}
content.append("y="+ssrc+"\r\n");//ssrc
Request request = headerProvider.createInviteRequest(device, content.toString(), null, "live", null);
transmitRequest(device.getTransport(), request);
return ssrc;
} catch ( SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
return null;
}
}
/**
* 语音广播
*
* @param deviceId 视频设备
* @param channelId 预览通道
*/
@Override
public String audioBroadcastCmd(String deviceId, String channelId) {
// TODO Auto-generated method stub
return null;
}
/**
* 音视频录像控制
*
* @param deviceId 视频设备
* @param channelId 预览通道
*/
@Override
public String recordCmd(String deviceId, String channelId) {
// TODO Auto-generated method stub
return null;
}
/**
* 报警布防/撤防命令
*
* @param deviceId 视频设备
*/
@Override
public String guardCmd(String deviceId) {
// TODO Auto-generated method stub
return null;
}
/**
* 报警复位命令
*
* @param deviceId 视频设备
*/
@Override
public String alarmCmd(String deviceId) {
// TODO Auto-generated method stub
return null;
}
/**
* 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
*
* @param deviceId 视频设备
* @param channelId 预览通道
*/
@Override
public String iFameCmd(String deviceId, String channelId) {
// TODO Auto-generated method stub
return null;
}
/**
* 看守位控制命令
*
* @param deviceId 视频设备
*/
@Override
public String homePositionCmd(String deviceId) {
// TODO Auto-generated method stub
return null;
}
/**
* 设备配置命令
*
* @param deviceId 视频设备
*/
@Override
public String deviceConfigCmd(String deviceId) {
// TODO Auto-generated method stub
return null;
}
/**
* 查询设备状态
*
* @param device 视频设备
*/
@Override
public boolean deviceStatusQuery(Device device) {
// TODO Auto-generated method stub
return false;
}
/**
* 查询设备信息
*
* @param device 视频设备
*/
@Override
public boolean deviceInfoQuery(Device device) {
try {
StringBuffer catalogXml = new StringBuffer(200);
catalogXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>");
catalogXml.append("<Query>");
catalogXml.append("<CmdType>DeviceInfo</CmdType>");
catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>");
catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>");
catalogXml.append("</Query>");
Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaDeviceInfoBranch", "FromDeviceInfoTag", "ToDeviceInfoTag");
transmitRequest(device.getTransport(), request);
} catch (SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 查询目录列表
*
* @param device 视频设备
*/
@Override
public boolean catalogQuery(Device device) {
try {
StringBuffer catalogXml = new StringBuffer(200);
catalogXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>");
catalogXml.append("<Query>");
catalogXml.append("<CmdType>Catalog</CmdType>");
catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>");
catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>");
catalogXml.append("</Query>");
Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaCatalogBranch", "FromCatalogTag", "ToCatalogTag");
transmitRequest(device.getTransport(), request);
} catch (SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 查询录像信息
*
* @param device 视频设备
*/
@Override
public boolean recordInfoQuery(Device device) {
// TODO Auto-generated method stub
return false;
}
/**
* 查询报警信息
*
* @param device 视频设备
*/
@Override
public boolean alarmInfoQuery(Device device) {
// TODO Auto-generated method stub
return false;
}
/**
* 查询设备配置
*
* @param device 视频设备
*/
@Override
public boolean configQuery(Device device) {
// TODO Auto-generated method stub
return false;
}
/**
* 查询设备预置位置
*
* @param device 视频设备
*/
@Override
public boolean presetQuery(Device device) {
// TODO Auto-generated method stub
return false;
}
/**
* 查询移动设备位置数据
*
* @param device 视频设备
*/
@Override
public boolean mobilePostitionQuery(Device device) {
// TODO Auto-generated method stub
return false;
}
private void transmitRequest(String transport, Request request) throws SipException {
if(transport.equals("TCP")) {
sipLayer.getTcpSipProvider().sendRequest(request);
} else if(transport.equals("UDP")) {
sipLayer.getUdpSipProvider().sendRequest(request);
}
}
}

17
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/ISIPRequestProcessor.java

@ -0,0 +1,17 @@
package com.genersoft.iot.vmp.gb28181.transmit.request;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import com.genersoft.iot.vmp.gb28181.SipLayer;
/**
* @Description:处理接收IPCamera发来的SIP协议请求消息
* @author: songww
* @date: 2020年5月3日 下午4:42:22
*/
public interface ISIPRequestProcessor {
public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction);
}

51
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java

@ -0,0 +1,51 @@
package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.message.Request;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
import gov.nist.javax.sip.header.CSeq;
/**
* @Description:ACK请求处理器
* @author: songww
* @date: 2020年5月3日 下午5:31:45
*/
@Component
public class AckRequestProcessor implements ISIPRequestProcessor {
/**
* 处理 ACK请求
*
* @param evt
* @param layer
* @param transaction
* @param config
*/
@Override
public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
Request request = evt.getRequest();
Dialog dialog = evt.getDialog();
try {
Request ackRequest = null;
CSeq csReq = (CSeq) request.getHeader(CSeq.NAME);
ackRequest = dialog.createAck(csReq.getSeqNumber());
dialog.sendAck(ackRequest);
System.out.println("send ack to callee:" + ackRequest.toString());
} catch (SipException e) {
e.printStackTrace();
} catch (InvalidArgumentException e) {
e.printStackTrace();
}
}
}

33
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java

@ -0,0 +1,33 @@
package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
/**
* @Description: BYE请求处理器
* @author: songww
* @date: 2020年5月3日 下午5:32:05
*/
@Component
public class ByeRequestProcessor implements ISIPRequestProcessor {
/**
* 处理BYE请求
*
* @param evt
* @param layer
* @param transaction
* @param config
*/
@Override
public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
// TODO Auto-generated method stub
}
}

33
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/CancelRequestProcessor.java

@ -0,0 +1,33 @@
package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
/**
* @Description:CANCEL请求处理器
* @author: songww
* @date: 2020年5月3日 下午5:32:23
*/
@Component
public class CancelRequestProcessor implements ISIPRequestProcessor {
/**
* 处理CANCEL请求
*
* @param evt
* @param layer
* @param transaction
* @param config
*/
@Override
public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
// TODO Auto-generated method stub
}
}

77
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java

@ -0,0 +1,77 @@
package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
/**
* @Description:处理INVITE请求
* @author: songww
* @date: 2020年5月3日 下午4:43:52
*/
@Component
public class InviteRequestProcessor implements ISIPRequestProcessor {
/**
* 处理invite请求
*
* @param request
* 请求消息
*/
@Override
public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
// TODO Auto-generated method stub
// Request request = requestEvent.getRequest();
//
// try {
// // 发送100 Trying
// ServerTransaction serverTransaction = getServerTransaction(requestEvent);
// // 查询目标地址
// URI reqUri = request.getRequestURI();
// URI contactURI = currUser.get(reqUri);
//
// System.out.println("processInvite rqStr=" + reqUri + " contact=" + contactURI);
//
// // 根据Request uri来路由,后续的响应消息通过VIA来路由
// Request cliReq = messageFactory.createRequest(request.toString());
// cliReq.setRequestURI(contactURI);
//
// HeaderFactory headerFactory = SipFactory.getInstance().createHeaderFactory();
// Via callerVia = (Via) request.getHeader(Via.NAME);
// Via via = (Via) headerFactory.createViaHeader(SIPMain.ip, SIPMain.port, "UDP",
// callerVia.getBranch() + "sipphone");
//
// // FIXME 需要测试是否能够通过设置VIA头域来修改VIA头域值
// cliReq.removeHeader(Via.NAME);
// cliReq.addHeader(via);
//
// // 更新contact的地址
// ContactHeader contactHeader = headerFactory.createContactHeader();
// Address address = SipFactory.getInstance().createAddressFactory()
// .createAddress("sip:sipsoft@" + SIPMain.ip + ":" + SIPMain.port);
// contactHeader.setAddress(address);
// contactHeader.setExpires(3600);
// cliReq.setHeader(contactHeader);
//
// clientTransactionId = sipProvider.getNewClientTransaction(cliReq);
// clientTransactionId.sendRequest();
//
// System.out.println("processInvite clientTransactionId=" + clientTransactionId.toString());
//
// System.out.println("send invite to callee: " + cliReq);
// } catch (TransactionUnavailableException e1) {
// e1.printStackTrace();
// } catch (SipException e) {
// e.printStackTrace();
// } catch (ParseException e) {
// e.printStackTrace();
// } catch (Exception e) {
// e.printStackTrace();
// }
}
}

239
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java

@ -0,0 +1,239 @@
package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
import java.io.ByteArrayInputStream;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.message.Request;
import javax.sip.message.Response;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
/**
* @Description:MESSAGE请求处理器
* @author: songww
* @date: 2020年5月3日 下午5:32:41
*/
@Component
public class MessageRequestProcessor implements ISIPRequestProcessor {
private ServerTransaction transaction;
private SipLayer layer;
@Autowired
private SIPCommander cmder;
@Autowired
private IVideoManagerStorager storager;
@Autowired
private EventPublisher publisher;
/**
* 处理MESSAGE请求
*
* @param evt
* @param layer
* @param transaction
*/
@Override
public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
this.layer = layer;
this.transaction = transaction;
Request request = evt.getRequest();
if (new String(request.getRawContent()).contains("<CmdType>Keepalive</CmdType>")) {
processMessageKeepAlive(evt);
} else if (new String(request.getRawContent()).contains("<CmdType>Catalog</CmdType>")) {
processMessageCatalogList(evt);
} else if (new String(request.getRawContent()).contains("<CmdType>DeviceInfo</CmdType>")) {
processMessageDeviceInfo(evt);
} else if (new String(request.getRawContent()).contains("<CmdType>Alarm</CmdType>")) {
processMessageAlarm(evt);
}
}
/***
* 收到catalog设备目录列表请求 处理
* @param evt
*/
private void processMessageCatalogList(RequestEvent evt) {
try {
Request request = evt.getRequest();
SAXReader reader = new SAXReader();
reader.setEncoding("GB2312");
Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
Element rootElement = xml.getRootElement();
Element deviceIdElement = rootElement.element("DeviceID");
String deviceId = deviceIdElement.getText().toString();
Element deviceListElement = rootElement.element("DeviceList");
if (deviceListElement == null) {
return;
}
Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
if (deviceListIterator != null) {
Device device = storager.queryVideoDevice(deviceId);
if (device == null) {
return;
}
Map<String, DeviceChannel> channelMap = device.getChannelMap();
if (channelMap == null) {
channelMap = new HashMap<String, DeviceChannel>(5);
device.setChannelMap(channelMap);
}
// 遍历DeviceList
while (deviceListIterator.hasNext()) {
Element itemDevice = deviceListIterator.next();
Element channelDeviceElement = itemDevice.element("DeviceID");
if (channelDeviceElement == null) {
continue;
}
String channelDeviceId = channelDeviceElement.getText().toString();
Element channdelNameElement = itemDevice.element("Name");
String channelName = channdelNameElement != null ? channdelNameElement.getText().toString() : "";
Element statusElement = itemDevice.element("Status");
String status = statusElement != null ? statusElement.getText().toString() : "ON";
DeviceChannel deviceChannel = channelMap.containsKey(channelDeviceId) ? channelMap.get(channelDeviceId) : new DeviceChannel();
deviceChannel.setName(channelName);
deviceChannel.setChannelId(channelDeviceId);
if(status.equals("ON")) {
deviceChannel.setStatus(1);
}
if(status.equals("OFF")) {
deviceChannel.setStatus(0);
}
deviceChannel.setManufacture(XmlUtil.getText(itemDevice,"Manufacturer"));
deviceChannel.setModel(XmlUtil.getText(itemDevice,"Model"));
deviceChannel.setOwner(XmlUtil.getText(itemDevice,"Owner"));
deviceChannel.setCivilCode(XmlUtil.getText(itemDevice,"CivilCode"));
deviceChannel.setBlock(XmlUtil.getText(itemDevice,"Block"));
deviceChannel.setAddress(XmlUtil.getText(itemDevice,"Address"));
deviceChannel.setParental(itemDevice.element("Parental") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"Parental")));
deviceChannel.setParentId(XmlUtil.getText(itemDevice,"ParentId"));
deviceChannel.setSafetyWay(itemDevice.element("SafetyWay") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"SafetyWay")));
deviceChannel.setRegisterWay(itemDevice.element("RegisterWay") == null? 1:Integer.parseInt(XmlUtil.getText(itemDevice,"RegisterWay")));
deviceChannel.setCertNum(XmlUtil.getText(itemDevice,"CertNum"));
deviceChannel.setCertifiable(itemDevice.element("Certifiable") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"Certifiable")));
deviceChannel.setErrCode(itemDevice.element("ErrCode") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"ErrCode")));
deviceChannel.setEndTime(XmlUtil.getText(itemDevice,"EndTime"));
deviceChannel.setSecrecy(XmlUtil.getText(itemDevice,"Secrecy"));
deviceChannel.setIpAddress(XmlUtil.getText(itemDevice,"IPAddress"));
deviceChannel.setPort(itemDevice.element("Port") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"Port")));
deviceChannel.setPassword(XmlUtil.getText(itemDevice,"Password"));
deviceChannel.setLongitude(itemDevice.element("Longitude") == null? 0.00:Double.parseDouble(XmlUtil.getText(itemDevice,"Longitude")));
deviceChannel.setLatitude(itemDevice.element("Latitude") == null? 0.00:Double.parseDouble(XmlUtil.getText(itemDevice,"Latitude")));
channelMap.put(channelDeviceId, deviceChannel);
}
// 更新
storager.update(device);
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
/***
* 收到deviceInfo设备信息请求 处理
* @param evt
*/
private void processMessageDeviceInfo(RequestEvent evt) {
try {
Request request = evt.getRequest();
SAXReader reader = new SAXReader();
// reader.setEncoding("GB2312");
Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
Element rootElement = xml.getRootElement();
Element deviceIdElement = rootElement.element("DeviceID");
String deviceId = deviceIdElement.getText().toString();
Device device = storager.queryVideoDevice(deviceId);
if (device == null) {
return;
}
device.setName(XmlUtil.getText(rootElement,"DeviceName"));
device.setManufacturer(XmlUtil.getText(rootElement,"Manufacturer"));
device.setModel(XmlUtil.getText(rootElement,"Model"));
device.setFirmware(XmlUtil.getText(rootElement,"Firmware"));
storager.update(device);
cmder.catalogQuery(device);
} catch (DocumentException e) {
e.printStackTrace();
}
}
/***
* 收到alarm设备报警信息 处理
* @param evt
*/
private void processMessageAlarm(RequestEvent evt) {
try {
Request request = evt.getRequest();
SAXReader reader = new SAXReader();
// reader.setEncoding("GB2312");
Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
Element rootElement = xml.getRootElement();
Element deviceIdElement = rootElement.element("DeviceID");
String deviceId = deviceIdElement.getText().toString();
Device device = storager.queryVideoDevice(deviceId);
if (device == null) {
return;
}
device.setName(XmlUtil.getText(rootElement,"DeviceName"));
device.setManufacturer(XmlUtil.getText(rootElement,"Manufacturer"));
device.setModel(XmlUtil.getText(rootElement,"Model"));
device.setFirmware(XmlUtil.getText(rootElement,"Firmware"));
storager.update(device);
cmder.catalogQuery(device);
} catch (DocumentException e) {
e.printStackTrace();
}
}
/***
* 收到keepalive请求 处理
* @param evt
*/
private void processMessageKeepAlive(RequestEvent evt){
try {
Request request = evt.getRequest();
Response response = layer.getMessageFactory().createResponse(Response.OK,request);
SAXReader reader = new SAXReader();
Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
// reader.setEncoding("GB2312");
Element rootElement = xml.getRootElement();
Element deviceIdElement = rootElement.element("DeviceID");
transaction.sendResponse(response);
publisher.onlineEventPublish(deviceIdElement.getText(), VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
e.printStackTrace();
}
}
}

32
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java

@ -0,0 +1,32 @@
package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
/**
* @Description:暂不支持的消息请求处理器
* @author: songww
* @date: 2020年5月3日 下午5:32:59
*/
@Component
public class OtherRequestProcessor implements ISIPRequestProcessor {
/**
* <p>Title: process</p>
* <p>Description: </p>
* @param evt
* @param layer
* @param transaction
* @param config
*/
@Override
public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
System.out.println("no support the method! Method:" + evt.getRequest().getMethod());
}
}

163
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java

@ -0,0 +1,163 @@
package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Locale;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.header.AuthorizationHeader;
import javax.sip.header.ContactHeader;
import javax.sip.header.ExpiresHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.Host;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import gov.nist.javax.sip.address.AddressImpl;
import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.header.Expires;
/**
* @Description:收到注册请求 处理
* @author: songww
* @date: 2020年5月3日 下午4:47:25
*/
@Component
public class RegisterRequestProcessor implements ISIPRequestProcessor {
@Autowired
private SipConfig config;
@Autowired
private SIPCommander cmder;
@Autowired
private IVideoManagerStorager storager;
@Autowired
private EventPublisher publisher;
/***
* 收到注册请求 处理
*
* @param request
* 请求消息
*/
@Override
public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
try {
System.out.println("收到注册请求,开始处理");
Request request = evt.getRequest();
Response response = null;
boolean passwordCorrect = false;
// 注册标志 0:未携带授权头或者密码错误 1:注册成功 2:注销成功
int registerFlag = 0;
Device device = null;
AuthorizationHeader authorhead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
// 校验密码是否正确
if (authorhead != null) {
passwordCorrect = new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request,
config.getSipPassword());
}
// 未携带授权头或者密码错误 回复401
if (authorhead == null || !passwordCorrect) {
if (authorhead == null) {
System.out.println("未携带授权头 回复401");
} else if (!passwordCorrect) {
System.out.println("密码错误 回复401");
}
response = layer.getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
new DigestServerAuthenticationHelper().generateChallenge(layer.getHeaderFactory(), response, config.getSipDomain());
}
// 携带授权头并且密码正确
else if (passwordCorrect) {
response = layer.getMessageFactory().createResponse(Response.OK, request);
// 添加date头
response.addHeader(layer.getHeaderFactory().createDateHeader(Calendar.getInstance(Locale.ENGLISH)));
ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
// 添加Contact头
response.addHeader(request.getHeader(ContactHeader.NAME));
// 添加Expires头
response.addHeader(request.getExpires());
// 1.获取到通信地址等信息,保存到Redis
FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
String received = viaHeader.getReceived();
int rPort = viaHeader.getRPort();
// 本地模拟设备 received 为空 rPort 为 -1
// 解析本地地址替代
if (StringUtils.isEmpty(received) || rPort == -1) {
received = viaHeader.getHost();
rPort = viaHeader.getPort();
}
//
Host host = new Host();
host.setIp(received);
host.setPort(rPort);
host.setAddress(received.concat(":").concat(String.valueOf(rPort)));
AddressImpl address = (AddressImpl) fromHeader.getAddress();
SipUri uri = (SipUri) address.getURI();
String deviceId = uri.getUser();
device = new Device();
device.setDeviceId(deviceId);
device.setHost(host);
// 注销成功
if (expiresHeader != null && expiresHeader.getExpires() == 0) {
registerFlag = 2;
}
// 注册成功
else {
registerFlag = 1;
// 判断TCP还是UDP
boolean isTcp = false;
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
String transport = reqViaHeader.getTransport();
if (transport.equals("TCP")) {
isTcp = true;
}
device.setTransport(isTcp ? "TCP" : "UDP");
}
}
transaction.sendResponse(response);
// 注册成功
// 保存到redis
// 下发catelog查询目录
if (registerFlag == 1 && device != null) {
System.out.println("注册成功! deviceId:" + device.getDeviceId());
storager.update(device);
publisher.onlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_ONLINE_REGISTER);
cmder.deviceInfoQuery(device);
} else if (registerFlag == 2) {
System.out.println("注销成功! deviceId:" + device.getDeviceId());
publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER);
}
} catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) {
e.printStackTrace();
}
}
}

64
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/SubscribeRequestProcessor.java

@ -0,0 +1,64 @@
package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
import java.text.ParseException;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.header.ExpiresHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
/**
* @Description:SUBSCRIBE请求处理器
* @author: songww
* @date: 2020年5月3日 下午5:31:20
*/
@Component
public class SubscribeRequestProcessor implements ISIPRequestProcessor {
/**
* 处理SUBSCRIBE请求
*
* @param evt
* @param layer
* @param transaction
* @param config
*/
@Override
public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
Request request = evt.getRequest();
try {
Response response = null;
response = layer.getMessageFactory().createResponse(200, request);
if (response != null) {
ExpiresHeader expireHeader = layer.getHeaderFactory().createExpiresHeader(30);
response.setExpires(expireHeader);
}
System.out.println("response : " + response.toString());
if (transaction != null) {
transaction.sendResponse(response);
transaction.terminate();
} else {
System.out.println("processRequest serverTransactionId is null.");
}
} catch (ParseException e) {
e.printStackTrace();
} catch (SipException e) {
e.printStackTrace();
} catch (InvalidArgumentException e) {
e.printStackTrace();
}
}
}

17
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/ISIPResponseProcessor.java

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

33
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/ByeResponseProcessor.java

@ -0,0 +1,33 @@
package com.genersoft.iot.vmp.gb28181.transmit.response.impl;
import javax.sip.ResponseEvent;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
/**
* @Description: BYE请求响应器
* @author: songww
* @date: 2020年5月3日 下午5:32:05
*/
@Component
public class ByeResponseProcessor implements ISIPResponseProcessor {
/**
* 处理BYE响应
*
* @param evt
* @param layer
* @param transaction
* @param config
*/
@Override
public void process(ResponseEvent evt, SipLayer layer, SipConfig config) {
// TODO Auto-generated method stub
}
}

33
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/CancelResponseProcessor.java

@ -0,0 +1,33 @@
package com.genersoft.iot.vmp.gb28181.transmit.response.impl;
import javax.sip.ResponseEvent;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
/**
* @Description:CANCEL响应处理器
* @author: songww
* @date: 2020年5月3日 下午5:32:23
*/
@Component
public class CancelResponseProcessor implements ISIPResponseProcessor {
/**
* 处理CANCEL响应
*
* @param evt
* @param layer
* @param transaction
* @param config
*/
@Override
public void process(ResponseEvent evt, SipLayer layer, SipConfig config) {
// TODO Auto-generated method stub
}
}

40
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java

@ -0,0 +1,40 @@
package com.genersoft.iot.vmp.gb28181.transmit.response.impl;
import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.ResponseEvent;
import javax.sip.SipException;
import javax.sip.message.Request;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
/**
* @Description:处理INVITE响应
* @author: songww
* @date: 2020年5月3日 下午4:43:52
*/
@Component
public class InviteResponseProcessor implements ISIPResponseProcessor {
/**
* 处理invite响应
*
* @param request
* 响应消息
*/
@Override
public void process(ResponseEvent evt, SipLayer layer, SipConfig config) {
try {
Dialog dialog = evt.getDialog();
Request reqAck =dialog.createAck(1L);
dialog.sendAck(reqAck);
} catch (InvalidArgumentException | SipException e) {
e.printStackTrace();
}
}
}

32
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/OtherResponseProcessor.java

@ -0,0 +1,32 @@
package com.genersoft.iot.vmp.gb28181.transmit.response.impl;
import javax.sip.ResponseEvent;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
/**
* @Description:暂不支持的消息响应处理器
* @author: songww
* @date: 2020年5月3日 下午5:32:59
*/
@Component
public class OtherResponseProcessor implements ISIPResponseProcessor {
/**
* <p>Title: process</p>
* <p>Description: </p>
* @param evt
* @param layer
* @param config
*/
@Override
public void process(ResponseEvent evt, SipLayer layer, SipConfig config) {
// TODO Auto-generated method stub
}
}

121
src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java

@ -0,0 +1,121 @@
package com.genersoft.iot.vmp.gb28181.utils;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 基于dom4j的工具包
*
*
*/
public class XmlUtil
{
/**
* 日志服务
*/
private static Logger LOG = LoggerFactory.getLogger(XmlUtil.class);
/**
* 解析XML为Document对象
*
* @param xml
* 被解析的XMl
* @return Document
*/
public static Element parseXml(String xml)
{
Document document = null;
//
StringReader sr = new StringReader(xml);
SAXReader saxReader = new SAXReader();
try
{
document = saxReader.read(sr);
}
catch (DocumentException e)
{
LOG.error("解析失败", e);
}
return null == document ? null : document.getRootElement();
}
/**
* 获取element对象的text的值
*
* @param em
* 节点的对象
* @param tag
* 节点的tag
* @return 节点
*/
public static String getText(Element em, String tag)
{
if (null == em)
{
return null;
}
Element e = em.element(tag);
//
return null == e ? null : e.getText();
}
/**
* 递归解析xml节点适用于 多节点数据
*
* @param node
* node
* @param nodeName
* nodeName
* @return List<Map<String, Object>>
*/
public static List<Map<String, Object>> listNodes(Element node, String nodeName)
{
if (null == node)
{
return null;
}
// 初始化返回
List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();
// 首先获取当前节点的所有属性节点
List<Attribute> list = node.attributes();
Map<String, Object> map = null;
// 遍历属性节点
for (Attribute attribute : list)
{
if (nodeName.equals(node.getName()))
{
if (null == map)
{
map = new HashMap<String, Object>();
listMap.add(map);
}
// 取到的节点属性放到map中
map.put(attribute.getName(), attribute.getValue());
}
}
// 遍历当前节点下的所有节点 ,nodeName 要解析的节点名称
// 使用递归
Iterator<Element> iterator = node.elementIterator();
while (iterator.hasNext())
{
Element e = iterator.next();
listMap.addAll(listNodes(e, nodeName));
}
return listMap;
}
}

77
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java

@ -0,0 +1,77 @@
package com.genersoft.iot.vmp.storager;
import java.util.List;
import com.genersoft.iot.vmp.gb28181.bean.Device;
/**
* @Description:视频设备数据存储接口
* @author: songww
* @date: 2020年5月6日 下午2:14:31
*/
public interface IVideoManagerStorager {
/**
* 根据设备ID判断设备是否存在
*
* @param deviceId 设备ID
* @return true:存在 false不存在
*/
public boolean exists(String deviceId);
/**
* 视频设备创建
*
* @param device 设备对象
* @return true创建成功 false创建失败
*/
public boolean create(Device device);
/**
* 视频设备更新
*
* @param device 设备对象
* @return true创建成功 false创建失败
*/
public boolean update(Device device);
/**
* 获取设备
*
* @param deviceId 设备ID
* @return DShadow 设备对象
*/
public Device queryVideoDevice(String deviceId);
/**
* 获取多个设备
*
* @param deviceIds 设备ID数组
* @return List<Device> 设备对象数组
*/
public List<Device> queryVideoDeviceList(String[] deviceIds);
/**
* 删除设备
*
* @param deviceId 设备ID
* @return true删除成功 false删除失败
*/
public boolean delete(String deviceId);
/**
* 更新设备在线
*
* @param deviceId 设备ID
* @return true更新成功 false更新失败
*/
public boolean online(String deviceId);
/**
* 更新设备离线
*
* @param deviceId 设备ID
* @return true更新成功 false更新失败
*/
public boolean outline(String deviceId);
}

36
src/main/java/com/genersoft/iot/vmp/storager/VideoManagerStoragerFactory.java

@ -0,0 +1,36 @@
package com.genersoft.iot.vmp.storager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.VManagerConfig;
/**
* @Description:视频设备数据存储工厂根据存储策略返回对应的存储器
* @author: songww
* @date: 2020年5月6日 下午2:15:16
*/
@Component
public class VideoManagerStoragerFactory {
@Autowired
private VManagerConfig vmConfig;
@Autowired
private IVideoManagerStorager jdbcStorager;
@Autowired
private IVideoManagerStorager redisStorager;
@Bean("storager")
public IVideoManagerStorager getStorager() {
if ("redis".equals(vmConfig.getDatabase().toLowerCase())) {
return redisStorager;
} else if ("jdbc".equals(vmConfig.getDatabase().toLowerCase())) {
return jdbcStorager;
}
return redisStorager;
}
}

116
src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java

@ -0,0 +1,116 @@
package com.genersoft.iot.vmp.storager.jdbc;
import java.util.List;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
/**
* @Description:视频设备数据存储-jdbc实现
* @author: songww
* @date: 2020年5月6日 下午2:28:12
*/
@Component("jdbcStorager")
public class VideoManagerJdbcStoragerImpl implements IVideoManagerStorager {
/**
* 根据设备ID判断设备是否存在
*
* @param deviceId 设备ID
* @return true:存在 false不存在
*/
@Override
public boolean exists(String deviceId) {
// TODO Auto-generated method stub
return false;
}
/**
* 视频设备创建
*
* @param device 设备对象
* @return true创建成功 false创建失败
*/
@Override
public boolean create(Device device) {
// TODO Auto-generated method stub
return false;
}
/**
* 视频设备更新
*
* @param device 设备对象
* @return true更新成功 false更新失败
*/
@Override
public boolean update(Device device) {
// TODO Auto-generated method stub
return false;
}
/**
* 获取设备
*
* @param deviceId 设备ID
* @return Device 设备对象
*/
@Override
public Device queryVideoDevice(String deviceId) {
// TODO Auto-generated method stub
return null;
}
/**
* 获取多个设备
*
* @param deviceIds 设备ID数组
* @return List<Device> 设备对象数组
*/
@Override
public List<Device> queryVideoDeviceList(String[] deviceIds) {
// TODO Auto-generated method stub
return null;
}
/**
* 删除设备
*
* @param deviceId 设备ID
* @return true删除成功 false删除失败
*/
@Override
public boolean delete(String deviceId) {
// TODO Auto-generated method stub
return false;
}
/**
* 更新设备在线
*
* @param deviceId 设备ID
* @return true更新成功 false更新失败
*/
@Override
public boolean online(String deviceId) {
// TODO Auto-generated method stub
return false;
}
/**
* 更新设备离线
*
* @param deviceId 设备ID
* @return true更新成功 false更新失败
*/
@Override
public boolean outline(String deviceId) {
// TODO Auto-generated method stub
return false;
}
}

129
src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java

@ -0,0 +1,129 @@
package com.genersoft.iot.vmp.storager.redis;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
/**
* @Description:视频设备数据存储-redis实现
* @author: songww
* @date: 2020年5月6日 下午2:31:42
*/
@Component("redisStorager")
public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
@Autowired
private RedisUtil redis;
/**
* 根据设备ID判断设备是否存在
*
* @param deviceId 设备ID
* @return true:存在 false不存在
*/
@Override
public boolean exists(String deviceId) {
return redis.hasKey(VideoManagerConstants.CACHEKEY_PREFIX+deviceId);
}
/**
* 视频设备创建
*
* @param device 设备对象
* @return true创建成功 false创建失败
*/
@Override
public boolean create(Device device) {
return redis.set(VideoManagerConstants.CACHEKEY_PREFIX+device.getDeviceId(), device);
}
/**
* 视频设备更新
*
* @param device 设备对象
* @return true更新成功 false更新失败
*/
@Override
public boolean update(Device device) {
return redis.set(VideoManagerConstants.CACHEKEY_PREFIX+device.getDeviceId(), device);
}
/**
* 获取设备
*
* @param deviceId 设备ID
* @return Device 设备对象
*/
@Override
public Device queryVideoDevice(String deviceId) {
return (Device)redis.get(VideoManagerConstants.CACHEKEY_PREFIX+deviceId);
}
/**
* 获取多个设备
*
* @param deviceIds 设备ID数组
* @return List<Device> 设备对象数组
*/
@Override
public List<Device> queryVideoDeviceList(String[] deviceIds) {
List<Device> devices = new ArrayList<>();
if (deviceIds == null || deviceIds.length == 0) {
List<Object> deviceIdList = redis.keys(VideoManagerConstants.CACHEKEY_PREFIX+"*");
for (int i = 0; i < deviceIdList.size(); i++) {
devices.add((Device)redis.get((String)deviceIdList.get(i)));
}
} else {
for (int i = 0; i < deviceIds.length; i++) {
devices.add((Device)redis.get(VideoManagerConstants.CACHEKEY_PREFIX+deviceIds[i]));
}
}
return devices;
}
/**
* 删除设备
*
* @param deviceId 设备ID
* @return true删除成功 false删除失败
*/
@Override
public boolean delete(String deviceId) {
redis.del(VideoManagerConstants.CACHEKEY_PREFIX+deviceId);
return true;
}
/**
* 更新设备在线
*
* @param deviceId 设备ID
* @return true更新成功 false更新失败
*/
@Override
public boolean online(String deviceId) {
Device device = (Device)redis.get(VideoManagerConstants.CACHEKEY_PREFIX+deviceId);
device.setOnline(1);
return redis.set(VideoManagerConstants.CACHEKEY_PREFIX+device.getDeviceId(), device);
}
/**
* 更新设备离线
*
* @param deviceId 设备ID
* @return true更新成功 false更新失败
*/
@Override
public boolean outline(String deviceId) {
Device device = (Device)redis.get(VideoManagerConstants.CACHEKEY_PREFIX+deviceId);
device.setOnline(0);
return redis.set(VideoManagerConstants.CACHEKEY_PREFIX+device.getDeviceId(), device);
}
}

43
src/main/java/com/genersoft/iot/vmp/utils/redis/FastJsonRedisSerializer.java

@ -0,0 +1,43 @@
package com.genersoft.iot.vmp.utils.redis;
import java.nio.charset.Charset;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
/**
* @Description:使用fastjson实现redis的序列化
* @author: songww
* @date: 2020年5月6日 下午8:40:11
*/
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
public FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return (T) JSON.parseObject(str, clazz);
}
}

557
src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java

@ -0,0 +1,557 @@
package com.genersoft.iot.vmp.utils.redis;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
/**
* @Description:Redis工具类
* @author: songww
* @date: 2020年5月6日 下午8:27:29
*/
@Component
public class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
/**
* 指定缓存失效时间
* @param key
* @param time 时间
* @return true / false
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据 key 获取过期时间
* @param key
* @return
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断 key 是否存在
* @param key
* @return true / false
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @SuppressWarnings("unchecked") 忽略类型转换警告
* @param key 一个或者多个
*/
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
// 传入一个 Collection<String> 集合
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
// ============================== String ==============================
/**
* 普通缓存获取
* @param key
* @return
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key
* @param value
* @return true / false
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key
* @param value
* @param time 时间如果 time < 0 则设置无限时间
* @return true / false
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key
* @param delta 递增大小
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于 0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key
* @param delta 递减大小
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于 0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
// ============================== Map ==============================
/**
* HashGet
* @param key no null
* @param item no null
* @return
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取 key 对应的 map
* @param key no null
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key
* @param map
* @return true / false
*/
public boolean hmset(String key, Map<Object, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key
* @param map
* @param time 时间
* @return true / false
*/
public boolean hmset(String key, Map<Object, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张 Hash表 中放入数据如不存在则创建
* @param key
* @param item
* @param value
* @return true / false
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张 Hash表 中放入数据并设置时间如不存在则创建
* @param key
* @param item
* @param value
* @param time 时间如果原来的 Hash表 设置了时间这里会覆盖
* @return true / false
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除 Hash表 中的值
* @param key
* @param item 可以多个no null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断 Hash表 中是否有该键的值
* @param key no null
* @param item no null
* @return true / false
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* Hash递增如果不存在则创建一个并把新增的值返回
* @param key
* @param item
* @param by 递增大小 > 0
* @return
*/
public Double hincr(String key, String item, Double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* Hash递减
* @param key
* @param item
* @param by 递减大小
* @return
*/
public Double hdecr(String key, String item, Double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================== Set ==============================
/**
* 根据 key 获取 set 中的所有值
* @param key
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 从键为 key set 根据 value 查询是否存在
* @param key
* @param value
* @return true / false
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入 set缓存
* @param key 键值
* @param values 可以多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将数据放入 set缓存并设置时间
* @param key
* @param time 时间
* @param values 可以多个
* @return 成功放入个数
*/
public long sSet(String key, long time, Object... values) {
try {
long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取 set缓存的长度
* @param key
* @return 长度
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除 set缓存中值为 value
* @param key
* @param values
* @return 成功移除个数
*/
public long setRemove(String key, Object... values) {
try {
return redisTemplate.opsForSet().remove(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ============================== List ==============================
/**
* 获取 list缓存的内容
* @param key
* @param start 开始
* @param end 结束0 -1 代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取 list缓存的长度
* @param key
* @return 长度
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 根据索引 index 获取键为 key list 中的元素
* @param key
* @param index 索引
* index >= 0 {0:表头, 1:第二个元素}
* index < 0 {-1:表尾, -2:倒数第二个元素}
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将值 value 插入键为 key list 如果 list 不存在则创建空 list
* @param key
* @param value
* @return true / false
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将值 value 插入键为 key list 并设置时间
* @param key
* @param value
* @param time 时间
* @return true / false
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* values 插入键为 key list
* @param key
* @param values
* @return true / false
*/
public boolean lSetList(String key, List<Object> values) {
try {
redisTemplate.opsForList().rightPushAll(key, values);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* values 插入键为 key list 并设置时间
* @param key
* @param values
* @param time 时间
* @return true / false
*/
public boolean lSetList(String key, List<Object> values, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, values);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引 index 修改键为 key 的值
* @param key
* @param index 索引
* @param value
* @return true / false
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 在键为 key list 中删除值为 value 的元素
* @param key
* @param count 如果 count == 0 则删除 list 中所有值为 value 的元素
* 如果 count > 0 则删除 list 中最左边那个值为 value 的元素
* 如果 count < 0 则删除 list 中最右边那个值为 value 的元素
* @param value
* @return
*/
public long lRemove(String key, long count, Object value) {
try {
return redisTemplate.opsForList().remove(key, count, value);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 模糊查询
* @param key
* @return true / false
*/
public List<Object> keys(String key) {
try {
Set<String> set = redisTemplate.keys(key);
return new ArrayList<>(set);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

50
src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java

@ -0,0 +1,50 @@
package com.genersoft.iot.vmp.vmanager.device;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
@RestController
@RequestMapping("/api")
public class DeviceController {
private final static Logger logger = LoggerFactory.getLogger(DeviceController.class);
@Autowired
private IVideoManagerStorager storager;
@GetMapping("/devices/{deviceId}")
public ResponseEntity<List<Device>> devices(@PathVariable String deviceId){
if (logger.isDebugEnabled()) {
logger.debug("查询视频设备API调用,deviceId:" + deviceId);
}
List<Device> deviceList = new ArrayList<>();
deviceList.add(storager.queryVideoDevice(deviceId));
return new ResponseEntity<>(deviceList,HttpStatus.OK);
}
@GetMapping("/devices")
public ResponseEntity<List<Device>> devices(){
if (logger.isDebugEnabled()) {
logger.debug("查询所有视频设备API调用");
}
List<Device> deviceList = storager.queryVideoDeviceList(null);
return new ResponseEntity<>(deviceList,HttpStatus.OK);
}
}

41
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java

@ -0,0 +1,41 @@
package com.genersoft.iot.vmp.vmanager.play;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
@RestController
@RequestMapping("/api")
public class PlayController {
private final static Logger logger = LoggerFactory.getLogger(PlayController.class);
@Autowired
private SIPCommander cmder;
@GetMapping("/play/{deviceId}_{channelId}")
public ResponseEntity<String> play(@PathVariable String deviceId,@PathVariable String channelId){
String ssrc = cmder.playStreamCmd(deviceId, channelId);
if (logger.isDebugEnabled()) {
logger.debug(String.format("设备预览 API调用,deviceId:%s ,channelId:%s",deviceId, channelId));
logger.debug("设备预览 API调用,ssrc:"+ssrc+",ZLMedia streamId:"+Integer.toHexString(Integer.parseInt(ssrc)));
}
if(ssrc!=null) {
return new ResponseEntity<String>(ssrc,HttpStatus.OK);
} else {
logger.warn("设备预览API调用失败!");
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}

45
src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java

@ -0,0 +1,45 @@
package com.genersoft.iot.vmp.vmanager.ptz;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
@RestController
@RequestMapping("/api")
public class PtzController {
private final static Logger logger = LoggerFactory.getLogger(PtzController.class);
@Autowired
private SIPCommander cmder;
/***
* http://localhost:8080/api/ptz/34020000001320000002_34020000001320000008?leftRight=1&upDown=0&inOut=0&moveSpeed=50&zoomSpeed=0
* @param deviceId
* @param channelId
* @param leftRight
* @param upDown
* @param inOut
* @param moveSpeed
* @param zoomSpeed
* @return
*/
@GetMapping("/ptz/{deviceId}_{channelId}")
public ResponseEntity<String> ptz(@PathVariable String deviceId,@PathVariable String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed){
if (logger.isDebugEnabled()) {
logger.debug(String.format("设备云台控制 API调用,deviceId:%s ,channelId:%s ,leftRight:%d ,upDown:%d ,inOut:%d ,moveSpeed:%d ,zoomSpeed:%d",deviceId, channelId, leftRight, upDown, inOut, moveSpeed, zoomSpeed));
}
cmder.ptzCmd(deviceId, channelId, leftRight, upDown, inOut, moveSpeed, zoomSpeed);
return new ResponseEntity<String>("success",HttpStatus.OK);
}
}

36
src/main/resources/application.yml

@ -0,0 +1,36 @@
spring:
application:
name: wvp
# 数据存储方式,支持redis、jdbc
database: redis
redis:
# Redis服务器IP
host: 127.0.0.1
#端口号
port: 6379
datebase: 0
#访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
password:
#超时时间
timeout: 10000
datasource:
name: wcp
url: jdbc:mysql://127.0.0.1:3306/wcp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
server:
port: 8080
sip:
# 本地服务地址
ip: 192.168.0.3
server_id: 34020000002000000001
port: 5060
domain: 34020000
# 暂时使用统一密码,后续改为一机一密
password: admin
media:
# ZLMediaServer IP
ip: 192.168.0.4
port: 10000
Loading…
Cancel
Save