songww
5 years ago
commit
70091f29f2
48 changed files with 4650 additions and 0 deletions
@ -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> |
@ -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); |
|||
} |
|||
|
|||
} |
@ -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"; |
|||
} |
@ -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");
|
|||
// }
|
|||
} |
@ -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; |
|||
} |
|||
|
|||
|
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -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); |
|||
|
|||
} |
|||
|
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
|
|||
|
|||
|
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -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()); |
|||
} |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
|
|||
/** |
|||
* 监听失效的key,key格式为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); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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()); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
|||
|
|||
} |
@ -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); |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
|||
} |
@ -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); |
|||
|
|||
} |
@ -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(); |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
@ -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
|
|||
|
|||
} |
|||
|
|||
} |
@ -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
|
|||
|
|||
} |
|||
|
|||
} |
@ -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();
|
|||
// }
|
|||
} |
|||
|
|||
} |
@ -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(); |
|||
} |
|||
} |
|||
|
|||
} |
@ -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()); |
|||
} |
|||
|
|||
} |
@ -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(); |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
@ -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(); |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
@ -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); |
|||
|
|||
} |
@ -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
|
|||
|
|||
} |
|||
|
|||
} |
@ -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
|
|||
|
|||
} |
|||
|
|||
} |
@ -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(); |
|||
} |
|||
} |
|||
|
|||
} |
@ -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
|
|||
|
|||
} |
|||
|
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -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); |
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -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); |
|||
} |
|||
|
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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…
Reference in new issue