Commit f2c6210539cfc5f423a773b4d897c37a9066f821

Authored by 648540858
2 parents bb49f1f0 b010e63d

Merge branch 'wvp-28181-2.0'

# Conflicts:
#	doc/_content/introduction/deployment.md
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
#	src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java
#	src/main/resources/all-application.yml
#	web_src/src/components/dialog/devicePlayer.vue
Showing 78 changed files with 2178 additions and 1043 deletions

Too many changes to show.

To preserve performance only 78 of 95 files are displayed.

README.md
@@ -98,6 +98,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git @@ -98,6 +98,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
98 - [X] 支持推流鉴权 98 - [X] 支持推流鉴权
99 - [X] 支持接口鉴权 99 - [X] 支持接口鉴权
100 - [X] 云端录像,推流/代理/国标视频均可以录制在云端服务器,支持预览和下载 100 - [X] 云端录像,推流/代理/国标视频均可以录制在云端服务器,支持预览和下载
  101 +- [X] 支持打包可执行jar和war
101 102
102 103
103 # 遇到问题如何解决 104 # 遇到问题如何解决
doc/_content/introduction/compile.md
@@ -77,13 +77,18 @@ npm run build @@ -77,13 +77,18 @@ npm run build
77 **编译完成一般是这个样子,中间没有报红的错误信息** 77 **编译完成一般是这个样子,中间没有报红的错误信息**
78 ![编译成功](_media/img.png) 78 ![编译成功](_media/img.png)
79 79
80 -### 5.3 打包项目, 生成可执行jar 80 +### 5.3 生成可执行jar
81 ```bash 81 ```bash
82 cd wvp-GB28181-pro 82 cd wvp-GB28181-pro
83 mvn package 83 mvn package
84 ``` 84 ```
  85 +### 5.4 生成war
  86 +```bash
  87 +cd wvp-GB28181-pro
  88 +mvn package -P war
  89 +```
85 编译如果报错, 一般都是网络问题, 导致的依赖包下载失败 90 编译如果报错, 一般都是网络问题, 导致的依赖包下载失败
86 -编译完成后在target目录下出现wvp-pro-***.jar 91 +编译完成后在target目录下出现wvp-pro-***.jar/wvp-pro-***.war
87 接下来[配置服务](./_content/introduction/config.md) 92 接下来[配置服务](./_content/introduction/config.md)
88 93
89 94
doc/_content/introduction/config.md
@@ -31,6 +31,7 @@ java -jar wvp-pro-*.jar @@ -31,6 +31,7 @@ java -jar wvp-pro-*.jar
31 ## 2 配置WVP-PRO 31 ## 2 配置WVP-PRO
32 ### 2.1 Mysql数据库配置 32 ### 2.1 Mysql数据库配置
33 首先你需要创建一个名为wvp(也可使用其他名字)的数据库,并使用sql/mysql.sql导入数据库,初始化数据库结构。 33 首先你需要创建一个名为wvp(也可使用其他名字)的数据库,并使用sql/mysql.sql导入数据库,初始化数据库结构。
  34 +(这里注意,取决于版本,新版的sql文件夹下有update.sql,补丁包,一定要注意运行导入)
34 在application-dev.yml中配置(使用1.2方式的是在jar包的同级目录的application.yml)配置数据库连接,包括数据库连接信息,密码。 35 在application-dev.yml中配置(使用1.2方式的是在jar包的同级目录的application.yml)配置数据库连接,包括数据库连接信息,密码。
35 ### 2.2 Redis数据库配置 36 ### 2.2 Redis数据库配置
36 配置wvp中的redis连接信息,建议wvp自己单独使用一个db。 37 配置wvp中的redis连接信息,建议wvp自己单独使用一个db。
@@ -116,4 +117,4 @@ user-settings: @@ -116,4 +117,4 @@ user-settings:
116 117
117 118
118 如果配置信息无误,你可以启动zlm,再启动wvp来测试了,启动成功的话,你可以在wvp的日志下看到zlm已连接的提示。 119 如果配置信息无误,你可以启动zlm,再启动wvp来测试了,启动成功的话,你可以在wvp的日志下看到zlm已连接的提示。
119 -接下来[部署到服务器](./_content/introduction/deployment.md), 如何你只是本地运行直接再本地运行即可。  
120 \ No newline at end of file 120 \ No newline at end of file
  121 +接下来[部署到服务器](./_content/introduction/deployment.md), 如何你只是本地运行直接再本地运行即可。
doc/_content/introduction/deployment.md
@@ -27,7 +27,9 @@ @@ -27,7 +27,9 @@
27 ```shell 27 ```shell
28 nohup java -jar wvp-pro-*.jar & 28 nohup java -jar wvp-pro-*.jar &
29 ``` 29 ```
30 - 30 +war包:
  31 +下载Tomcat后将war包放入webapps中,启动Tomcat以解压war包,停止Tomcat后,删除ROOT目录以及war包,将解压后的war包目录重命名为ROOT,
  32 +然后启动Tomcat。
31 **启动ZLM** 33 **启动ZLM**
32 ```shell 34 ```shell
33 nohup ./MediaServer -d -m 3 & 35 nohup ./MediaServer -d -m 3 &
@@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
14 <version>2.6.7</version> 14 <version>2.6.7</version>
15 <name>web video platform</name> 15 <name>web video platform</name>
16 <description>国标28181视频平台</description> 16 <description>国标28181视频平台</description>
  17 + <packaging>${project.packaging}</packaging>
17 18
18 <repositories> 19 <repositories>
19 <repository> 20 <repository>
@@ -56,6 +57,42 @@ @@ -56,6 +57,42 @@
56 <asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory> 57 <asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>
57 </properties> 58 </properties>
58 59
  60 + <profiles>
  61 + <profile>
  62 + <id>jar</id>
  63 + <activation>
  64 + <activeByDefault>true</activeByDefault>
  65 + </activation>
  66 + <properties>
  67 + <project.packaging>jar</project.packaging>
  68 + </properties>
  69 + </profile>
  70 + <profile>
  71 + <id>war</id>
  72 + <properties>
  73 + <project.packaging>war</project.packaging>
  74 + </properties>
  75 + <dependencies>
  76 + <dependency>
  77 + <groupId>org.springframework.boot</groupId>
  78 + <artifactId>spring-boot-starter-web</artifactId>
  79 + <exclusions>
  80 + <exclusion>
  81 + <groupId>org.springframework.boot</groupId>
  82 + <artifactId>spring-boot-starter-jetty</artifactId>
  83 + </exclusion>
  84 + </exclusions>
  85 + </dependency>
  86 + <dependency>
  87 + <groupId>javax.servlet</groupId>
  88 + <artifactId>javax.servlet-api</artifactId>
  89 + <version>3.1.0</version>
  90 + <scope>provided</scope>
  91 + </dependency>
  92 + </dependencies>
  93 + </profile>
  94 + </profiles>
  95 +
59 <dependencies> 96 <dependencies>
60 <dependency> 97 <dependency>
61 <groupId>org.springframework.boot</groupId> 98 <groupId>org.springframework.boot</groupId>
@@ -242,8 +279,8 @@ @@ -242,8 +279,8 @@
242 <artifactId>spring-boot-starter-test</artifactId> 279 <artifactId>spring-boot-starter-test</artifactId>
243 <!-- <scope>test</scope>--> 280 <!-- <scope>test</scope>-->
244 </dependency> 281 </dependency>
245 - </dependencies>  
246 282
  283 + </dependencies>
247 284
248 285
249 <build> 286 <build>
sql/update.sql deleted 100644 → 0
1 --- 2.6.6->2.6.7  
2 -alter table device  
3 - add keepaliveIntervalTime int default null;  
4 \ No newline at end of file 0 \ No newline at end of file
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
1 package com.genersoft.iot.vmp; 1 package com.genersoft.iot.vmp;
2 2
3 -import java.util.logging.LogManager;  
4 -  
5 import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport; 3 import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport;
6 -import com.genersoft.iot.vmp.storager.impl.RedisCatchStorageImpl;  
7 import com.genersoft.iot.vmp.utils.GitUtil; 4 import com.genersoft.iot.vmp.utils.GitUtil;
8 import com.genersoft.iot.vmp.utils.SpringBeanFactory; 5 import com.genersoft.iot.vmp.utils.SpringBeanFactory;
9 import org.slf4j.Logger; 6 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory; 7 import org.slf4j.LoggerFactory;
11 -import org.springframework.beans.factory.annotation.Autowired;  
12 import org.springframework.boot.SpringApplication; 8 import org.springframework.boot.SpringApplication;
13 import org.springframework.boot.autoconfigure.SpringBootApplication; 9 import org.springframework.boot.autoconfigure.SpringBootApplication;
  10 +import org.springframework.boot.builder.SpringApplicationBuilder;
14 import org.springframework.boot.web.servlet.ServletComponentScan; 11 import org.springframework.boot.web.servlet.ServletComponentScan;
  12 +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
15 import org.springframework.context.ConfigurableApplicationContext; 13 import org.springframework.context.ConfigurableApplicationContext;
16 import org.springframework.scheduling.annotation.EnableScheduling; 14 import org.springframework.scheduling.annotation.EnableScheduling;
17 15
  16 +import javax.servlet.ServletContext;
  17 +import javax.servlet.ServletException;
  18 +import javax.servlet.SessionCookieConfig;
  19 +import javax.servlet.SessionTrackingMode;
  20 +import java.util.Collections;
  21 +
18 /** 22 /**
19 * 启动类 23 * 启动类
20 */ 24 */
@@ -22,7 +26,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; @@ -22,7 +26,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
22 @SpringBootApplication 26 @SpringBootApplication
23 @EnableScheduling 27 @EnableScheduling
24 @EnableDruidSupport 28 @EnableDruidSupport
25 -public class VManageBootstrap extends LogManager { 29 +public class VManageBootstrap extends SpringBootServletInitializer {
26 30
27 private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class); 31 private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class);
28 32
@@ -41,6 +45,21 @@ public class VManageBootstrap extends LogManager { @@ -41,6 +45,21 @@ public class VManageBootstrap extends LogManager {
41 context.close(); 45 context.close();
42 VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args); 46 VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
43 } 47 }
44 -  
45 48
  49 + @Override
  50 + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
  51 + return application.sources(VManageBootstrap.class);
  52 + }
  53 +
  54 + @Override
  55 + public void onStartup(ServletContext servletContext) throws ServletException {
  56 + super.onStartup(servletContext);
  57 +
  58 + servletContext.setSessionTrackingModes(
  59 + Collections.singleton(SessionTrackingMode.COOKIE)
  60 + );
  61 + SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig();
  62 + sessionCookieConfig.setHttpOnly(true);
  63 +
  64 + }
46 } 65 }
src/main/java/com/genersoft/iot/vmp/common/enums/DeviceControlType.java 0 → 100644
  1 +package com.genersoft.iot.vmp.common.enums;
  2 +
  3 +import org.dom4j.Element;
  4 +import org.springframework.util.ObjectUtils;
  5 +
  6 +
  7 +/**
  8 + * @author gaofuwang
  9 + * @date 2023/01/18/ 10:09:00
  10 + * @since 1.0
  11 + */
  12 +public enum DeviceControlType {
  13 +
  14 + /**
  15 + * 云台控制
  16 + * 上下左右,预置位,扫描,辅助功能,巡航
  17 + */
  18 + PTZ("PTZCmd","云台控制"),
  19 + /**
  20 + * 远程启动
  21 + */
  22 + TELE_BOOT("TeleBoot","远程启动"),
  23 + /**
  24 + * 录像控制
  25 + */
  26 + RECORD("RecordCmd","录像控制"),
  27 + /**
  28 + * 布防撤防
  29 + */
  30 + GUARD("GuardCmd","布防撤防"),
  31 + /**
  32 + * 告警控制
  33 + */
  34 + ALARM("AlarmCmd","告警控制"),
  35 + /**
  36 + * 强制关键帧
  37 + */
  38 + I_FRAME("IFameCmd","强制关键帧"),
  39 + /**
  40 + * 拉框放大
  41 + */
  42 + DRAG_ZOOM_IN("DragZoomIn","拉框放大"),
  43 + /**
  44 + * 拉框缩小
  45 + */
  46 + DRAG_ZOOM_OUT("DragZoomOut","拉框缩小"),
  47 + /**
  48 + * 看守位
  49 + */
  50 + HOME_POSITION("HomePosition","看守位");
  51 +
  52 + private final String val;
  53 +
  54 + private final String desc;
  55 +
  56 + DeviceControlType(String val, String desc) {
  57 + this.val = val;
  58 + this.desc = desc;
  59 + }
  60 +
  61 + public String getVal() {
  62 + return val;
  63 + }
  64 +
  65 + public String getDesc() {
  66 + return desc;
  67 + }
  68 +
  69 + public static DeviceControlType typeOf(Element rootElement) {
  70 + for (DeviceControlType item : DeviceControlType.values()) {
  71 + if (!ObjectUtils.isEmpty(rootElement.element(item.val)) || !ObjectUtils.isEmpty(rootElement.elements(item.val))) {
  72 + return item;
  73 + }
  74 + }
  75 + return null;
  76 + }
  77 +}
src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
@@ -10,6 +10,7 @@ import org.slf4j.Logger; @@ -10,6 +10,7 @@ import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory; 10 import org.slf4j.LoggerFactory;
11 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.beans.factory.annotation.Autowired;
12 import org.springframework.http.HttpStatus; 12 import org.springframework.http.HttpStatus;
  13 +import org.springframework.stereotype.Component;
13 import org.springframework.util.ObjectUtils; 14 import org.springframework.util.ObjectUtils;
14 import org.springframework.web.filter.OncePerRequestFilter; 15 import org.springframework.web.filter.OncePerRequestFilter;
15 16
@@ -23,6 +24,7 @@ import java.io.IOException; @@ -23,6 +24,7 @@ import java.io.IOException;
23 * @author lin 24 * @author lin
24 */ 25 */
25 @WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true) 26 @WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true)
  27 +@Component
26 public class ApiAccessFilter extends OncePerRequestFilter { 28 public class ApiAccessFilter extends OncePerRequestFilter {
27 29
28 private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class); 30 private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class);
@@ -48,7 +50,7 @@ public class ApiAccessFilter extends OncePerRequestFilter { @@ -48,7 +50,7 @@ public class ApiAccessFilter extends OncePerRequestFilter {
48 50
49 filterChain.doFilter(servletRequest, servletResponse); 51 filterChain.doFilter(servletRequest, servletResponse);
50 52
51 - if (uriName != null && userSetting.getLogInDatebase()) { 53 + if (uriName != null && userSetting != null && userSetting.getLogInDatebase() != null && userSetting.getLogInDatebase()) {
52 54
53 LogDto logDto = new LogDto(); 55 LogDto logDto = new LogDto();
54 logDto.setName(uriName); 56 logDto.setName(uriName);
src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
@@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.conf; @@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.conf;
2 2
3 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 3 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
4 import com.genersoft.iot.vmp.service.IMediaServerService; 4 import com.genersoft.iot.vmp.service.IMediaServerService;
5 -import org.apache.catalina.connector.ClientAbortException;  
6 import org.apache.http.HttpHost; 5 import org.apache.http.HttpHost;
7 import org.apache.http.HttpRequest; 6 import org.apache.http.HttpRequest;
8 import org.apache.http.HttpResponse; 7 import org.apache.http.HttpResponse;
@@ -15,11 +14,9 @@ import org.springframework.boot.web.servlet.ServletRegistrationBean; @@ -15,11 +14,9 @@ import org.springframework.boot.web.servlet.ServletRegistrationBean;
15 import org.springframework.context.annotation.Bean; 14 import org.springframework.context.annotation.Bean;
16 import org.springframework.context.annotation.Configuration; 15 import org.springframework.context.annotation.Configuration;
17 import org.springframework.util.ObjectUtils; 16 import org.springframework.util.ObjectUtils;
18 -import org.springframework.util.StringUtils;  
19 17
20 import javax.servlet.ServletException; 18 import javax.servlet.ServletException;
21 import javax.servlet.http.HttpServletRequest; 19 import javax.servlet.http.HttpServletRequest;
22 -import javax.servlet.http.HttpServletResponse;  
23 import java.io.IOException; 20 import java.io.IOException;
24 import java.net.ConnectException; 21 import java.net.ConnectException;
25 22
@@ -77,9 +74,7 @@ public class ProxyServletConfig { @@ -77,9 +74,7 @@ public class ProxyServletConfig {
77 } catch (IOException ioException) { 74 } catch (IOException ioException) {
78 if (ioException instanceof ConnectException) { 75 if (ioException instanceof ConnectException) {
79 logger.error("zlm 连接失败"); 76 logger.error("zlm 连接失败");
80 - } else if (ioException instanceof ClientAbortException) {  
81 - logger.error("zlm: 用户已中断连接,代理终止");  
82 - } else { 77 + } else {
83 logger.error("zlm 代理失败: ", e); 78 logger.error("zlm 代理失败: ", e);
84 } 79 }
85 } catch (RuntimeException exception){ 80 } catch (RuntimeException exception){
@@ -195,9 +190,7 @@ public class ProxyServletConfig { @@ -195,9 +190,7 @@ public class ProxyServletConfig {
195 } catch (IOException ioException) { 190 } catch (IOException ioException) {
196 if (ioException instanceof ConnectException) { 191 if (ioException instanceof ConnectException) {
197 logger.error("录像服务 连接失败"); 192 logger.error("录像服务 连接失败");
198 - } else if (ioException instanceof ClientAbortException) {  
199 - logger.error("录像服务:用户已中断连接,代理终止");  
200 - } else { 193 + }else {
201 logger.error("录像服务 代理失败: ", e); 194 logger.error("录像服务 代理失败: ", e);
202 } 195 }
203 } catch (RuntimeException exception){ 196 } catch (RuntimeException exception){
src/main/java/com/genersoft/iot/vmp/conf/ServiceInfo.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf;
  2 +
  3 +import org.slf4j.Logger;
  4 +import org.slf4j.LoggerFactory;
  5 +import org.springframework.boot.web.context.WebServerInitializedEvent;
  6 +import org.springframework.context.ApplicationListener;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +@Component
  10 +public class ServiceInfo implements ApplicationListener<WebServerInitializedEvent> {
  11 +
  12 + private final Logger logger = LoggerFactory.getLogger(ServiceInfo.class);
  13 +
  14 + private static int serverPort;
  15 +
  16 + public static int getServerPort() {
  17 + return serverPort;
  18 + }
  19 +
  20 + @Override
  21 + public void onApplicationEvent(WebServerInitializedEvent event) {
  22 + // 项目启动获取启动的端口号
  23 + ServiceInfo.serverPort = event.getWebServer().getPort();
  24 + logger.info("项目启动获取启动的端口号: " + ServiceInfo.serverPort);
  25 + }
  26 +
  27 + public void setServerPort(int serverPort) {
  28 + ServiceInfo.serverPort = serverPort;
  29 + }
  30 +}
src/main/java/com/genersoft/iot/vmp/conf/security/UrlTokenHandler.java deleted 100644 → 0
1 -package com.genersoft.iot.vmp.conf.security;  
2 -  
3 -import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;  
4 -  
5 -import javax.servlet.ServletContext;  
6 -import javax.servlet.ServletException;  
7 -import javax.servlet.SessionCookieConfig;  
8 -import javax.servlet.SessionTrackingMode;  
9 -import java.util.Collections;  
10 -  
11 -public class UrlTokenHandler extends SpringBootServletInitializer {  
12 -  
13 - @Override  
14 - public void onStartup(ServletContext servletContext) throws ServletException {  
15 - super.onStartup(servletContext);  
16 -  
17 - servletContext.setSessionTrackingModes(  
18 - Collections.singleton(SessionTrackingMode.COOKIE)  
19 - );  
20 - SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig();  
21 - sessionCookieConfig.setHttpOnly(true);  
22 -  
23 - }  
24 -}  
src/main/java/com/genersoft/iot/vmp/gb28181/bean/AlarmChannelMessage.java
1 package com.genersoft.iot.vmp.gb28181.bean; 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2
  3 +
3 /** 4 /**
4 * 通过redis分发报警消息 5 * 通过redis分发报警消息
5 */ 6 */
@@ -8,12 +9,14 @@ public class AlarmChannelMessage { @@ -8,12 +9,14 @@ public class AlarmChannelMessage {
8 * 国标编号 9 * 国标编号
9 */ 10 */
10 private String gbId; 11 private String gbId;
11 -  
12 /** 12 /**
13 * 报警编号 13 * 报警编号
14 */ 14 */
15 private int alarmSn; 15 private int alarmSn;
16 - 16 + /**
  17 + * 告警类型
  18 + */
  19 + private int alarmType;
17 20
18 /** 21 /**
19 * 报警描述 22 * 报警描述
@@ -36,6 +39,14 @@ public class AlarmChannelMessage { @@ -36,6 +39,14 @@ public class AlarmChannelMessage {
36 this.alarmSn = alarmSn; 39 this.alarmSn = alarmSn;
37 } 40 }
38 41
  42 + public int getAlarmType() {
  43 + return alarmType;
  44 + }
  45 +
  46 + public void setAlarmType(int alarmType) {
  47 + this.alarmType = alarmType;
  48 + }
  49 +
39 public String getAlarmDescription() { 50 public String getAlarmDescription() {
40 return alarmDescription; 51 return alarmDescription;
41 } 52 }
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarmMethod.java
@@ -37,4 +37,18 @@ public enum DeviceAlarmMethod { @@ -37,4 +37,18 @@ public enum DeviceAlarmMethod {
37 public int getVal() { 37 public int getVal() {
38 return val; 38 return val;
39 } 39 }
  40 +
  41 + /**
  42 + * 查询是否匹配类型
  43 + * @param code
  44 + * @return
  45 + */
  46 + public static DeviceAlarmMethod typeOf(int code) {
  47 + for (DeviceAlarmMethod item : DeviceAlarmMethod.values()) {
  48 + if (code==item.getVal()) {
  49 + return item;
  50 + }
  51 + }
  52 + return null;
  53 + }
40 } 54 }
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DragZoomRequest.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.bean;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.utils.MessageElement;
  4 +
  5 +/**
  6 + * 设备信息查询响应
  7 + *
  8 + * @author Y.G
  9 + * @version 1.0
  10 + * @date 2022/6/28 14:55
  11 + */
  12 +public class DragZoomRequest {
  13 + /**
  14 + * 序列号
  15 + */
  16 + @MessageElement("SN")
  17 + private String sn;
  18 +
  19 + @MessageElement("DeviceID")
  20 + private String deviceId;
  21 +
  22 + @MessageElement(value = "DragZoomIn")
  23 + private DragZoom dragZoomIn;
  24 +
  25 + @MessageElement(value = "DragZoomOut")
  26 + private DragZoom dragZoomOut;
  27 +
  28 + /**
  29 + * 基本参数
  30 + */
  31 + public static class DragZoom {
  32 + /**
  33 + * 播放窗口长度像素值
  34 + */
  35 + @MessageElement("Length")
  36 + protected Integer length;
  37 + /**
  38 + * 播放窗口宽度像素值
  39 + */
  40 + @MessageElement("Width")
  41 + protected Integer width;
  42 + /**
  43 + * 拉框中心的横轴坐标像素值
  44 + */
  45 + @MessageElement("MidPointX")
  46 + protected Integer midPointX;
  47 + /**
  48 + * 拉框中心的纵轴坐标像素值
  49 + */
  50 + @MessageElement("MidPointY")
  51 + protected Integer midPointY;
  52 + /**
  53 + * 拉框长度像素值
  54 + */
  55 + @MessageElement("LengthX")
  56 + protected Integer lengthX;
  57 + /**
  58 + * 拉框宽度像素值
  59 + */
  60 + @MessageElement("LengthY")
  61 + protected Integer lengthY;
  62 +
  63 + public Integer getLength() {
  64 + return length;
  65 + }
  66 +
  67 + public void setLength(Integer length) {
  68 + this.length = length;
  69 + }
  70 +
  71 + public Integer getWidth() {
  72 + return width;
  73 + }
  74 +
  75 + public void setWidth(Integer width) {
  76 + this.width = width;
  77 + }
  78 +
  79 + public Integer getMidPointX() {
  80 + return midPointX;
  81 + }
  82 +
  83 + public void setMidPointX(Integer midPointX) {
  84 + this.midPointX = midPointX;
  85 + }
  86 +
  87 + public Integer getMidPointY() {
  88 + return midPointY;
  89 + }
  90 +
  91 + public void setMidPointY(Integer midPointY) {
  92 + this.midPointY = midPointY;
  93 + }
  94 +
  95 + public Integer getLengthX() {
  96 + return lengthX;
  97 + }
  98 +
  99 + public void setLengthX(Integer lengthX) {
  100 + this.lengthX = lengthX;
  101 + }
  102 +
  103 + public Integer getLengthY() {
  104 + return lengthY;
  105 + }
  106 +
  107 + public void setLengthY(Integer lengthY) {
  108 + this.lengthY = lengthY;
  109 + }
  110 + }
  111 +
  112 + public String getSn() {
  113 + return sn;
  114 + }
  115 +
  116 + public void setSn(String sn) {
  117 + this.sn = sn;
  118 + }
  119 +
  120 + public String getDeviceId() {
  121 + return deviceId;
  122 + }
  123 +
  124 + public void setDeviceId(String deviceId) {
  125 + this.deviceId = deviceId;
  126 + }
  127 +
  128 + public DragZoom getDragZoomIn() {
  129 + return dragZoomIn;
  130 + }
  131 +
  132 + public void setDragZoomIn(DragZoom dragZoomIn) {
  133 + this.dragZoomIn = dragZoomIn;
  134 + }
  135 +
  136 + public DragZoom getDragZoomOut() {
  137 + return dragZoomOut;
  138 + }
  139 +
  140 + public void setDragZoomOut(DragZoom dragZoomOut) {
  141 + this.dragZoomOut = dragZoomOut;
  142 + }
  143 +}
src/main/java/com/genersoft/iot/vmp/gb28181/bean/HomePositionRequest.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.bean;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.utils.MessageElement;
  4 +
  5 +/**
  6 + * 设备信息查询响应
  7 + *
  8 + * @author Y.G
  9 + * @version 1.0
  10 + * @date 2022/6/28 14:55
  11 + */
  12 +public class HomePositionRequest {
  13 + /**
  14 + * 序列号
  15 + */
  16 + @MessageElement("SN")
  17 + private String sn;
  18 +
  19 + @MessageElement("DeviceID")
  20 + private String deviceId;
  21 +
  22 + @MessageElement(value = "HomePosition")
  23 + private HomePosition homePosition;
  24 +
  25 +
  26 + /**
  27 + * 基本参数
  28 + */
  29 + public static class HomePosition {
  30 + /**
  31 + * 播放窗口长度像素值
  32 + */
  33 + @MessageElement("Enabled")
  34 + protected String enabled;
  35 + /**
  36 + * 播放窗口宽度像素值
  37 + */
  38 + @MessageElement("ResetTime")
  39 + protected String resetTime;
  40 + /**
  41 + * 拉框中心的横轴坐标像素值
  42 + */
  43 + @MessageElement("PresetIndex")
  44 + protected String presetIndex;
  45 +
  46 + public String getEnabled() {
  47 + return enabled;
  48 + }
  49 +
  50 + public void setEnabled(String enabled) {
  51 + this.enabled = enabled;
  52 + }
  53 +
  54 + public String getResetTime() {
  55 + return resetTime;
  56 + }
  57 +
  58 + public void setResetTime(String resetTime) {
  59 + this.resetTime = resetTime;
  60 + }
  61 +
  62 + public String getPresetIndex() {
  63 + return presetIndex;
  64 + }
  65 +
  66 + public void setPresetIndex(String presetIndex) {
  67 + this.presetIndex = presetIndex;
  68 + }
  69 + }
  70 +
  71 + public String getSn() {
  72 + return sn;
  73 + }
  74 +
  75 + public void setSn(String sn) {
  76 + this.sn = sn;
  77 + }
  78 +
  79 + public String getDeviceId() {
  80 + return deviceId;
  81 + }
  82 +
  83 + public void setDeviceId(String deviceId) {
  84 + this.deviceId = deviceId;
  85 + }
  86 +
  87 + public HomePosition getHomePosition() {
  88 + return homePosition;
  89 + }
  90 +
  91 + public void setHomePosition(HomePosition homePosition) {
  92 + this.homePosition = homePosition;
  93 + }
  94 +}
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
1 package com.genersoft.iot.vmp.gb28181.bean; 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2
  3 +
3 import java.time.Instant; 4 import java.time.Instant;
4 import java.util.List; 5 import java.util.List;
5 6
@@ -20,6 +21,8 @@ public class RecordInfo { @@ -20,6 +21,8 @@ public class RecordInfo {
20 21
21 private int sumNum; 22 private int sumNum;
22 23
  24 + private int count;
  25 +
23 private Instant lastTime; 26 private Instant lastTime;
24 27
25 private List<RecordItem> recordList; 28 private List<RecordItem> recordList;
@@ -79,4 +82,12 @@ public class RecordInfo { @@ -79,4 +82,12 @@ public class RecordInfo {
79 public void setLastTime(Instant lastTime) { 82 public void setLastTime(Instant lastTime) {
80 this.lastTime = lastTime; 83 this.lastTime = lastTime;
81 } 84 }
  85 +
  86 + public int getCount() {
  87 + return count;
  88 + }
  89 +
  90 + public void setCount(int count) {
  91 + this.count = count;
  92 + }
82 } 93 }
src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java
1 package com.genersoft.iot.vmp.gb28181.event.record; 1 package com.genersoft.iot.vmp.gb28181.event.record;
2 2
3 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; 3 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
  4 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
4 import org.slf4j.Logger; 5 import org.slf4j.Logger;
5 import org.slf4j.LoggerFactory; 6 import org.slf4j.LoggerFactory;
  7 +import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.context.ApplicationListener; 8 import org.springframework.context.ApplicationListener;
7 import org.springframework.stereotype.Component; 9 import org.springframework.stereotype.Component;
8 10
@@ -20,25 +22,46 @@ public class RecordEndEventListener implements ApplicationListener&lt;RecordEndEven @@ -20,25 +22,46 @@ public class RecordEndEventListener implements ApplicationListener&lt;RecordEndEven
20 22
21 private final static Logger logger = LoggerFactory.getLogger(RecordEndEventListener.class); 23 private final static Logger logger = LoggerFactory.getLogger(RecordEndEventListener.class);
22 24
  25 + private Map<String, RecordEndEventHandler> handlerMap = new ConcurrentHashMap<>();
23 public interface RecordEndEventHandler{ 26 public interface RecordEndEventHandler{
24 void handler(RecordInfo recordInfo); 27 void handler(RecordInfo recordInfo);
25 } 28 }
26 29
27 - private Map<String, RecordEndEventHandler> handlerMap = new ConcurrentHashMap<>();  
28 -  
29 @Override 30 @Override
30 public void onApplicationEvent(RecordEndEvent event) { 31 public void onApplicationEvent(RecordEndEvent event) {
31 - logger.info("录像查询完成事件触发,deviceId:{}, channelId: {}, 录像数量{}条", event.getRecordInfo().getDeviceId(),  
32 - event.getRecordInfo().getChannelId(), event.getRecordInfo().getSumNum() ); 32 + String deviceId = event.getRecordInfo().getDeviceId();
  33 + String channelId = event.getRecordInfo().getChannelId();
  34 + int count = event.getRecordInfo().getCount();
  35 + int sumNum = event.getRecordInfo().getSumNum();
  36 + logger.info("录像查询完成事件触发,deviceId:{}, channelId: {}, 录像数量{}/{}条", event.getRecordInfo().getDeviceId(),
  37 + event.getRecordInfo().getChannelId(), count,sumNum);
33 if (handlerMap.size() > 0) { 38 if (handlerMap.size() > 0) {
34 - for (RecordEndEventHandler recordEndEventHandler : handlerMap.values()) {  
35 - recordEndEventHandler.handler(event.getRecordInfo()); 39 + RecordEndEventHandler handler = handlerMap.get(deviceId + channelId);
  40 + if (handler !=null){
  41 + handler.handler(event.getRecordInfo());
  42 + if (count ==sumNum){
  43 + handlerMap.remove(deviceId + channelId);
  44 + }
36 } 45 }
37 } 46 }
38 - handlerMap.clear();  
39 } 47 }
40 48
  49 + /**
  50 + * 添加
  51 + * @param device
  52 + * @param channelId
  53 + * @param recordEndEventHandler
  54 + */
41 public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) { 55 public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) {
42 handlerMap.put(device + channelId, recordEndEventHandler); 56 handlerMap.put(device + channelId, recordEndEventHandler);
43 } 57 }
  58 + /**
  59 + * 添加
  60 + * @param device
  61 + * @param channelId
  62 + */
  63 + public void delEndEventHandler(String device, String channelId) {
  64 + handlerMap.remove(device + channelId);
  65 + }
  66 +
44 } 67 }
src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java
@@ -109,12 +109,18 @@ public class CatalogDataCatch { @@ -109,12 +109,18 @@ public class CatalogDataCatch {
109 109
110 for (String deviceId : keys) { 110 for (String deviceId : keys) {
111 CatalogData catalogData = data.get(deviceId); 111 CatalogData catalogData = data.get(deviceId);
112 - if ( catalogData.getLastTime().isBefore(instantBefore5S)) { // 超过五秒收不到消息任务超时, 只更新这一部分数据 112 + if ( catalogData.getLastTime().isBefore(instantBefore5S)) {
  113 + // 超过五秒收不到消息任务超时, 只更新这一部分数据, 收到数据与声明的总数一致,则重置通道数据,数据不全则只对收到的数据做更新操作
113 if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.runIng)) { 114 if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.runIng)) {
114 - storager.resetChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList()); 115 + if (catalogData.getTotal() == catalogData.getChannelList().size()) {
  116 + storager.resetChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList());
  117 + }else {
  118 + storager.updateChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList());
  119 + }
  120 + String errorMsg = "更新成功,共" + catalogData.getTotal() + "条,已更新" + catalogData.getChannelList().size() + "条";
  121 + catalogData.setErrorMsg(errorMsg);
115 if (catalogData.getTotal() != catalogData.getChannelList().size()) { 122 if (catalogData.getTotal() != catalogData.getChannelList().size()) {
116 - String errorMsg = "更新成功,共" + catalogData.getTotal() + "条,已更新" + catalogData.getChannelList().size() + "条";  
117 - catalogData.setErrorMsg(errorMsg); 123 +
118 } 124 }
119 }else if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.ready)) { 125 }else if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.ready)) {
120 String errorMsg = "同步失败,等待回复超时"; 126 String errorMsg = "同步失败,等待回复超时";
src/main/java/com/genersoft/iot/vmp/gb28181/session/RecordDataCatch.java
1 package com.genersoft.iot.vmp.gb28181.session; 1 package com.genersoft.iot.vmp.gb28181.session;
2 2
3 import com.genersoft.iot.vmp.gb28181.bean.*; 3 import com.genersoft.iot.vmp.gb28181.bean.*;
  4 +import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEventListener;
4 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; 5 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
5 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; 6 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
6 import com.genersoft.iot.vmp.vmanager.bean.WVPResult; 7 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
@@ -23,14 +24,17 @@ public class RecordDataCatch { @@ -23,14 +24,17 @@ public class RecordDataCatch {
23 24
24 @Autowired 25 @Autowired
25 private DeferredResultHolder deferredResultHolder; 26 private DeferredResultHolder deferredResultHolder;
  27 + @Autowired
  28 + private RecordEndEventListener recordEndEventListener;
26 29
27 30
28 - public int put(String deviceId, String sn, int sumNum, List<RecordItem> recordItems) { 31 + public int put(String deviceId,String channelId, String sn, int sumNum, List<RecordItem> recordItems) {
29 String key = deviceId + sn; 32 String key = deviceId + sn;
30 RecordInfo recordInfo = data.get(key); 33 RecordInfo recordInfo = data.get(key);
31 if (recordInfo == null) { 34 if (recordInfo == null) {
32 recordInfo = new RecordInfo(); 35 recordInfo = new RecordInfo();
33 recordInfo.setDeviceId(deviceId); 36 recordInfo.setDeviceId(deviceId);
  37 + recordInfo.setChannelId(channelId);
34 recordInfo.setSn(sn.trim()); 38 recordInfo.setSn(sn.trim());
35 recordInfo.setSumNum(sumNum); 39 recordInfo.setSumNum(sumNum);
36 recordInfo.setRecordList(Collections.synchronizedList(new ArrayList<>())); 40 recordInfo.setRecordList(Collections.synchronizedList(new ArrayList<>()));
@@ -67,6 +71,7 @@ public class RecordDataCatch { @@ -67,6 +71,7 @@ public class RecordDataCatch {
67 msg.setKey(msgKey); 71 msg.setKey(msgKey);
68 msg.setData(recordInfo); 72 msg.setData(recordInfo);
69 deferredResultHolder.invokeAllResult(msg); 73 deferredResultHolder.invokeAllResult(msg);
  74 + recordEndEventListener.delEndEventHandler(recordInfo.getDeviceId(),recordInfo.getChannelId());
70 data.remove(key); 75 data.remove(key);
71 } 76 }
72 } 77 }
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
@@ -4,6 +4,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; @@ -4,6 +4,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants;
4 import com.genersoft.iot.vmp.conf.UserSetting; 4 import com.genersoft.iot.vmp.conf.UserSetting;
5 import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; 5 import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
6 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; 6 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
  7 +import com.genersoft.iot.vmp.utils.JsonUtil;
7 import com.genersoft.iot.vmp.utils.redis.RedisUtil; 8 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
8 import gov.nist.javax.sip.message.SIPResponse; 9 import gov.nist.javax.sip.message.SIPResponse;
9 import org.springframework.beans.factory.annotation.Autowired; 10 import org.springframework.beans.factory.annotation.Autowired;
@@ -134,7 +135,7 @@ public class VideoStreamSessionManager { @@ -134,7 +135,7 @@ public class VideoStreamSessionManager {
134 List<SsrcTransaction> result= new ArrayList<>(); 135 List<SsrcTransaction> result= new ArrayList<>();
135 for (int i = 0; i < ssrcTransactionKeys.size(); i++) { 136 for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
136 String key = (String)ssrcTransactionKeys.get(i); 137 String key = (String)ssrcTransactionKeys.get(i);
137 - SsrcTransaction ssrcTransaction = (SsrcTransaction)RedisUtil.get(key); 138 + SsrcTransaction ssrcTransaction = JsonUtil.redisJsonToObject(key, SsrcTransaction.class);
138 result.add(ssrcTransaction); 139 result.add(ssrcTransaction);
139 } 140 }
140 return result; 141 return result;
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java
@@ -47,61 +47,65 @@ public class SIPSender { @@ -47,61 +47,65 @@ public class SIPSender {
47 } 47 }
48 48
49 public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, ParseException { 49 public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, ParseException {
50 - ViaHeader viaHeader = (ViaHeader)message.getHeader(ViaHeader.NAME);  
51 - String transport = "UDP";  
52 - if (viaHeader == null) {  
53 - logger.warn("[消息头缺失]: ViaHeader, 使用默认的UDP方式处理数据");  
54 - }else {  
55 - transport = viaHeader.getTransport();  
56 - }  
57 - if (message.getHeader(UserAgentHeader.NAME) == null) {  
58 - try {  
59 - message.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));  
60 - } catch (ParseException e) {  
61 - logger.error("添加UserAgentHeader失败", e); 50 + try {
  51 + ViaHeader viaHeader = (ViaHeader)message.getHeader(ViaHeader.NAME);
  52 + String transport = "UDP";
  53 + if (viaHeader == null) {
  54 + logger.warn("[消息头缺失]: ViaHeader, 使用默认的UDP方式处理数据");
  55 + }else {
  56 + transport = viaHeader.getTransport();
  57 + }
  58 + if (message.getHeader(UserAgentHeader.NAME) == null) {
  59 + try {
  60 + message.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  61 + } catch (ParseException e) {
  62 + logger.error("添加UserAgentHeader失败", e);
  63 + }
62 } 64 }
63 - }  
64 65
65 - CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME);  
66 - // 添加错误订阅  
67 - if (errorEvent != null) {  
68 - sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {  
69 - errorEvent.response(eventResult);  
70 - sipSubscribe.removeErrorSubscribe(eventResult.callId);  
71 - sipSubscribe.removeOkSubscribe(eventResult.callId);  
72 - }));  
73 - }  
74 - // 添加订阅  
75 - if (okEvent != null) {  
76 - sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {  
77 - okEvent.response(eventResult);  
78 - sipSubscribe.removeOkSubscribe(eventResult.callId);  
79 - sipSubscribe.removeErrorSubscribe(eventResult.callId);  
80 - });  
81 - }  
82 - if ("TCP".equals(transport)) {  
83 - SipProviderImpl tcpSipProvider = sipLayer.getTcpSipProvider(ip);  
84 - if (tcpSipProvider == null) {  
85 - logger.error("[发送信息失败] 未找到tcp://{}的监听信息", ip);  
86 - return; 66 + CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME);
  67 + // 添加错误订阅
  68 + if (errorEvent != null) {
  69 + sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
  70 + errorEvent.response(eventResult);
  71 + sipSubscribe.removeErrorSubscribe(eventResult.callId);
  72 + sipSubscribe.removeOkSubscribe(eventResult.callId);
  73 + }));
87 } 74 }
88 - if (message instanceof Request) {  
89 - tcpSipProvider.sendRequest((Request)message);  
90 - }else if (message instanceof Response) {  
91 - tcpSipProvider.sendResponse((Response)message); 75 + // 添加订阅
  76 + if (okEvent != null) {
  77 + sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
  78 + okEvent.response(eventResult);
  79 + sipSubscribe.removeOkSubscribe(eventResult.callId);
  80 + sipSubscribe.removeErrorSubscribe(eventResult.callId);
  81 + });
92 } 82 }
  83 + if ("TCP".equals(transport)) {
  84 + SipProviderImpl tcpSipProvider = sipLayer.getTcpSipProvider(ip);
  85 + if (tcpSipProvider == null) {
  86 + logger.error("[发送信息失败] 未找到tcp://{}的监听信息", ip);
  87 + return;
  88 + }
  89 + if (message instanceof Request) {
  90 + tcpSipProvider.sendRequest((Request)message);
  91 + }else if (message instanceof Response) {
  92 + tcpSipProvider.sendResponse((Response)message);
  93 + }
93 94
94 - } else if ("UDP".equals(transport)) {  
95 - SipProviderImpl sipProvider = sipLayer.getUdpSipProvider(ip);  
96 - if (sipProvider == null) {  
97 - logger.error("[发送信息失败] 未找到udp://{}的监听信息", ip);  
98 - return;  
99 - }  
100 - if (message instanceof Request) {  
101 - sipProvider.sendRequest((Request)message);  
102 - }else if (message instanceof Response) {  
103 - sipProvider.sendResponse((Response)message); 95 + } else if ("UDP".equals(transport)) {
  96 + SipProviderImpl sipProvider = sipLayer.getUdpSipProvider(ip);
  97 + if (sipProvider == null) {
  98 + logger.error("[发送信息失败] 未找到udp://{}的监听信息", ip);
  99 + return;
  100 + }
  101 + if (message instanceof Request) {
  102 + sipProvider.sendRequest((Request)message);
  103 + }else if (message instanceof Response) {
  104 + sipProvider.sendResponse((Response)message);
  105 + }
104 } 106 }
  107 + } finally {
  108 + logger.info("[SEND]:SUCCESS:{}", message);
105 } 109 }
106 } 110 }
107 111
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -182,7 +182,7 @@ public interface ISIPCommander { @@ -182,7 +182,7 @@ public interface ISIPCommander {
182 * @param channelId 预览通道 182 * @param channelId 预览通道
183 * @param recordCmdStr 录像命令:Record / StopRecord 183 * @param recordCmdStr 录像命令:Record / StopRecord
184 */ 184 */
185 - void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; 185 + void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
186 186
187 /** 187 /**
188 * 远程启动控制命令 188 * 远程启动控制命令
@@ -196,7 +196,7 @@ public interface ISIPCommander { @@ -196,7 +196,7 @@ public interface ISIPCommander {
196 * 196 *
197 * @param device 视频设备 197 * @param device 视频设备
198 */ 198 */
199 - void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; 199 + void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
200 200
201 /** 201 /**
202 * 报警复位命令 202 * 报警复位命令
@@ -205,7 +205,7 @@ public interface ISIPCommander { @@ -205,7 +205,7 @@ public interface ISIPCommander {
205 * @param alarmMethod 报警方式(可选) 205 * @param alarmMethod 报警方式(可选)
206 * @param alarmType 报警类型(可选) 206 * @param alarmType 报警类型(可选)
207 */ 207 */
208 - void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; 208 + void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
209 209
210 /** 210 /**
211 * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 211 * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
@@ -214,17 +214,19 @@ public interface ISIPCommander { @@ -214,17 +214,19 @@ public interface ISIPCommander {
214 * @param channelId 预览通道 214 * @param channelId 预览通道
215 */ 215 */
216 void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException; 216 void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException;
217 - 217 +
218 /** 218 /**
219 * 看守位控制命令 219 * 看守位控制命令
220 - *  
221 - * @param device 视频设备  
222 - * @param enabled 看守位使能:1 = 开启,0 = 关闭  
223 - * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)  
224 - * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255  
225 - */  
226 - void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;  
227 - 220 + *
  221 + * @param device 视频设备
  222 + * @param channelId 通道id,非通道则是设备本身
  223 + * @param frontCmd 上级平台的指令,如果存在则直接下发
  224 + * @param enabled 看守位使能:1 = 开启,0 = 关闭
  225 + * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
  226 + * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
  227 + */
  228 + void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
  229 +
228 /** 230 /**
229 * 设备配置命令 231 * 设备配置命令
230 * 232 *
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
@@ -51,11 +51,11 @@ public interface ISIPCommanderForPlatform { @@ -51,11 +51,11 @@ public interface ISIPCommanderForPlatform {
51 /** 51 /**
52 * 向上级回复DeviceInfo查询信息 52 * 向上级回复DeviceInfo查询信息
53 * @param parentPlatform 平台信息 53 * @param parentPlatform 平台信息
54 - * @param sn  
55 - * @param fromTag 54 + * @param sn SN
  55 + * @param fromTag FROM头的tag信息
56 * @return 56 * @return
57 */ 57 */
58 - void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException; 58 + void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException;
59 59
60 /** 60 /**
61 * 向上级回复DeviceStatus查询信息 61 * 向上级回复DeviceStatus查询信息
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -32,6 +32,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -32,6 +32,7 @@ import org.springframework.beans.factory.annotation.Autowired;
32 import org.springframework.context.annotation.DependsOn; 32 import org.springframework.context.annotation.DependsOn;
33 import org.springframework.stereotype.Component; 33 import org.springframework.stereotype.Component;
34 import org.springframework.util.ObjectUtils; 34 import org.springframework.util.ObjectUtils;
  35 +import org.springframework.util.StringUtils;
35 36
36 import javax.sip.InvalidArgumentException; 37 import javax.sip.InvalidArgumentException;
37 import javax.sip.ResponseEvent; 38 import javax.sip.ResponseEvent;
@@ -711,7 +712,7 @@ public class SIPCommander implements ISIPCommander { @@ -711,7 +712,7 @@ public class SIPCommander implements ISIPCommander {
711 * @param recordCmdStr 录像命令:Record / StopRecord 712 * @param recordCmdStr 录像命令:Record / StopRecord
712 */ 713 */
713 @Override 714 @Override
714 - public void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { 715 + public void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
715 StringBuffer cmdXml = new StringBuffer(200); 716 StringBuffer cmdXml = new StringBuffer(200);
716 String charset = device.getCharset(); 717 String charset = device.getCharset();
717 cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n"); 718 cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
@@ -729,7 +730,7 @@ public class SIPCommander implements ISIPCommander { @@ -729,7 +730,7 @@ public class SIPCommander implements ISIPCommander {
729 730
730 731
731 Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); 732 Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
732 - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); 733 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
733 } 734 }
734 735
735 /** 736 /**
@@ -763,7 +764,7 @@ public class SIPCommander implements ISIPCommander { @@ -763,7 +764,7 @@ public class SIPCommander implements ISIPCommander {
763 * @param guardCmdStr "SetGuard"/"ResetGuard" 764 * @param guardCmdStr "SetGuard"/"ResetGuard"
764 */ 765 */
765 @Override 766 @Override
766 - public void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { 767 + public void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
767 768
768 StringBuffer cmdXml = new StringBuffer(200); 769 StringBuffer cmdXml = new StringBuffer(200);
769 String charset = device.getCharset(); 770 String charset = device.getCharset();
@@ -778,7 +779,7 @@ public class SIPCommander implements ISIPCommander { @@ -778,7 +779,7 @@ public class SIPCommander implements ISIPCommander {
778 779
779 780
780 Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); 781 Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
781 - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); 782 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
782 } 783 }
783 784
784 /** 785 /**
@@ -787,7 +788,7 @@ public class SIPCommander implements ISIPCommander { @@ -787,7 +788,7 @@ public class SIPCommander implements ISIPCommander {
787 * @param device 视频设备 788 * @param device 视频设备
788 */ 789 */
789 @Override 790 @Override
790 - public void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { 791 + public void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
791 792
792 StringBuffer cmdXml = new StringBuffer(200); 793 StringBuffer cmdXml = new StringBuffer(200);
793 String charset = device.getCharset(); 794 String charset = device.getCharset();
@@ -814,7 +815,7 @@ public class SIPCommander implements ISIPCommander { @@ -814,7 +815,7 @@ public class SIPCommander implements ISIPCommander {
814 815
815 816
816 Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); 817 Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
817 - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); 818 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
818 } 819 }
819 820
820 /** 821 /**
@@ -850,12 +851,14 @@ public class SIPCommander implements ISIPCommander { @@ -850,12 +851,14 @@ public class SIPCommander implements ISIPCommander {
850 * 看守位控制命令 851 * 看守位控制命令
851 * 852 *
852 * @param device 视频设备 853 * @param device 视频设备
  854 + * @param channelId 通道id,非通道则是设备本身
  855 + * @param frontCmd 上级平台的指令,如果存在则直接下发
853 * @param enabled 看守位使能:1 = 开启,0 = 关闭 856 * @param enabled 看守位使能:1 = 开启,0 = 关闭
854 * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) 857 * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
855 * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 858 * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
856 */ 859 */
857 @Override 860 @Override
858 - public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { 861 + public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
859 862
860 StringBuffer cmdXml = new StringBuffer(200); 863 StringBuffer cmdXml = new StringBuffer(200);
861 String charset = device.getCharset(); 864 String charset = device.getCharset();
@@ -890,7 +893,7 @@ public class SIPCommander implements ISIPCommander { @@ -890,7 +893,7 @@ public class SIPCommander implements ISIPCommander {
890 893
891 894
892 Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); 895 Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
893 - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); 896 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
894 } 897 }
895 898
896 /** 899 /**
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
1 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; 1 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
2 2
3 import com.alibaba.fastjson2.JSON; 3 import com.alibaba.fastjson2.JSON;
  4 +import com.genersoft.iot.vmp.conf.DynamicTask;
4 import com.genersoft.iot.vmp.gb28181.SipLayer; 5 import com.genersoft.iot.vmp.gb28181.SipLayer;
5 import com.genersoft.iot.vmp.gb28181.bean.*; 6 import com.genersoft.iot.vmp.gb28181.bean.*;
6 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; 7 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
@@ -61,6 +62,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -61,6 +62,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
61 @Autowired 62 @Autowired
62 private SIPSender sipSender; 63 private SIPSender sipSender;
63 64
  65 + @Autowired
  66 + private DynamicTask dynamicTask;
  67 +
64 @Override 68 @Override
65 public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { 69 public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
66 register(parentPlatform, null, null, errorEvent, okEvent, false, true); 70 register(parentPlatform, null, null, errorEvent, okEvent, false, true);
@@ -109,13 +113,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -109,13 +113,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
109 public String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException { 113 public String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException {
110 String characterSet = parentPlatform.getCharacterSet(); 114 String characterSet = parentPlatform.getCharacterSet();
111 StringBuffer keepaliveXml = new StringBuffer(200); 115 StringBuffer keepaliveXml = new StringBuffer(200);
112 - keepaliveXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");  
113 - keepaliveXml.append("<Notify>\r\n");  
114 - keepaliveXml.append("<CmdType>Keepalive</CmdType>\r\n");  
115 - keepaliveXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
116 - keepaliveXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");  
117 - keepaliveXml.append("<Status>OK</Status>\r\n");  
118 - keepaliveXml.append("</Notify>\r\n"); 116 + keepaliveXml.append("<?xml version=\"1.0\" encoding=\"")
  117 + .append(characterSet).append("\"?>\r\n")
  118 + .append("<Notify>\r\n")
  119 + .append("<CmdType>Keepalive</CmdType>\r\n")
  120 + .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
  121 + .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
  122 + .append("<Status>OK</Status>\r\n")
  123 + .append("</Notify>\r\n");
119 124
120 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); 125 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
121 126
@@ -133,7 +138,6 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -133,7 +138,6 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
133 * 向上级回复通道信息 138 * 向上级回复通道信息
134 * @param channel 通道信息 139 * @param channel 通道信息
135 * @param parentPlatform 平台信息 140 * @param parentPlatform 平台信息
136 - * @return  
137 */ 141 */
138 @Override 142 @Override
139 public void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException { 143 public void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException {
@@ -160,18 +164,18 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -160,18 +164,18 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
160 if ( parentPlatform ==null) { 164 if ( parentPlatform ==null) {
161 return ; 165 return ;
162 } 166 }
163 - sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0); 167 + sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0, true);
164 } 168 }
165 private String getCatalogXml(List<DeviceChannel> channels, String sn, ParentPlatform parentPlatform, int size) { 169 private String getCatalogXml(List<DeviceChannel> channels, String sn, ParentPlatform parentPlatform, int size) {
166 String characterSet = parentPlatform.getCharacterSet(); 170 String characterSet = parentPlatform.getCharacterSet();
167 StringBuffer catalogXml = new StringBuffer(600); 171 StringBuffer catalogXml = new StringBuffer(600);
168 - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet +"\"?>\r\n");  
169 - catalogXml.append("<Response>\r\n");  
170 - catalogXml.append("<CmdType>Catalog</CmdType>\r\n");  
171 - catalogXml.append("<SN>" +sn + "</SN>\r\n");  
172 - catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");  
173 - catalogXml.append("<SumNum>" + size + "</SumNum>\r\n");  
174 - catalogXml.append("<DeviceList Num=\"" + channels.size() +"\">\r\n"); 172 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet +"\"?>\r\n")
  173 + .append("<Response>\r\n")
  174 + .append("<CmdType>Catalog</CmdType>\r\n")
  175 + .append("<SN>" +sn + "</SN>\r\n")
  176 + .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
  177 + .append("<SumNum>" + size + "</SumNum>\r\n")
  178 + .append("<DeviceList Num=\"" + channels.size() +"\">\r\n");
175 if (channels.size() > 0) { 179 if (channels.size() > 0) {
176 for (DeviceChannel channel : channels) { 180 for (DeviceChannel channel : channels) {
177 if (parentPlatform.getServerGBId().equals(channel.getParentId())) { 181 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
@@ -222,7 +226,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -222,7 +226,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
222 return catalogXml.toString(); 226 return catalogXml.toString();
223 } 227 }
224 228
225 - private void sendCatalogResponse(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag, int index) throws SipException, InvalidArgumentException, ParseException { 229 + private void sendCatalogResponse(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag, int index, boolean sendAfterResponse) throws SipException, InvalidArgumentException, ParseException {
226 if (index >= channels.size()) { 230 if (index >= channels.size()) {
227 return; 231 return;
228 } 232 }
@@ -236,15 +240,49 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -236,15 +240,49 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
236 // callid 240 // callid
237 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); 241 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
238 242
239 - Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);  
240 - sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, eventResult -> {  
241 - int indexNext = index + parentPlatform.getCatalogGroup();  
242 - try {  
243 - sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext);  
244 - } catch (SipException | InvalidArgumentException | ParseException e) {  
245 - logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());  
246 - }  
247 - }); 243 + SIPRequest request = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);
  244 +
  245 + String timeoutTaskKey = "catalog_task_" + parentPlatform.getServerGBId() + sn;
  246 +
  247 + String callId = request.getCallIdHeader().getCallId();
  248 +
  249 + if (sendAfterResponse) {
  250 + // 默认按照收到200回复后发送下一条, 如果超时收不到回复,就以30毫秒的间隔直接发送。
  251 + dynamicTask.startDelay(timeoutTaskKey, ()->{
  252 + sipSubscribe.removeOkSubscribe(callId);
  253 + int indexNext = index + parentPlatform.getCatalogGroup();
  254 + try {
  255 + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false);
  256 + } catch (SipException | InvalidArgumentException | ParseException e) {
  257 + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
  258 + }
  259 + }, 3000);
  260 + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> {
  261 + logger.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg);
  262 + dynamicTask.stop(timeoutTaskKey);
  263 + }, eventResult -> {
  264 + dynamicTask.stop(timeoutTaskKey);
  265 + int indexNext = index + parentPlatform.getCatalogGroup();
  266 + try {
  267 + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, true);
  268 + } catch (SipException | InvalidArgumentException | ParseException e) {
  269 + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
  270 + }
  271 + });
  272 + }else {
  273 + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> {
  274 + logger.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg);
  275 + dynamicTask.stop(timeoutTaskKey);
  276 + }, null);
  277 + dynamicTask.startDelay(timeoutTaskKey, ()->{
  278 + int indexNext = index + parentPlatform.getCatalogGroup();
  279 + try {
  280 + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false);
  281 + } catch (SipException | InvalidArgumentException | ParseException e) {
  282 + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
  283 + }
  284 + }, 30);
  285 + }
248 } 286 }
249 287
250 /** 288 /**
@@ -255,7 +293,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -255,7 +293,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
255 * @return 293 * @return
256 */ 294 */
257 @Override 295 @Override
258 - public void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException { 296 + public void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException {
259 if (parentPlatform == null) { 297 if (parentPlatform == null) {
260 return; 298 return;
261 } 299 }
@@ -265,11 +303,11 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -265,11 +303,11 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
265 deviceInfoXml.append("<Response>\r\n"); 303 deviceInfoXml.append("<Response>\r\n");
266 deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n"); 304 deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
267 deviceInfoXml.append("<SN>" +sn + "</SN>\r\n"); 305 deviceInfoXml.append("<SN>" +sn + "</SN>\r\n");
268 - deviceInfoXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");  
269 - deviceInfoXml.append("<DeviceName>" + parentPlatform.getName() + "</DeviceName>\r\n");  
270 - deviceInfoXml.append("<Manufacturer>wvp</Manufacturer>\r\n");  
271 - deviceInfoXml.append("<Model>wvp-28181-2.0</Model>\r\n");  
272 - deviceInfoXml.append("<Firmware>2.0.202107</Firmware>\r\n"); 306 + deviceInfoXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  307 + deviceInfoXml.append("<DeviceName>" + device.getName() + "</DeviceName>\r\n");
  308 + deviceInfoXml.append("<Manufacturer>" + device.getManufacturer() + "</Manufacturer>\r\n");
  309 + deviceInfoXml.append("<Model>" + device.getModel() + "</Model>\r\n");
  310 + deviceInfoXml.append("<Firmware>" + device.getFirmware() + "</Firmware>\r\n");
273 deviceInfoXml.append("<Result>OK</Result>\r\n"); 311 deviceInfoXml.append("<Result>OK</Result>\r\n");
274 deviceInfoXml.append("</Response>\r\n"); 312 deviceInfoXml.append("</Response>\r\n");
275 313
@@ -294,15 +332,15 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -294,15 +332,15 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
294 String statusStr = (status==1)?"ONLINE":"OFFLINE"; 332 String statusStr = (status==1)?"ONLINE":"OFFLINE";
295 String characterSet = parentPlatform.getCharacterSet(); 333 String characterSet = parentPlatform.getCharacterSet();
296 StringBuffer deviceStatusXml = new StringBuffer(600); 334 StringBuffer deviceStatusXml = new StringBuffer(600);
297 - deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");  
298 - deviceStatusXml.append("<Response>\r\n");  
299 - deviceStatusXml.append("<CmdType>DeviceStatus</CmdType>\r\n");  
300 - deviceStatusXml.append("<SN>" +sn + "</SN>\r\n");  
301 - deviceStatusXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");  
302 - deviceStatusXml.append("<Result>OK</Result>\r\n");  
303 - deviceStatusXml.append("<Online>"+statusStr+"</Online>\r\n");  
304 - deviceStatusXml.append("<Status>OK</Status>\r\n");  
305 - deviceStatusXml.append("</Response>\r\n"); 335 + deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  336 + .append("<Response>\r\n")
  337 + .append("<CmdType>DeviceStatus</CmdType>\r\n")
  338 + .append("<SN>" +sn + "</SN>\r\n")
  339 + .append("<DeviceID>" + channelId + "</DeviceID>\r\n")
  340 + .append("<Result>OK</Result>\r\n")
  341 + .append("<Online>"+statusStr+"</Online>\r\n")
  342 + .append("<Status>OK</Status>\r\n")
  343 + .append("</Response>\r\n");
306 344
307 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); 345 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
308 346
@@ -321,18 +359,18 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -321,18 +359,18 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
321 359
322 String characterSet = parentPlatform.getCharacterSet(); 360 String characterSet = parentPlatform.getCharacterSet();
323 StringBuffer deviceStatusXml = new StringBuffer(600); 361 StringBuffer deviceStatusXml = new StringBuffer(600);
324 - deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");  
325 - deviceStatusXml.append("<Notify>\r\n");  
326 - deviceStatusXml.append("<CmdType>MobilePosition</CmdType>\r\n");  
327 - deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
328 - deviceStatusXml.append("<DeviceID>" + gpsMsgInfo.getId() + "</DeviceID>\r\n");  
329 - deviceStatusXml.append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n");  
330 - deviceStatusXml.append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n");  
331 - deviceStatusXml.append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n");  
332 - deviceStatusXml.append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n");  
333 - deviceStatusXml.append("<Direction>" + gpsMsgInfo.getDirection() + "</Direction>\r\n");  
334 - deviceStatusXml.append("<Altitude>" + gpsMsgInfo.getAltitude() + "</Altitude>\r\n");  
335 - deviceStatusXml.append("</Notify>\r\n"); 362 + deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  363 + .append("<Notify>\r\n")
  364 + .append("<CmdType>MobilePosition</CmdType>\r\n")
  365 + .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
  366 + .append("<DeviceID>" + gpsMsgInfo.getId() + "</DeviceID>\r\n")
  367 + .append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n")
  368 + .append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n")
  369 + .append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n")
  370 + .append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n")
  371 + .append("<Direction>" + gpsMsgInfo.getDirection() + "</Direction>\r\n")
  372 + .append("<Altitude>" + gpsMsgInfo.getAltitude() + "</Altitude>\r\n")
  373 + .append("</Notify>\r\n");
336 374
337 sendNotify(parentPlatform, deviceStatusXml.toString(), subscribeInfo, eventResult -> { 375 sendNotify(parentPlatform, deviceStatusXml.toString(), subscribeInfo, eventResult -> {
338 logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg); 376 logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg);
@@ -349,21 +387,21 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -349,21 +387,21 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
349 deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm)); 387 deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm));
350 String characterSet = parentPlatform.getCharacterSet(); 388 String characterSet = parentPlatform.getCharacterSet();
351 StringBuffer deviceStatusXml = new StringBuffer(600); 389 StringBuffer deviceStatusXml = new StringBuffer(600);
352 - deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");  
353 - deviceStatusXml.append("<Notify>\r\n");  
354 - deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");  
355 - deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
356 - deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");  
357 - deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");  
358 - deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");  
359 - deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");  
360 - deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");  
361 - deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");  
362 - deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");  
363 - deviceStatusXml.append("<info>\r\n");  
364 - deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");  
365 - deviceStatusXml.append("</info>\r\n");  
366 - deviceStatusXml.append("</Notify>\r\n"); 390 + deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  391 + .append("<Notify>\r\n")
  392 + .append("<CmdType>Alarm</CmdType>\r\n")
  393 + .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
  394 + .append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n")
  395 + .append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n")
  396 + .append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n")
  397 + .append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n")
  398 + .append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n")
  399 + .append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n")
  400 + .append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n")
  401 + .append("<info>\r\n")
  402 + .append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n")
  403 + .append("</info>\r\n")
  404 + .append("</Notify>\r\n");
367 405
368 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); 406 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
369 407
@@ -422,13 +460,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -422,13 +460,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
422 StringBuffer catalogXml = new StringBuffer(600); 460 StringBuffer catalogXml = new StringBuffer(600);
423 461
424 String characterSet = parentPlatform.getCharacterSet(); 462 String characterSet = parentPlatform.getCharacterSet();
425 - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");  
426 - catalogXml.append("<Notify>\r\n");  
427 - catalogXml.append("<CmdType>Catalog</CmdType>\r\n");  
428 - catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");  
429 - catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");  
430 - catalogXml.append("<SumNum>1</SumNum>\r\n");  
431 - catalogXml.append("<DeviceList Num=\"" + channels.size() + "\">\r\n"); 463 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  464 + .append("<Notify>\r\n")
  465 + .append("<CmdType>Catalog</CmdType>\r\n")
  466 + .append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
  467 + .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
  468 + .append("<SumNum>1</SumNum>\r\n")
  469 + .append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
432 if (channels.size() > 0) { 470 if (channels.size() > 0) {
433 for (DeviceChannel channel : channels) { 471 for (DeviceChannel channel : channels) {
434 if (parentPlatform.getServerGBId().equals(channel.getParentId())) { 472 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
@@ -449,16 +487,16 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -449,16 +487,16 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
449 catalogXml.append("<Parental>" + channel.getParental() + "</Parental>\r\n"); 487 catalogXml.append("<Parental>" + channel.getParental() + "</Parental>\r\n");
450 if (channel.getParental() == 0) { 488 if (channel.getParental() == 0) {
451 // 通道项 489 // 通道项
452 - catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n");  
453 - catalogXml.append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n");  
454 - catalogXml.append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n");  
455 - catalogXml.append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n"); 490 + catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n")
  491 + .append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n")
  492 + .append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n")
  493 + .append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n");
456 494
457 if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性 495 if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性
458 - catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n");  
459 - catalogXml.append("<Owner> " + channel.getOwner()+ "</Owner>\r\n");  
460 - catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n");  
461 - catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n"); 496 + catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n")
  497 + .append("<Owner> " + channel.getOwner()+ "</Owner>\r\n")
  498 + .append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n")
  499 + .append("<Address>" + channel.getAddress() + "</Address>\r\n");
462 } 500 }
463 if (!"presence".equals(subscribeInfo.getEventType())) { 501 if (!"presence".equals(subscribeInfo.getEventType())) {
464 catalogXml.append("<Event>" + type + "</Event>\r\n"); 502 catalogXml.append("<Event>" + type + "</Event>\r\n");
@@ -468,8 +506,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -468,8 +506,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
468 catalogXml.append("</Item>\r\n"); 506 catalogXml.append("</Item>\r\n");
469 } 507 }
470 } 508 }
471 - catalogXml.append("</DeviceList>\r\n");  
472 - catalogXml.append("</Notify>\r\n"); 509 + catalogXml.append("</DeviceList>\r\n")
  510 + .append("</Notify>\r\n");
473 return catalogXml.toString(); 511 return catalogXml.toString();
474 } 512 }
475 513
@@ -515,26 +553,26 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -515,26 +553,26 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
515 553
516 String characterSet = parentPlatform.getCharacterSet(); 554 String characterSet = parentPlatform.getCharacterSet();
517 StringBuffer catalogXml = new StringBuffer(600); 555 StringBuffer catalogXml = new StringBuffer(600);
518 - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");  
519 - catalogXml.append("<Notify>\r\n");  
520 - catalogXml.append("<CmdType>Catalog</CmdType>\r\n");  
521 - catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");  
522 - catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");  
523 - catalogXml.append("<SumNum>1</SumNum>\r\n");  
524 - catalogXml.append("<DeviceList Num=\" " + channels.size() + " \">\r\n"); 556 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  557 + .append("<Notify>\r\n")
  558 + .append("<CmdType>Catalog</CmdType>\r\n")
  559 + .append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
  560 + .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
  561 + .append("<SumNum>1</SumNum>\r\n")
  562 + .append("<DeviceList Num=\" " + channels.size() + " \">\r\n");
525 if (channels.size() > 0) { 563 if (channels.size() > 0) {
526 for (DeviceChannel channel : channels) { 564 for (DeviceChannel channel : channels) {
527 if (parentPlatform.getServerGBId().equals(channel.getParentId())) { 565 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
528 channel.setParentId(parentPlatform.getDeviceGBId()); 566 channel.setParentId(parentPlatform.getDeviceGBId());
529 } 567 }
530 - catalogXml.append("<Item>\r\n");  
531 - catalogXml.append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n");  
532 - catalogXml.append("<Event>" + type + "</Event>\r\n");  
533 - catalogXml.append("</Item>\r\n"); 568 + catalogXml.append("<Item>\r\n")
  569 + .append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n")
  570 + .append("<Event>" + type + "</Event>\r\n")
  571 + .append("</Item>\r\n");
534 } 572 }
535 } 573 }
536 - catalogXml.append("</DeviceList>\r\n");  
537 - catalogXml.append("</Notify>\r\n"); 574 + catalogXml.append("</DeviceList>\r\n")
  575 + .append("</Notify>\r\n");
538 return catalogXml.toString(); 576 return catalogXml.toString();
539 } 577 }
540 @Override 578 @Override
@@ -544,12 +582,12 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -544,12 +582,12 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
544 } 582 }
545 String characterSet = parentPlatform.getCharacterSet(); 583 String characterSet = parentPlatform.getCharacterSet();
546 StringBuffer recordXml = new StringBuffer(600); 584 StringBuffer recordXml = new StringBuffer(600);
547 - recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");  
548 - recordXml.append("<Response>\r\n");  
549 - recordXml.append("<CmdType>RecordInfo</CmdType>\r\n");  
550 - recordXml.append("<SN>" +recordInfo.getSn() + "</SN>\r\n");  
551 - recordXml.append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n");  
552 - recordXml.append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n"); 585 + recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  586 + .append("<Response>\r\n")
  587 + .append("<CmdType>RecordInfo</CmdType>\r\n")
  588 + .append("<SN>" +recordInfo.getSn() + "</SN>\r\n")
  589 + .append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n")
  590 + .append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
553 if (recordInfo.getRecordList() == null ) { 591 if (recordInfo.getRecordList() == null ) {
554 recordXml.append("<RecordList Num=\"0\">\r\n"); 592 recordXml.append("<RecordList Num=\"0\">\r\n");
555 }else { 593 }else {
@@ -558,12 +596,12 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -558,12 +596,12 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
558 for (RecordItem recordItem : recordInfo.getRecordList()) { 596 for (RecordItem recordItem : recordInfo.getRecordList()) {
559 recordXml.append("<Item>\r\n"); 597 recordXml.append("<Item>\r\n");
560 if (deviceChannel != null) { 598 if (deviceChannel != null) {
561 - recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n");  
562 - recordXml.append("<Name>" + recordItem.getName() + "</Name>\r\n");  
563 - recordXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n");  
564 - recordXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n");  
565 - recordXml.append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n");  
566 - recordXml.append("<Type>" + recordItem.getType() + "</Type>\r\n"); 599 + recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n")
  600 + .append("<Name>" + recordItem.getName() + "</Name>\r\n")
  601 + .append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n")
  602 + .append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n")
  603 + .append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n")
  604 + .append("<Type>" + recordItem.getType() + "</Type>\r\n");
567 if (!ObjectUtils.isEmpty(recordItem.getFileSize())) { 605 if (!ObjectUtils.isEmpty(recordItem.getFileSize())) {
568 recordXml.append("<FileSize>" + recordItem.getFileSize() + "</FileSize>\r\n"); 606 recordXml.append("<FileSize>" + recordItem.getFileSize() + "</FileSize>\r\n");
569 } 607 }
@@ -576,8 +614,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -576,8 +614,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
576 } 614 }
577 } 615 }
578 616
579 - recordXml.append("</RecordList>\r\n");  
580 - recordXml.append("</Response>\r\n"); 617 + recordXml.append("</RecordList>\r\n")
  618 + .append("</Response>\r\n");
581 619
582 // callid 620 // callid
583 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); 621 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
@@ -596,13 +634,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -596,13 +634,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
596 634
597 String characterSet = parentPlatform.getCharacterSet(); 635 String characterSet = parentPlatform.getCharacterSet();
598 StringBuffer mediaStatusXml = new StringBuffer(200); 636 StringBuffer mediaStatusXml = new StringBuffer(200);
599 - mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");  
600 - mediaStatusXml.append("<Notify>\r\n");  
601 - mediaStatusXml.append("<CmdType>MediaStatus</CmdType>\r\n");  
602 - mediaStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
603 - mediaStatusXml.append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n");  
604 - mediaStatusXml.append("<NotifyType>121</NotifyType>\r\n");  
605 - mediaStatusXml.append("</Notify>\r\n"); 637 + mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  638 + .append("<Notify>\r\n")
  639 + .append("<CmdType>MediaStatus</CmdType>\r\n")
  640 + .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
  641 + .append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n")
  642 + .append("<NotifyType>121</NotifyType>\r\n")
  643 + .append("</Notify>\r\n");
606 644
607 SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, mediaStatusXml.toString(), 645 SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, mediaStatusXml.toString(),
608 sendRtpItem); 646 sendRtpItem);
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
@@ -16,7 +16,6 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -16,7 +16,6 @@ import org.springframework.beans.factory.annotation.Autowired;
16 16
17 import javax.sip.*; 17 import javax.sip.*;
18 import javax.sip.address.Address; 18 import javax.sip.address.Address;
19 -import javax.sip.address.AddressFactory;  
20 import javax.sip.address.SipURI; 19 import javax.sip.address.SipURI;
21 import javax.sip.header.ContentTypeHeader; 20 import javax.sip.header.ContentTypeHeader;
22 import javax.sip.header.ExpiresHeader; 21 import javax.sip.header.ExpiresHeader;
@@ -42,15 +41,6 @@ public abstract class SIPRequestProcessorParent { @@ -42,15 +41,6 @@ public abstract class SIPRequestProcessorParent {
42 @Autowired 41 @Autowired
43 private SIPSender sipSender; 42 private SIPSender sipSender;
44 43
45 - public AddressFactory getAddressFactory() {  
46 - try {  
47 - return SipFactory.getInstance().createAddressFactory();  
48 - } catch (PeerUnavailableException e) {  
49 - e.printStackTrace();  
50 - }  
51 - return null;  
52 - }  
53 -  
54 public HeaderFactory getHeaderFactory() { 44 public HeaderFactory getHeaderFactory() {
55 try { 45 try {
56 return SipFactory.getInstance().createHeaderFactory(); 46 return SipFactory.getInstance().createHeaderFactory();
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -275,7 +275,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -275,7 +275,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
275 } 275 }
276 return; 276 return;
277 } else { 277 } else {
278 - logger.info("通道不存在,返回404"); 278 + logger.info("通道不存在,返回404: {}", channelId);
279 try { 279 try {
280 // 通道不存在,发404,资源不存在 280 // 通道不存在,发404,资源不存在
281 responseAck(request, Response.NOT_FOUND); 281 responseAck(request, Response.NOT_FOUND);
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
2 2
  3 +import com.genersoft.iot.vmp.conf.ServiceInfo;
3 import com.genersoft.iot.vmp.conf.SipConfig; 4 import com.genersoft.iot.vmp.conf.SipConfig;
4 import com.genersoft.iot.vmp.conf.UserSetting; 5 import com.genersoft.iot.vmp.conf.UserSetting;
5 import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper; 6 import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
@@ -82,6 +83,19 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen @@ -82,6 +83,19 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
82 RequestEventExt evtExt = (RequestEventExt) evt; 83 RequestEventExt evtExt = (RequestEventExt) evt;
83 String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort(); 84 String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort();
84 logger.info("[注册请求] 开始处理: {}", requestAddress); 85 logger.info("[注册请求] 开始处理: {}", requestAddress);
  86 +// MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
  87 +// QueryExp protocol = Query.match(Query.attr("protocol"), Query.value("HTTP/1.1"));
  88 +//// ObjectName name = new ObjectName("*:type=Connector,*");
  89 +// ObjectName name = new ObjectName("*:*");
  90 +// Set<ObjectName> objectNames = beanServer.queryNames(name, protocol);
  91 +// for (ObjectName objectName : objectNames) {
  92 +// String catalina = objectName.getDomain();
  93 +// if ("Catalina".equals(catalina)) {
  94 +// System.out.println(objectName.getKeyProperty("port"));
  95 +// }
  96 +// }
  97 +
  98 + System.out.println(ServiceInfo.getServerPort());
85 SIPRequest request = (SIPRequest)evt.getRequest(); 99 SIPRequest request = (SIPRequest)evt.getRequest();
86 Response response = null; 100 Response response = null;
87 boolean passwordCorrect = false; 101 boolean passwordCorrect = false;
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.cmd; 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.cmd;
2 2
3 -import com.genersoft.iot.vmp.VManageBootstrap; 3 +import com.genersoft.iot.vmp.common.enums.DeviceControlType;
4 import com.genersoft.iot.vmp.gb28181.bean.Device; 4 import com.genersoft.iot.vmp.gb28181.bean.Device;
  5 +import com.genersoft.iot.vmp.gb28181.bean.DragZoomRequest;
  6 +import com.genersoft.iot.vmp.gb28181.bean.HomePositionRequest;
5 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; 7 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  8 +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
6 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; 9 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
7 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; 10 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; 11 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
@@ -19,17 +22,14 @@ import org.springframework.beans.factory.annotation.Qualifier; @@ -19,17 +22,14 @@ import org.springframework.beans.factory.annotation.Qualifier;
19 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 22 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
20 import org.springframework.stereotype.Component; 23 import org.springframework.stereotype.Component;
21 import org.springframework.util.ObjectUtils; 24 import org.springframework.util.ObjectUtils;
22 -import org.springframework.util.StringUtils;  
23 25
24 import javax.sip.*; 26 import javax.sip.*;
25 import javax.sip.address.SipURI; 27 import javax.sip.address.SipURI;
26 -import javax.sip.header.HeaderAddress;  
27 -import javax.sip.header.ToHeader;  
28 import javax.sip.message.Response; 28 import javax.sip.message.Response;
29 import java.text.ParseException; 29 import java.text.ParseException;
30 -import java.util.Iterator; 30 +import java.util.List;
31 31
32 -import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; 32 +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.*;
33 33
34 @Component 34 @Component
35 public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { 35 public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
@@ -81,7 +81,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent @@ -81,7 +81,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
81 } catch (InvalidArgumentException | ParseException | SipException e) { 81 } catch (InvalidArgumentException | ParseException | SipException e) {
82 logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); 82 logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
83 } 83 }
84 - taskExecutor.execute(()->{ 84 + taskExecutor.execute(() -> {
85 // 远程启动 85 // 远程启动
86 // try { 86 // try {
87 // Thread.sleep(3000); 87 // Thread.sleep(3000);
@@ -101,13 +101,12 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent @@ -101,13 +101,12 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
101 // logger.error("[任务执行失败] 服务重启: {}", e.getMessage()); 101 // logger.error("[任务执行失败] 服务重启: {}", e.getMessage());
102 // } 102 // }
103 }); 103 });
104 - } else {  
105 - // 远程启动指定设备  
106 } 104 }
107 } 105 }
108 - // 云台/前端控制命令  
109 - if (!ObjectUtils.isEmpty(getText(rootElement,"PTZCmd")) && !parentPlatform.getServerGBId().equals(targetGBId)) {  
110 - String cmdString = getText(rootElement,"PTZCmd"); 106 + DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement);
  107 + logger.info("[接受deviceControl命令] 命令: {}", deviceControlType);
  108 + if (!ObjectUtils.isEmpty(deviceControlType) && !parentPlatform.getServerGBId().equals(targetGBId)) {
  109 + //判断是否存在该通道
111 Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); 110 Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
112 if (deviceForPlatform == null) { 111 if (deviceForPlatform == null) {
113 try { 112 try {
@@ -117,25 +116,240 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent @@ -117,25 +116,240 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
117 } 116 }
118 return; 117 return;
119 } 118 }
120 - try {  
121 - cmder.fronEndCmd(deviceForPlatform, channelId, cmdString, eventResult -> {  
122 - // 失败的回复  
123 - try {  
124 - responseAck(request, eventResult.statusCode, eventResult.msg);  
125 - } catch (SipException | InvalidArgumentException | ParseException e) {  
126 - logger.error("[命令发送失败] 云台/前端回复: {}", e.getMessage());  
127 - }  
128 - }, eventResult -> {  
129 - // 成功的回复  
130 - try {  
131 - responseAck(request, eventResult.statusCode);  
132 - } catch (SipException | InvalidArgumentException | ParseException e) {  
133 - logger.error("[命令发送失败] 云台/前端回复: {}", e.getMessage());  
134 - }  
135 - });  
136 - } catch (InvalidArgumentException | SipException | ParseException e) {  
137 - logger.error("[命令发送失败] 云台/前端: {}", e.getMessage()); 119 + switch (deviceControlType) {
  120 + case PTZ:
  121 + handlePtzCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.PTZ);
  122 + break;
  123 + case ALARM:
  124 + handleAlarmCmd(deviceForPlatform, rootElement, request);
  125 + break;
  126 + case GUARD:
  127 + handleGuardCmd(deviceForPlatform, rootElement, request, DeviceControlType.GUARD);
  128 + break;
  129 + case RECORD:
  130 + handleRecordCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.RECORD);
  131 + break;
  132 + case I_FRAME:
  133 + handleIFameCmd(deviceForPlatform, request, channelId);
  134 + break;
  135 + case TELE_BOOT:
  136 + handleTeleBootCmd(deviceForPlatform, request);
  137 + break;
  138 + case DRAG_ZOOM_IN:
  139 + handleDragZoom(deviceForPlatform, channelId, rootElement, request, DeviceControlType.DRAG_ZOOM_IN);
  140 + break;
  141 + case DRAG_ZOOM_OUT:
  142 + handleDragZoom(deviceForPlatform, channelId, rootElement, request, DeviceControlType.DRAG_ZOOM_OUT);
  143 + break;
  144 + case HOME_POSITION:
  145 + handleHomePositionCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.HOME_POSITION);
  146 + break;
  147 + default:
  148 + break;
138 } 149 }
139 } 150 }
140 } 151 }
  152 +
  153 + /**
  154 + * 处理云台指令
  155 + *
  156 + * @param device 设备
  157 + * @param channelId 通道id
  158 + * @param rootElement
  159 + * @param request
  160 + */
  161 + private void handlePtzCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) {
  162 + String cmdString = getText(rootElement, type.getVal());
  163 + try {
  164 + cmder.fronEndCmd(device, channelId, cmdString,
  165 + errorResult -> onError(request, errorResult),
  166 + okResult -> onOk(request, okResult));
  167 + } catch (InvalidArgumentException | SipException | ParseException e) {
  168 + logger.error("[命令发送失败] 云台/前端: {}", e.getMessage());
  169 + }
  170 + }
  171 +
  172 + /**
  173 + * 处理强制关键帧
  174 + *
  175 + * @param device 设备
  176 + * @param channelId 通道id
  177 + */
  178 + private void handleIFameCmd(Device device, SIPRequest request, String channelId) {
  179 + try {
  180 + cmder.iFrameCmd(device, channelId);
  181 + responseAck(request, Response.OK);
  182 + } catch (InvalidArgumentException | SipException | ParseException e) {
  183 + logger.error("[命令发送失败] 强制关键帧: {}", e.getMessage());
  184 + }
  185 + }
  186 +
  187 + /**
  188 + * 处理重启命令
  189 + *
  190 + * @param device 设备信息
  191 + */
  192 + private void handleTeleBootCmd(Device device, SIPRequest request) {
  193 + try {
  194 + cmder.teleBootCmd(device);
  195 + responseAck(request, Response.OK);
  196 + } catch (InvalidArgumentException | SipException | ParseException e) {
  197 + logger.error("[命令发送失败] 重启: {}", e.getMessage());
  198 + }
  199 +
  200 + }
  201 +
  202 + /**
  203 + * 处理拉框控制***
  204 + *
  205 + * @param device 设备信息
  206 + * @param channelId 通道id
  207 + * @param rootElement 根节点
  208 + * @param type 消息类型
  209 + */
  210 + private void handleDragZoom(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) {
  211 + try {
  212 + DragZoomRequest dragZoomRequest = loadElement(rootElement, DragZoomRequest.class);
  213 + DragZoomRequest.DragZoom dragZoom = dragZoomRequest.getDragZoomIn();
  214 + if (dragZoom == null) {
  215 + dragZoom = dragZoomRequest.getDragZoomOut();
  216 + }
  217 + StringBuffer cmdXml = new StringBuffer(200);
  218 + cmdXml.append("<" + type.getVal() + ">\r\n");
  219 + cmdXml.append("<Length>" + dragZoom.getLength() + "</Length>\r\n");
  220 + cmdXml.append("<Width>" + dragZoom.getWidth() + "</Width>\r\n");
  221 + cmdXml.append("<MidPointX>" + dragZoom.getMidPointX() + "</MidPointX>\r\n");
  222 + cmdXml.append("<MidPointY>" + dragZoom.getMidPointY() + "</MidPointY>\r\n");
  223 + cmdXml.append("<LengthX>" + dragZoom.getLengthX() + "</LengthX>\r\n");
  224 + cmdXml.append("<LengthY>" + dragZoom.getLengthY() + "</LengthY>\r\n");
  225 + cmdXml.append("</" + type.getVal() + ">\r\n");
  226 + cmder.dragZoomCmd(device, channelId, cmdXml.toString());
  227 + responseAck(request, Response.OK);
  228 + } catch (Exception e) {
  229 + logger.error("[命令发送失败] 拉框控制: {}", e.getMessage());
  230 + }
  231 +
  232 + }
  233 +
  234 + /**
  235 + * 处理看守位命令***
  236 + *
  237 + * @param device 设备信息
  238 + * @param channelId 通道id
  239 + * @param rootElement 根节点
  240 + * @param request 请求信息
  241 + * @param type 消息类型
  242 + */
  243 + private void handleHomePositionCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) {
  244 + try {
  245 + HomePositionRequest homePosition = loadElement(rootElement, HomePositionRequest.class);
  246 + //获取整个消息主体,我们只需要修改请求头即可
  247 + HomePositionRequest.HomePosition info = homePosition.getHomePosition();
  248 + cmder.homePositionCmd(device, channelId, info.getEnabled(), info.getResetTime(), info.getPresetIndex(),
  249 + errorResult -> onError(request, errorResult),
  250 + okResult -> onOk(request, okResult));
  251 + } catch (Exception e) {
  252 + logger.error("[命令发送失败] 看守位设置: {}", e.getMessage());
  253 + }
  254 + }
  255 +
  256 + /**
  257 + * 处理告警消息***
  258 + *
  259 + * @param device 设备信息
  260 + * @param rootElement 根节点
  261 + * @param request 请求信息
  262 + */
  263 + private void handleAlarmCmd(Device device, Element rootElement, SIPRequest request) {
  264 + //告警方法
  265 + String alarmMethod = "";
  266 + //告警类型
  267 + String alarmType = "";
  268 + List<Element> info = rootElement.elements("Info");
  269 + if (info != null) {
  270 + for (Element element : info) {
  271 + alarmMethod = getText(element, "AlarmMethod");
  272 + alarmType = getText(element, "AlarmType");
  273 + }
  274 + }
  275 + try {
  276 + cmder.alarmCmd(device, alarmMethod, alarmType,
  277 + errorResult -> onError(request, errorResult),
  278 + okResult -> onOk(request, okResult));
  279 + } catch (InvalidArgumentException | SipException | ParseException e) {
  280 + logger.error("[命令发送失败] 告警消息: {}", e.getMessage());
  281 + }
  282 + }
  283 +
  284 + /**
  285 + * 处理录像控制
  286 + *
  287 + * @param device 设备信息
  288 + * @param channelId 通道id
  289 + * @param rootElement 根节点
  290 + * @param request 请求信息
  291 + * @param type 消息类型
  292 + */
  293 + private void handleRecordCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) {
  294 + //获取整个消息主体,我们只需要修改请求头即可
  295 + String cmdString = getText(rootElement, type.getVal());
  296 + try {
  297 + cmder.recordCmd(device, channelId, cmdString,
  298 + errorResult -> onError(request, errorResult),
  299 + okResult -> onOk(request, okResult));
  300 + } catch (InvalidArgumentException | SipException | ParseException e) {
  301 + logger.error("[命令发送失败] 录像控制: {}", e.getMessage());
  302 + }
  303 + }
  304 +
  305 + /**
  306 + * 处理报警布防/撤防命令
  307 + *
  308 + * @param device 设备信息
  309 + * @param rootElement 根节点
  310 + * @param request 请求信息
  311 + * @param type 消息类型
  312 + */
  313 + private void handleGuardCmd(Device device, Element rootElement, SIPRequest request, DeviceControlType type) {
  314 + //获取整个消息主体,我们只需要修改请求头即可
  315 + String cmdString = getText(rootElement, type.getVal());
  316 + try {
  317 + cmder.guardCmd(device, cmdString,
  318 + errorResult -> onError(request, errorResult),
  319 + okResult -> onOk(request, okResult));
  320 + } catch (InvalidArgumentException | SipException | ParseException e) {
  321 + logger.error("[命令发送失败] 布防/撤防命令: {}", e.getMessage());
  322 + }
  323 + }
  324 +
  325 +
  326 + /**
  327 + * 错误响应处理
  328 + *
  329 + * @param request 请求
  330 + * @param eventResult 响应结构
  331 + */
  332 + private void onError(SIPRequest request, SipSubscribe.EventResult eventResult) {
  333 + // 失败的回复
  334 + try {
  335 + responseAck(request, eventResult.statusCode, eventResult.msg);
  336 + } catch (SipException | InvalidArgumentException | ParseException e) {
  337 + logger.error("[命令发送失败] 回复: {}", e.getMessage());
  338 + }
  339 + }
  340 +
  341 + /**
  342 + * 成功响应处理
  343 + *
  344 + * @param request 请求
  345 + * @param eventResult 响应结构
  346 + */
  347 + private void onOk(SIPRequest request, SipSubscribe.EventResult eventResult) {
  348 + // 成功的回复
  349 + try {
  350 + responseAck(request, eventResult.statusCode);
  351 + } catch (SipException | InvalidArgumentException | ParseException e) {
  352 + logger.error("[命令发送失败] 回复: {}", e.getMessage());
  353 + }
  354 + }
141 } 355 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
@@ -181,11 +181,14 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @@ -181,11 +181,14 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
181 } 181 }
182 } 182 }
183 logger.info("[收到报警通知]内容:{}", JSON.toJSONString(deviceAlarm)); 183 logger.info("[收到报警通知]内容:{}", JSON.toJSONString(deviceAlarm));
184 - if ("7".equals(deviceAlarm.getAlarmMethod()) ) { 184 + // 作者自用判断,其他小伙伴需要此消息可以自行修改,但是不要提在pr里
  185 + if (DeviceAlarmMethod.Other.getVal() == Integer.parseInt(deviceAlarm.getAlarmMethod())) {
185 // 发送给平台的报警信息。 发送redis通知 186 // 发送给平台的报警信息。 发送redis通知
  187 + logger.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm));
186 AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage(); 188 AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
187 alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod())); 189 alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
188 alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription()); 190 alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
  191 + alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
189 alarmChannelMessage.setGbId(channelId); 192 alarmChannelMessage.setGbId(channelId);
190 redisCatchStorage.sendAlarmMsg(alarmChannelMessage); 193 redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
191 continue; 194 continue;
@@ -264,6 +267,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @@ -264,6 +267,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
264 alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod())); 267 alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
265 alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription()); 268 alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
266 alarmChannelMessage.setGbId(channelId); 269 alarmChannelMessage.setGbId(channelId);
  270 + alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
267 redisCatchStorage.sendAlarmMsg(alarmChannelMessage); 271 redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
268 return; 272 return;
269 } 273 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java
1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd; 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd;
2 2
3 import com.genersoft.iot.vmp.conf.SipConfig; 3 import com.genersoft.iot.vmp.conf.SipConfig;
4 -import com.genersoft.iot.vmp.gb28181.bean.*; 4 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  5 +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  6 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
5 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 7 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
6 -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;  
7 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; 8 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; 9 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
9 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; 10 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
@@ -63,7 +64,6 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem @@ -63,7 +64,6 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem
63 @Override 64 @Override
64 public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { 65 public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
65 66
66 - String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + parentPlatform.getServerGBId();  
67 FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); 67 FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
68 try { 68 try {
69 // 回复200 OK 69 // 回复200 OK
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java
@@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; @@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
6 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; 6 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
7 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; 7 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler; 8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
  9 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
9 import gov.nist.javax.sip.message.SIPRequest; 10 import gov.nist.javax.sip.message.SIPRequest;
10 import org.dom4j.Element; 11 import org.dom4j.Element;
11 import org.slf4j.Logger; 12 import org.slf4j.Logger;
@@ -21,6 +22,8 @@ import javax.sip.header.FromHeader; @@ -21,6 +22,8 @@ import javax.sip.header.FromHeader;
21 import javax.sip.message.Response; 22 import javax.sip.message.Response;
22 import java.text.ParseException; 23 import java.text.ParseException;
23 24
  25 +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
  26 +
24 @Component 27 @Component
25 public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { 28 public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
26 29
@@ -32,6 +35,8 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp @@ -32,6 +35,8 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp
32 35
33 @Autowired 36 @Autowired
34 private SIPCommanderFroPlatform cmderFroPlatform; 37 private SIPCommanderFroPlatform cmderFroPlatform;
  38 + @Autowired
  39 + private IVideoManagerStorage storager;
35 40
36 @Override 41 @Override
37 public void afterPropertiesSet() throws Exception { 42 public void afterPropertiesSet() throws Exception {
@@ -52,10 +57,20 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp @@ -52,10 +57,20 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp
52 responseAck((SIPRequest) evt.getRequest(), Response.OK); 57 responseAck((SIPRequest) evt.getRequest(), Response.OK);
53 } catch (SipException | InvalidArgumentException | ParseException e) { 58 } catch (SipException | InvalidArgumentException | ParseException e) {
54 logger.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage()); 59 logger.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage());
  60 + return;
55 } 61 }
56 String sn = rootElement.element("SN").getText(); 62 String sn = rootElement.element("SN").getText();
  63 + /*根据WVP原有的数据结构,设备和通道是分开放置,设备信息都是存放在设备表里,通道表里的设备信息不可作为真实信息处理
  64 + 大部分NVR/IPC设备对他的通道信息实现都是返回默认的值没有什么参考价值。NVR/IPC通道我们统一使用设备表的设备信息来作为返回。
  65 + 我们这里使用查询数据库的方式来实现这个设备信息查询的功能,在其他地方对设备信息更新达到正确的目的。*/
  66 + String channelId = getText(rootElement, "DeviceID");
  67 + Device device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
  68 + if (device ==null){
  69 + logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId);
  70 + return;
  71 + }
57 try { 72 try {
58 - cmderFroPlatform.deviceInfoResponse(parentPlatform, sn, fromHeader.getTag()); 73 + cmderFroPlatform.deviceInfoResponse(parentPlatform,device, sn, fromHeader.getTag());
59 } catch (SipException | InvalidArgumentException | ParseException e) { 74 } catch (SipException | InvalidArgumentException | ParseException e) {
60 logger.error("[命令发送失败] 国标级联 DeviceInfo查询回复: {}", e.getMessage()); 75 logger.error("[命令发送失败] 国标级联 DeviceInfo查询回复: {}", e.getMessage());
61 } 76 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
@@ -102,8 +102,9 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent @@ -102,8 +102,9 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
102 Element recordListElement = rootElementForCharset.element("RecordList"); 102 Element recordListElement = rootElementForCharset.element("RecordList");
103 if (recordListElement == null || sumNum == 0) { 103 if (recordListElement == null || sumNum == 0) {
104 logger.info("无录像数据"); 104 logger.info("无录像数据");
  105 + int count = recordDataCatch.put(take.getDevice().getDeviceId(),channelId, sn, sumNum, new ArrayList<>());
  106 + recordInfo.setCount(count);
105 eventPublisher.recordEndEventPush(recordInfo); 107 eventPublisher.recordEndEventPush(recordInfo);
106 - recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, new ArrayList<>());  
107 releaseRequest(take.getDevice().getDeviceId(), sn); 108 releaseRequest(take.getDevice().getDeviceId(), sn);
108 } else { 109 } else {
109 Iterator<Element> recordListIterator = recordListElement.elementIterator(); 110 Iterator<Element> recordListIterator = recordListElement.elementIterator();
@@ -137,12 +138,11 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent @@ -137,12 +138,11 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
137 recordList.add(record); 138 recordList.add(record);
138 } 139 }
139 recordInfo.setRecordList(recordList); 140 recordInfo.setRecordList(recordList);
  141 + int count = recordDataCatch.put(take.getDevice().getDeviceId(),channelId, sn, sumNum, recordList);recordInfo.setCount(count);
  142 + logger.info("[国标录像], {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum);
140 // 发送消息,如果是上级查询此录像,则会通过这里通知给上级 143 // 发送消息,如果是上级查询此录像,则会通过这里通知给上级
141 eventPublisher.recordEndEventPush(recordInfo); 144 eventPublisher.recordEndEventPush(recordInfo);
142 - int count = recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, recordList);  
143 - logger.info("[国标录像], {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum);  
144 } 145 }
145 -  
146 if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){ 146 if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){
147 releaseRequest(take.getDevice().getDeviceId(), sn); 147 releaseRequest(take.getDevice().getDeviceId(), sn);
148 } 148 }
src/main/java/com/genersoft/iot/vmp/gb28181/utils/MessageElement.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.utils;
  2 +
  3 +import java.lang.annotation.*;
  4 +
  5 +/**
  6 + * @author gaofuwang
  7 + * @version 1.0
  8 + * @date 2022/6/28 14:58
  9 + */
  10 +@Target({ElementType.FIELD})
  11 +@Retention(RetentionPolicy.RUNTIME)
  12 +@Documented
  13 +public @interface MessageElement {
  14 + String value();
  15 +
  16 + String subVal() default "";
  17 +}
src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
1 package com.genersoft.iot.vmp.gb28181.utils; 1 package com.genersoft.iot.vmp.gb28181.utils;
2 2
  3 +import com.alibaba.fastjson2.JSON;
3 import com.alibaba.fastjson2.JSONArray; 4 import com.alibaba.fastjson2.JSONArray;
4 import com.alibaba.fastjson2.JSONObject; 5 import com.alibaba.fastjson2.JSONObject;
5 import com.genersoft.iot.vmp.gb28181.bean.Device; 6 import com.genersoft.iot.vmp.gb28181.bean.Device;
@@ -15,12 +16,16 @@ import org.dom4j.io.SAXReader; @@ -15,12 +16,16 @@ import org.dom4j.io.SAXReader;
15 import org.slf4j.Logger; 16 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory; 17 import org.slf4j.LoggerFactory;
17 import org.springframework.util.ObjectUtils; 18 import org.springframework.util.ObjectUtils;
18 -import org.springframework.util.StringUtils; 19 +import org.springframework.util.ReflectionUtils;
19 20
20 import javax.sip.RequestEvent; 21 import javax.sip.RequestEvent;
21 import javax.sip.message.Request; 22 import javax.sip.message.Request;
22 import java.io.ByteArrayInputStream; 23 import java.io.ByteArrayInputStream;
23 import java.io.StringReader; 24 import java.io.StringReader;
  25 +import java.lang.reflect.Field;
  26 +import java.lang.reflect.InvocationTargetException;
  27 +import java.lang.reflect.ParameterizedType;
  28 +import java.lang.reflect.Type;
24 import java.util.*; 29 import java.util.*;
25 30
26 /** 31 /**
@@ -411,4 +416,76 @@ public class XmlUtil { @@ -411,4 +416,76 @@ public class XmlUtil {
411 } 416 }
412 return deviceChannel; 417 return deviceChannel;
413 } 418 }
  419 +
  420 + /**
  421 + * 新增方法支持内部嵌套
  422 + *
  423 + * @param element xmlElement
  424 + * @param clazz 结果类
  425 + * @param <T> 泛型
  426 + * @return 结果对象
  427 + * @throws NoSuchMethodException
  428 + * @throws InvocationTargetException
  429 + * @throws InstantiationException
  430 + * @throws IllegalAccessException
  431 + */
  432 + public static <T> T loadElement(Element element, Class<T> clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
  433 + Field[] fields = clazz.getDeclaredFields();
  434 + T t = clazz.getDeclaredConstructor().newInstance();
  435 + for (Field field : fields) {
  436 + ReflectionUtils.makeAccessible(field);
  437 + MessageElement annotation = field.getAnnotation(MessageElement.class);
  438 + if (annotation == null) {
  439 + continue;
  440 + }
  441 + String value = annotation.value();
  442 + String subVal = annotation.subVal();
  443 + Element element1 = element.element(value);
  444 + if (element1 == null) {
  445 + continue;
  446 + }
  447 + if ("".equals(subVal)) {
  448 + // 无下级数据
  449 + Object fieldVal = element1.isTextOnly() ? element1.getText() : loadElement(element1, field.getType());
  450 + Object o = simpleTypeDeal(field.getType(), fieldVal);
  451 + ReflectionUtils.setField(field, t, o);
  452 + } else {
  453 + // 存在下级数据
  454 + ArrayList<Object> list = new ArrayList<>();
  455 + Type genericType = field.getGenericType();
  456 + if (!(genericType instanceof ParameterizedType)) {
  457 + continue;
  458 + }
  459 + Class<?> aClass = (Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[0];
  460 + for (Element element2 : element1.elements(subVal)) {
  461 + list.add(loadElement(element2, aClass));
  462 + }
  463 + ReflectionUtils.setField(field, t, list);
  464 + }
  465 + }
  466 + return t;
  467 + }
  468 +
  469 + /**
  470 + * 简单类型处理
  471 + *
  472 + * @param tClass
  473 + * @param val
  474 + * @return
  475 + */
  476 + private static Object simpleTypeDeal(Class<?> tClass, Object val) {
  477 + if (tClass.equals(String.class)) {
  478 + return val.toString();
  479 + }
  480 + if (tClass.equals(Integer.class)) {
  481 + return Integer.valueOf(val.toString());
  482 + }
  483 + if (tClass.equals(Double.class)) {
  484 + return Double.valueOf(val.toString());
  485 + }
  486 + if (tClass.equals(Long.class)) {
  487 + return Long.valueOf(val.toString());
  488 + }
  489 + return val;
  490 + }
414 } 491 }
415 \ No newline at end of file 492 \ No newline at end of file
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -10,6 +10,8 @@ import com.genersoft.iot.vmp.gb28181.event.EventPublisher; @@ -10,6 +10,8 @@ import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
10 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; 10 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
11 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 11 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
12 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; 12 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
  13 +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
  14 +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
13 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; 15 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
14 import com.genersoft.iot.vmp.media.zlm.dto.HookType; 16 import com.genersoft.iot.vmp.media.zlm.dto.HookType;
15 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 17 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
@@ -19,7 +21,10 @@ import com.genersoft.iot.vmp.media.zlm.dto.hook.*; @@ -19,7 +21,10 @@ import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
19 import com.genersoft.iot.vmp.service.*; 21 import com.genersoft.iot.vmp.service.*;
20 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 22 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
21 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 23 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  24 +import com.genersoft.iot.vmp.vmanager.bean.DeferredResultEx;
  25 +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
22 import com.genersoft.iot.vmp.vmanager.bean.StreamContent; 26 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
  27 +import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
23 import org.slf4j.Logger; 28 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory; 29 import org.slf4j.LoggerFactory;
25 import org.springframework.beans.factory.annotation.Autowired; 30 import org.springframework.beans.factory.annotation.Autowired;
@@ -27,6 +32,7 @@ import org.springframework.beans.factory.annotation.Qualifier; @@ -27,6 +32,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
27 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 32 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
28 import org.springframework.util.ObjectUtils; 33 import org.springframework.util.ObjectUtils;
29 import org.springframework.web.bind.annotation.*; 34 import org.springframework.web.bind.annotation.*;
  35 +import org.springframework.web.context.request.async.DeferredResult;
30 36
31 import javax.servlet.http.HttpServletRequest; 37 import javax.servlet.http.HttpServletRequest;
32 import javax.sip.InvalidArgumentException; 38 import javax.sip.InvalidArgumentException;
@@ -35,20 +41,21 @@ import java.text.ParseException; @@ -35,20 +41,21 @@ import java.text.ParseException;
35 import java.util.HashMap; 41 import java.util.HashMap;
36 import java.util.List; 42 import java.util.List;
37 import java.util.Map; 43 import java.util.Map;
  44 +import java.util.UUID;
38 45
39 -/** 46 +/**
40 * @description:针对 ZLMediaServer的hook事件监听 47 * @description:针对 ZLMediaServer的hook事件监听
41 * @author: swwheihei 48 * @author: swwheihei
42 - * @date: 2020年5月8日 上午10:46:48 49 + * @date: 2020年5月8日 上午10:46:48
43 */ 50 */
44 @RestController 51 @RestController
45 @RequestMapping("/index/hook") 52 @RequestMapping("/index/hook")
46 public class ZLMHttpHookListener { 53 public class ZLMHttpHookListener {
47 54
48 - private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class); 55 + private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class);
49 56
50 - @Autowired  
51 - private SIPCommander cmder; 57 + @Autowired
  58 + private SIPCommander cmder;
52 59
53 @Autowired 60 @Autowired
54 private ISIPCommanderForPlatform commanderFroPlatform; 61 private ISIPCommanderForPlatform commanderFroPlatform;
@@ -59,265 +66,243 @@ public class ZLMHttpHookListener { @@ -59,265 +66,243 @@ public class ZLMHttpHookListener {
59 @Autowired 66 @Autowired
60 private ZLMRTPServerFactory zlmrtpServerFactory; 67 private ZLMRTPServerFactory zlmrtpServerFactory;
61 68
62 - @Autowired  
63 - private IPlayService playService;  
64 -  
65 - @Autowired  
66 - private IVideoManagerStorage storager;  
67 -  
68 - @Autowired  
69 - private IRedisCatchStorage redisCatchStorage;  
70 -  
71 - @Autowired  
72 - private IDeviceService deviceService;  
73 -  
74 - @Autowired  
75 - private IMediaServerService mediaServerService;  
76 -  
77 - @Autowired  
78 - private IStreamProxyService streamProxyService;  
79 -  
80 - @Autowired  
81 - private IStreamPushService streamPushService;  
82 -  
83 - @Autowired  
84 - private IMediaService mediaService;  
85 -  
86 - @Autowired  
87 - private EventPublisher eventPublisher;  
88 -  
89 - @Autowired  
90 - private ZLMMediaListManager zlmMediaListManager;  
91 -  
92 - @Autowired  
93 - private ZlmHttpHookSubscribe subscribe;  
94 -  
95 - @Autowired  
96 - private UserSetting userSetting;  
97 -  
98 - @Autowired  
99 - private IUserService userService;  
100 -  
101 - @Autowired  
102 - private VideoStreamSessionManager sessionManager;  
103 -  
104 - @Autowired  
105 - private AssistRESTfulUtils assistRESTfulUtils;  
106 -  
107 - @Qualifier("taskExecutor")  
108 - @Autowired  
109 - private ThreadPoolTaskExecutor taskExecutor;  
110 -  
111 - /**  
112 - * 服务器定时上报时间,上报间隔可配置,默认10s上报一次  
113 - *  
114 - */  
115 - @ResponseBody  
116 - @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")  
117 - public JSONObject onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param){  
118 -  
119 - logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId());  
120 -  
121 - taskExecutor.execute(()->{  
122 - List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);  
123 - JSONObject json = (JSONObject) JSON.toJSON(param);  
124 - if (subscribes != null && subscribes.size() > 0) {  
125 - for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {  
126 - subscribe.response(null, json);  
127 - }  
128 - }  
129 - });  
130 - mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData());  
131 -  
132 - JSONObject ret = new JSONObject();  
133 - ret.put("code", 0);  
134 - ret.put("msg", "success");  
135 -  
136 - return ret;  
137 - }  
138 -  
139 - /**  
140 - * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。  
141 - *  
142 - */  
143 - @ResponseBody  
144 - @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")  
145 - public JSONObject onPlay(@RequestBody OnPlayHookParam param){  
146 - if (logger.isDebugEnabled()) {  
147 - logger.debug("[ZLM HOOK] 播放鉴权:{}->{}" + param.getMediaServerId(), param);  
148 - }  
149 - String mediaServerId = param.getMediaServerId();  
150 -  
151 - taskExecutor.execute(()->{  
152 - JSONObject json = (JSONObject) JSON.toJSON(param);  
153 - ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);  
154 - if (subscribe != null ) {  
155 - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);  
156 - if (mediaInfo != null) {  
157 - subscribe.response(mediaInfo, json);  
158 - }  
159 - }  
160 - });  
161 - JSONObject ret = new JSONObject();  
162 - if (!"rtp".equals(param.getApp())) {  
163 - Map<String, String> paramMap = urlParamToMap(param.getParams());  
164 - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());  
165 - if (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(paramMap.get("callId"))) {  
166 - ret.put("code", 401);  
167 - ret.put("msg", "Unauthorized");  
168 - return ret;  
169 - }  
170 - }  
171 -  
172 - ret.put("code", 0);  
173 - ret.put("msg", "success");  
174 - return ret;  
175 - }  
176 -  
177 - /**  
178 - * rtsp/rtmp/rtp推流鉴权事件。  
179 - *  
180 - */  
181 - @ResponseBody  
182 - @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")  
183 - public JSONObject onPublish(@RequestBody OnPublishHookParam param) {  
184 -  
185 - JSONObject json = (JSONObject) JSON.toJSON(param);  
186 -  
187 - logger.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param);  
188 - JSONObject ret = new JSONObject();  
189 - String mediaServerId = json.getString("mediaServerId");  
190 - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);  
191 -  
192 - if (!"rtp".equals(param.getApp())) {  
193 - if (userSetting.getPushAuthority()) {  
194 - // 推流鉴权  
195 - if (param.getParams() == null) {  
196 - logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");  
197 - ret.put("code", 401);  
198 - ret.put("msg", "Unauthorized");  
199 - return ret;  
200 - }  
201 - Map<String, String> paramMap = urlParamToMap(param.getParams());  
202 - String sign = paramMap.get("sign");  
203 - if (sign == null) {  
204 - logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");  
205 - ret.put("code", 401);  
206 - ret.put("msg", "Unauthorized");  
207 - return ret;  
208 - }  
209 - // 推流自定义播放鉴权码  
210 - String callId = paramMap.get("callId");  
211 - // 鉴权配置  
212 - boolean hasAuthority = userService.checkPushAuthority(callId, sign);  
213 - if (!hasAuthority) {  
214 - logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign);  
215 - ret.put("code", 401);  
216 - ret.put("msg", "Unauthorized");  
217 - return ret;  
218 - }  
219 - StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);  
220 - streamAuthorityInfo.setCallId(callId);  
221 - streamAuthorityInfo.setSign(sign);  
222 - // 鉴权通过  
223 - redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);  
224 - // 通知assist新的callId  
225 - if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) {  
226 - taskExecutor.execute(()->{  
227 - assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);  
228 - });  
229 - }  
230 - }  
231 - }else {  
232 - zlmMediaListManager.sendStreamEvent(param.getApp(),param.getStream(), param.getMediaServerId());  
233 - }  
234 -  
235 - ret.put("code", 0);  
236 - ret.put("msg", "success");  
237 -  
238 - if (!"rtp".equals(param.getApp())) {  
239 - ret.put("enable_audio", true);  
240 - }  
241 -  
242 - taskExecutor.execute(()->{  
243 - ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);  
244 - if (subscribe != null) {  
245 - if (mediaInfo != null) {  
246 - subscribe.response(mediaInfo, json);  
247 - }else {  
248 - ret.put("code", 1);  
249 - ret.put("msg", "zlm not register");  
250 - }  
251 - }  
252 - });  
253 -  
254 - if ("rtp".equals(param.getApp())) {  
255 - ret.put("enable_mp4", userSetting.getRecordSip());  
256 - }else {  
257 - ret.put("enable_mp4", userSetting.isRecordPushLive());  
258 - }  
259 - List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());  
260 - if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {  
261 - String deviceId = ssrcTransactionForAll.get(0).getDeviceId();  
262 - String channelId = ssrcTransactionForAll.get(0).getChannelId();  
263 - DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);  
264 - if (deviceChannel != null) {  
265 - ret.put("enable_audio", deviceChannel.isHasAudio());  
266 - }  
267 - // 如果是录像下载就设置视频间隔十秒  
268 - if (ssrcTransactionForAll.get(0).getType() == VideoStreamSessionManager.SessionType.download) {  
269 - ret.put("mp4_max_second", 10);  
270 - ret.put("enable_mp4", true);  
271 - ret.put("enable_audio", true);  
272 - }  
273 - }  
274 - return ret;  
275 - }  
276 -  
277 - /**  
278 - * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。  
279 - *  
280 - */  
281 - @ResponseBody  
282 - @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")  
283 - public JSONObject onStreamChanged(@RequestBody OnStreamChangedHookParam param){  
284 -  
285 - if (param.isRegist()) {  
286 - logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());  
287 - }else {  
288 - logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());  
289 - }  
290 -  
291 -  
292 - JSONObject json = (JSONObject) JSON.toJSON(param);  
293 - taskExecutor.execute(()-> {  
294 - ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);  
295 - if (subscribe != null) {  
296 - MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());  
297 - if (mediaInfo != null) {  
298 - subscribe.response(mediaInfo, json);  
299 - }  
300 - }  
301 - // 流消失移除redis play  
302 - List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();  
303 - if (param.isRegist()) {  
304 - if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()  
305 - || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()  
306 - || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {  
307 -  
308 - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());  
309 - if (streamAuthorityInfo == null) {  
310 - streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);  
311 - } else {  
312 - streamAuthorityInfo.setOriginType(param.getOriginType());  
313 - streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());  
314 - }  
315 - redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);  
316 - }  
317 - } else {  
318 - redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());  
319 - }  
320 - }); 69 + @Autowired
  70 + private IPlayService playService;
  71 +
  72 + @Autowired
  73 + private IVideoManagerStorage storager;
  74 +
  75 + @Autowired
  76 + private IRedisCatchStorage redisCatchStorage;
  77 +
  78 + @Autowired
  79 + private IDeviceService deviceService;
  80 +
  81 + @Autowired
  82 + private IMediaServerService mediaServerService;
  83 +
  84 + @Autowired
  85 + private IStreamProxyService streamProxyService;
  86 +
  87 + @Autowired
  88 + private DeferredResultHolder resultHolder;
  89 +
  90 + @Autowired
  91 + private IMediaService mediaService;
  92 +
  93 + @Autowired
  94 + private EventPublisher eventPublisher;
  95 +
  96 + @Autowired
  97 + private ZLMMediaListManager zlmMediaListManager;
  98 +
  99 + @Autowired
  100 + private ZlmHttpHookSubscribe subscribe;
  101 +
  102 + @Autowired
  103 + private UserSetting userSetting;
  104 +
  105 + @Autowired
  106 + private IUserService userService;
  107 +
  108 + @Autowired
  109 + private VideoStreamSessionManager sessionManager;
  110 +
  111 + @Autowired
  112 + private AssistRESTfulUtils assistRESTfulUtils;
  113 +
  114 + @Qualifier("taskExecutor")
  115 + @Autowired
  116 + private ThreadPoolTaskExecutor taskExecutor;
  117 +
  118 + /**
  119 + * 服务器定时上报时间,上报间隔可配置,默认10s上报一次
  120 + */
  121 + @ResponseBody
  122 + @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
  123 + public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) {
  124 +
  125 + logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId());
  126 +
  127 + taskExecutor.execute(() -> {
  128 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
  129 + JSONObject json = (JSONObject) JSON.toJSON(param);
  130 + if (subscribes != null && subscribes.size() > 0) {
  131 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  132 + subscribe.response(null, json);
  133 + }
  134 + }
  135 + });
  136 + mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData());
  137 +
  138 + return HookResult.SUCCESS();
  139 + }
  140 +
  141 + /**
  142 + * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
  143 + */
  144 + @ResponseBody
  145 + @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
  146 + public HookResult onPlay(@RequestBody OnPlayHookParam param) {
  147 + if (logger.isDebugEnabled()) {
  148 + logger.debug("[ZLM HOOK] 播放鉴权:{}->{}" + param.getMediaServerId(), param);
  149 + }
  150 + String mediaServerId = param.getMediaServerId();
  151 +
  152 + taskExecutor.execute(() -> {
  153 + JSONObject json = (JSONObject) JSON.toJSON(param);
  154 + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);
  155 + if (subscribe != null) {
  156 + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
  157 + if (mediaInfo != null) {
  158 + subscribe.response(mediaInfo, json);
  159 + }
  160 + }
  161 + });
  162 + if (!"rtp".equals(param.getApp())) {
  163 + Map<String, String> paramMap = urlParamToMap(param.getParams());
  164 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
  165 + if (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(paramMap.get("callId"))) {
  166 + return new HookResult(401, "Unauthorized");
  167 + }
  168 + }
  169 +
  170 + return HookResult.SUCCESS();
  171 + }
  172 +
  173 + /**
  174 + * rtsp/rtmp/rtp推流鉴权事件。
  175 + */
  176 + @ResponseBody
  177 + @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
  178 + public HookResultForOnPublish onPublish(@RequestBody OnPublishHookParam param) {
  179 +
  180 + JSONObject json = (JSONObject) JSON.toJSON(param);
  181 +
  182 + logger.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param);
  183 +
  184 + String mediaServerId = json.getString("mediaServerId");
  185 + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
  186 +
  187 + if (!"rtp".equals(param.getApp())) {
  188 + if (userSetting.getPushAuthority()) {
  189 + // 推流鉴权
  190 + if (param.getParams() == null) {
  191 + logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
  192 + return new HookResultForOnPublish(401, "Unauthorized");
  193 + }
  194 + Map<String, String> paramMap = urlParamToMap(param.getParams());
  195 + String sign = paramMap.get("sign");
  196 + if (sign == null) {
  197 + logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
  198 + return new HookResultForOnPublish(401, "Unauthorized");
  199 + }
  200 + // 推流自定义播放鉴权码
  201 + String callId = paramMap.get("callId");
  202 + // 鉴权配置
  203 + boolean hasAuthority = userService.checkPushAuthority(callId, sign);
  204 + if (!hasAuthority) {
  205 + logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign);
  206 + return new HookResultForOnPublish(401, "Unauthorized");
  207 + }
  208 + StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
  209 + streamAuthorityInfo.setCallId(callId);
  210 + streamAuthorityInfo.setSign(sign);
  211 + // 鉴权通过
  212 + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
  213 + // 通知assist新的callId
  214 + if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) {
  215 + taskExecutor.execute(() -> {
  216 + assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);
  217 + });
  218 + }
  219 + }
  220 + } else {
  221 + zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
  222 + }
  223 +
  224 +
  225 + HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
  226 + if (!"rtp".equals(param.getApp())) {
  227 + result.setEnable_audio(true);
  228 + }
  229 +
  230 + taskExecutor.execute(() -> {
  231 + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
  232 + if (subscribe != null) {
  233 + if (mediaInfo != null) {
  234 + subscribe.response(mediaInfo, json);
  235 + } else {
  236 + new HookResultForOnPublish(1, "zlm not register");
  237 + }
  238 + }
  239 + });
  240 +
  241 + if ("rtp".equals(param.getApp())) {
  242 + result.setEnable_mp4(userSetting.getRecordSip());
  243 + } else {
  244 + result.setEnable_mp4(userSetting.isRecordPushLive());
  245 + }
  246 + List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
  247 + if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
  248 + String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
  249 + String channelId = ssrcTransactionForAll.get(0).getChannelId();
  250 + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
  251 + if (deviceChannel != null) {
  252 + result.setEnable_audio(deviceChannel.isHasAudio());
  253 + }
  254 + // 如果是录像下载就设置视频间隔十秒
  255 + if (ssrcTransactionForAll.get(0).getType() == VideoStreamSessionManager.SessionType.download) {
  256 + result.setMp4_max_second(10);
  257 + result.setEnable_audio(true);
  258 + result.setEnable_mp4(true);
  259 + }
  260 + }
  261 + return result;
  262 + }
  263 +
  264 + /**
  265 + * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
  266 + */
  267 + @ResponseBody
  268 + @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
  269 + public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) {
  270 +
  271 + if (param.isRegist()) {
  272 + logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  273 + } else {
  274 + logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  275 + }
  276 +
  277 +
  278 + JSONObject json = (JSONObject) JSON.toJSON(param);
  279 + taskExecutor.execute(() -> {
  280 + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
  281 + if (subscribe != null) {
  282 + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
  283 + if (mediaInfo != null) {
  284 + subscribe.response(mediaInfo, json);
  285 + }
  286 + }
  287 + // 流消失移除redis play
  288 + List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
  289 + if (param.isRegist()) {
  290 + if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
  291 + || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
  292 + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
  293 +
  294 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
  295 + if (streamAuthorityInfo == null) {
  296 + streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
  297 + } else {
  298 + streamAuthorityInfo.setOriginType(param.getOriginType());
  299 + streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
  300 + }
  301 + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
  302 + }
  303 + } else {
  304 + redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
  305 + }
321 306
322 if ("rtsp".equals(param.getSchema())){ 307 if ("rtsp".equals(param.getSchema())){
323 logger.info("流变化:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream()); 308 logger.info("流变化:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
@@ -465,72 +450,57 @@ public class ZLMHttpHookListener { @@ -465,72 +450,57 @@ public class ZLMHttpHookListener {
465 GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream()); 450 GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
466 if (gbStream != null) { 451 if (gbStream != null) {
467 // eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF); 452 // eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
468 - }  
469 - zlmMediaListManager.removeMedia(param.getApp(), param.getStream());  
470 - }  
471 - if (type != null) {  
472 - // 发送流变化redis消息  
473 - JSONObject jsonObject = new JSONObject();  
474 - jsonObject.put("serverId", userSetting.getServerId());  
475 - jsonObject.put("app", param.getApp());  
476 - jsonObject.put("stream", param.getStream());  
477 - jsonObject.put("register", param.isRegist());  
478 - jsonObject.put("mediaServerId", param.getMediaServerId());  
479 - redisCatchStorage.sendStreamChangeMsg(type, jsonObject);  
480 - }  
481 - }  
482 - }  
483 - }  
484 - if (!param.isRegist()) {  
485 - List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());  
486 - if (sendRtpItems.size() > 0) {  
487 - for (SendRtpItem sendRtpItem : sendRtpItems) {  
488 - if (sendRtpItem.getApp().equals(param.getApp())) {  
489 - String platformId = sendRtpItem.getPlatformId();  
490 - ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);  
491 - Device device = deviceService.getDevice(platformId);  
492 -  
493 - try {  
494 - if (platform != null) {  
495 - commanderFroPlatform.streamByeCmd(platform, sendRtpItem);  
496 - }else {  
497 - if (sendRtpItem.isOnlyAudio()) {  
498 - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());  
499 - if (audioBroadcastCatch != null) {  
500 -// playService.stopAudioBroadcast(device.getDeviceId(), sendRtpItem.getChannelId());  
501 - if ("talk".equals(param.getApp())) {  
502 -// cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);  
503 - }else {  
504 -// cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);  
505 - }  
506 - }  
507 - }  
508 -  
509 -  
510 -  
511 - }  
512 - } catch (SipException | InvalidArgumentException | ParseException e) {  
513 - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());  
514 - }  
515 - }  
516 - }  
517 - }  
518 - }  
519 - }  
520 -  
521 - JSONObject ret = new JSONObject();  
522 - ret.put("code", 0);  
523 - ret.put("msg", "success");  
524 - return ret;  
525 - }  
526 -  
527 - /**  
528 - * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。  
529 - *  
530 - */  
531 - @ResponseBody  
532 - @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")  
533 - public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param){ 453 + }
  454 + zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
  455 + }
  456 + if (type != null) {
  457 + // 发送流变化redis消息
  458 + JSONObject jsonObject = new JSONObject();
  459 + jsonObject.put("serverId", userSetting.getServerId());
  460 + jsonObject.put("app", param.getApp());
  461 + jsonObject.put("stream", param.getStream());
  462 + jsonObject.put("register", param.isRegist());
  463 + jsonObject.put("mediaServerId", param.getMediaServerId());
  464 + redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
  465 + }
  466 + }
  467 + }
  468 + }
  469 + if (!param.isRegist()) {
  470 + List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
  471 + if (sendRtpItems.size() > 0) {
  472 + for (SendRtpItem sendRtpItem : sendRtpItems) {
  473 + if (sendRtpItem.getApp().equals(param.getApp())) {
  474 + String platformId = sendRtpItem.getPlatformId();
  475 + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
  476 + Device device = deviceService.getDevice(platformId);
  477 +
  478 + try {
  479 + if (platform != null) {
  480 + commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
  481 + } else {
  482 + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId());
  483 + }
  484 + } catch (SipException | InvalidArgumentException | ParseException |
  485 + SsrcTransactionNotFoundException e) {
  486 + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
  487 + }
  488 + }
  489 + }
  490 + }
  491 + }
  492 + }
  493 + });
  494 +
  495 + return HookResult.SUCCESS();
  496 + }
  497 +
  498 + /**
  499 + * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。
  500 + */
  501 + @ResponseBody
  502 + @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
  503 + public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) {
534 504
535 logger.info("[ZLM HOOK]流无人观看:{]->{}->{}/{}" + param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); 505 logger.info("[ZLM HOOK]流无人观看:{]->{}->{}/{}" + param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
536 JSONObject ret = new JSONObject(); 506 JSONObject ret = new JSONObject();
@@ -571,215 +541,243 @@ public class ZLMHttpHookListener { @@ -571,215 +541,243 @@ public class ZLMHttpHookListener {
571 } 541 }
572 } 542 }
573 543
574 - redisCatchStorage.stopPlay(streamInfoForPlayCatch);  
575 - storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());  
576 - return ret;  
577 - }  
578 - // 录像回放  
579 - StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, param.getStream(), null);  
580 - if (streamInfoForPlayBackCatch != null ) {  
581 - if (streamInfoForPlayBackCatch.isPause()) {  
582 - ret.put("close", false);  
583 - }else {  
584 - Device device = deviceService.getDevice(streamInfoForPlayBackCatch.getDeviceID());  
585 - if (device != null) {  
586 - try {  
587 - cmder.streamByeCmd(device,streamInfoForPlayBackCatch.getChannelId(),  
588 - streamInfoForPlayBackCatch.getStream(), null);  
589 - } catch (InvalidArgumentException | ParseException | SipException |  
590 - SsrcTransactionNotFoundException e) {  
591 - logger.error("[无人观看]回放, 发送BYE失败 {}", e.getMessage());  
592 - }  
593 - }  
594 - redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),  
595 - streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);  
596 - }  
597 - return ret;  
598 - }  
599 - // 录像下载  
600 - StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, param.getStream(), null);  
601 - // 进行录像下载时无人观看不断流  
602 - if (streamInfoForDownload != null) {  
603 - ret.put("close", false);  
604 - return ret;  
605 - }  
606 - }else {  
607 - // 非国标流 推流/拉流代理  
608 - // 拉流代理  
609 - StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());  
610 - if (streamProxyItem != null ) {  
611 - if (streamProxyItem.isEnable_remove_none_reader()) {  
612 - // 无人观看自动移除  
613 - ret.put("close", true);  
614 - streamProxyService.del(param.getApp(), param.getStream());  
615 - String url = streamProxyItem.getUrl() != null?streamProxyItem.getUrl():streamProxyItem.getSrc_url();  
616 - logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url);  
617 - }else if (streamProxyItem.isEnable_disable_none_reader()) {  
618 - // 无人观看停用  
619 - ret.put("close", true);  
620 - // 修改数据  
621 - streamProxyService.stop(param.getApp(), param.getStream());  
622 - }else {  
623 - // 无人观看不做处理  
624 - ret.put("close", false);  
625 - }  
626 - return ret;  
627 - }  
628 - // 推流具有主动性,暂时不做处理 544 + redisCatchStorage.stopPlay(streamInfoForPlayCatch);
  545 + storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
  546 + return ret;
  547 + }
  548 + // 录像回放
  549 + StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, param.getStream(), null);
  550 + if (streamInfoForPlayBackCatch != null) {
  551 + if (streamInfoForPlayBackCatch.isPause()) {
  552 + ret.put("close", false);
  553 + } else {
  554 + Device device = deviceService.getDevice(streamInfoForPlayBackCatch.getDeviceID());
  555 + if (device != null) {
  556 + try {
  557 + cmder.streamByeCmd(device, streamInfoForPlayBackCatch.getChannelId(),
  558 + streamInfoForPlayBackCatch.getStream(), null);
  559 + } catch (InvalidArgumentException | ParseException | SipException |
  560 + SsrcTransactionNotFoundException e) {
  561 + logger.error("[无人观看]回放, 发送BYE失败 {}", e.getMessage());
  562 + }
  563 + }
  564 + redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),
  565 + streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
  566 + }
  567 + return ret;
  568 + }
  569 + // 录像下载
  570 + StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, param.getStream(), null);
  571 + // 进行录像下载时无人观看不断流
  572 + if (streamInfoForDownload != null) {
  573 + ret.put("close", false);
  574 + return ret;
  575 + }
  576 + } else {
  577 + // 非国标流 推流/拉流代理
  578 + // 拉流代理
  579 + StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
  580 + if (streamProxyItem != null) {
  581 + if (streamProxyItem.isEnable_remove_none_reader()) {
  582 + // 无人观看自动移除
  583 + ret.put("close", true);
  584 + streamProxyService.del(param.getApp(), param.getStream());
  585 + String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrc_url();
  586 + logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url);
  587 + } else if (streamProxyItem.isEnable_disable_none_reader()) {
  588 + // 无人观看停用
  589 + ret.put("close", true);
  590 + // 修改数据
  591 + streamProxyService.stop(param.getApp(), param.getStream());
  592 + } else {
  593 + // 无人观看不做处理
  594 + ret.put("close", false);
  595 + }
  596 + return ret;
  597 + }
  598 + // 推流具有主动性,暂时不做处理
629 // StreamPushItem streamPushItem = streamPushService.getPush(app, streamId); 599 // StreamPushItem streamPushItem = streamPushService.getPush(app, streamId);
630 // if (streamPushItem != null) { 600 // if (streamPushItem != null) {
631 // // TODO 发送停止 601 // // TODO 发送停止
632 // 602 //
633 // } 603 // }
634 - }  
635 - return ret;  
636 - }  
637 -  
638 - /**  
639 - * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。  
640 - *  
641 - */  
642 - @ResponseBody  
643 - @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")  
644 - public JSONObject onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param){  
645 - logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());  
646 - taskExecutor.execute(()->{  
647 - MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());  
648 - if (userSetting.isAutoApplyPlay() && mediaInfo != null) {  
649 - if ("rtp".equals(param.getApp())) {  
650 - if (mediaInfo.isRtpEnable()) {  
651 - String[] s = param.getStream().split("_");  
652 - if (s.length == 2) {  
653 - String deviceId = s[0];  
654 - String channelId = s[1];  
655 - Device device = redisCatchStorage.getDevice(deviceId);  
656 - if (device != null) {  
657 - playService.play(mediaInfo,deviceId, channelId, null, null, null);  
658 - }  
659 - }  
660 - }  
661 - }else {  
662 - // 拉流代理  
663 - StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());  
664 - if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) {  
665 - streamProxyService.start(param.getApp(), param.getStream());  
666 - }  
667 - }  
668 - }  
669 - });  
670 -  
671 -  
672 - JSONObject ret = new JSONObject();  
673 - ret.put("code", 0);  
674 - ret.put("msg", "success");  
675 - return ret;  
676 - }  
677 -  
678 - /**  
679 - * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。  
680 - *  
681 - */  
682 - @ResponseBody  
683 - @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")  
684 - public JSONObject onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject){  
685 -  
686 - jsonObject.put("ip", request.getRemoteAddr());  
687 - ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);  
688 - zlmServerConfig.setIp(request.getRemoteAddr());  
689 - logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());  
690 - taskExecutor.execute(()->{  
691 - List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);  
692 - if (subscribes != null && subscribes.size() > 0) {  
693 - for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {  
694 - subscribe.response(null, jsonObject);  
695 - }  
696 - }  
697 - mediaServerService.zlmServerOnline(zlmServerConfig);  
698 - });  
699 -  
700 - JSONObject ret = new JSONObject();  
701 - ret.put("code", 0);  
702 - ret.put("msg", "success");  
703 - return ret;  
704 - }  
705 -  
706 - /**  
707 - * 发送rtp(startSendRtp)被动关闭时回调  
708 - */  
709 - @ResponseBody  
710 - @PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8")  
711 - public JSONObject onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param){  
712 -  
713 - logger.info("[ZLM HOOK] rtp发送关闭:{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream());  
714 -  
715 - JSONObject ret = new JSONObject();  
716 - ret.put("code", 0);  
717 - ret.put("msg", "success");  
718 -  
719 - // 查找对应的上级推流,发送停止  
720 - if (!"rtp".equals(param.getApp())) {  
721 - return ret;  
722 - }  
723 - taskExecutor.execute(()->{  
724 - List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());  
725 - if (sendRtpItems.size() > 0) {  
726 - for (SendRtpItem sendRtpItem : sendRtpItems) {  
727 - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());  
728 - try {  
729 - commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());  
730 - } catch (SipException | InvalidArgumentException | ParseException e) {  
731 - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());  
732 - }  
733 - redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),  
734 - sendRtpItem.getCallId(), sendRtpItem.getStreamId());  
735 - }  
736 - }  
737 - });  
738 -  
739 -  
740 - return ret;  
741 - }  
742 -  
743 - /**  
744 - * rtpServer收流超时  
745 - */  
746 - @ResponseBody  
747 - @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")  
748 - public JSONObject onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param){  
749 - logger.info("[ZLM HOOK] rtpServer rtp超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());  
750 -  
751 - JSONObject ret = new JSONObject();  
752 - ret.put("code", 0);  
753 - ret.put("msg", "success");  
754 -  
755 - taskExecutor.execute(()->{  
756 - JSONObject json = (JSONObject) JSON.toJSON(param);  
757 - List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);  
758 - if (subscribes != null && subscribes.size() > 0) {  
759 - for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {  
760 - subscribe.response(null, json);  
761 - }  
762 - }  
763 - });  
764 -  
765 - return ret;  
766 - }  
767 -  
768 - private Map<String, String> urlParamToMap(String params) {  
769 - HashMap<String, String> map = new HashMap<>();  
770 - if (ObjectUtils.isEmpty(params)) {  
771 - return map;  
772 - }  
773 - String[] paramsArray = params.split("&");  
774 - if (paramsArray.length == 0) {  
775 - return map;  
776 - }  
777 - for (String param : paramsArray) {  
778 - String[] paramArray = param.split("=");  
779 - if (paramArray.length == 2){  
780 - map.put(paramArray[0], paramArray[1]);  
781 - }  
782 - }  
783 - return map;  
784 - } 604 + }
  605 + return ret;
  606 + }
  607 +
  608 + /**
  609 + * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。
  610 + */
  611 + @ResponseBody
  612 + @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
  613 + public DeferredResult<HookResult> onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) {
  614 + logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  615 +
  616 + DeferredResult<HookResult> defaultResult = new DeferredResult<>();
  617 +
  618 + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
  619 + if (!userSetting.isAutoApplyPlay() || mediaInfo == null) {
  620 + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
  621 + return defaultResult;
  622 + }
  623 +
  624 + if ("rtp".equals(param.getApp())) {
  625 + String[] s = param.getStream().split("_");
  626 + if (!mediaInfo.isRtpEnable() || s.length != 2) {
  627 + defaultResult.setResult(HookResult.SUCCESS());
  628 + return defaultResult;
  629 + }
  630 + String deviceId = s[0];
  631 + String channelId = s[1];
  632 + Device device = redisCatchStorage.getDevice(deviceId);
  633 + if (device == null) {
  634 + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
  635 + return defaultResult;
  636 + }
  637 + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
  638 + if (deviceChannel == null) {
  639 + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
  640 + return defaultResult;
  641 + }
  642 + logger.info("[ZLM HOOK] 流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  643 + RequestMessage msg = new RequestMessage();
  644 + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
  645 + boolean exist = resultHolder.exist(key, null);
  646 + msg.setKey(key);
  647 + String uuid = UUID.randomUUID().toString();
  648 + msg.setId(uuid);
  649 + DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
  650 + DeferredResultEx<HookResult> deferredResultEx = new DeferredResultEx<>(result);
  651 +
  652 + result.onTimeout(() -> {
  653 + logger.info("点播接口等待超时");
  654 + // 释放rtpserver
  655 + msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
  656 + resultHolder.invokeResult(msg);
  657 + });
  658 + // TODO 在点播未成功的情况下在此调用接口点播会导致返回的流地址ip错误
  659 + deferredResultEx.setFilter(result1 -> {
  660 + WVPResult<StreamInfo> wvpResult1 = (WVPResult<StreamInfo>) result1;
  661 + HookResult resultForEnd = new HookResult();
  662 + resultForEnd.setCode(wvpResult1.getCode());
  663 + resultForEnd.setMsg(wvpResult1.getMsg());
  664 + return resultForEnd;
  665 + });
  666 +
  667 + // 录像查询以channelId作为deviceId查询
  668 + resultHolder.put(key, uuid, deferredResultEx);
  669 +
  670 + if (!exist) {
  671 + playService.play(mediaInfo, deviceId, channelId, null, eventResult -> {
  672 + msg.setData(new HookResult(eventResult.statusCode, eventResult.msg));
  673 + resultHolder.invokeResult(msg);
  674 + }, null);
  675 + }
  676 + return result;
  677 + } else {
  678 + // 拉流代理
  679 + StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
  680 + if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) {
  681 + streamProxyService.start(param.getApp(), param.getStream());
  682 + }
  683 + DeferredResult<HookResult> result = new DeferredResult<>();
  684 + result.setResult(HookResult.SUCCESS());
  685 + return result;
  686 + }
  687 + }
  688 +
  689 + /**
  690 + * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。
  691 + */
  692 + @ResponseBody
  693 + @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
  694 + public HookResult onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject) {
  695 +
  696 + jsonObject.put("ip", request.getRemoteAddr());
  697 + ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
  698 + zlmServerConfig.setIp(request.getRemoteAddr());
  699 + logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
  700 + taskExecutor.execute(() -> {
  701 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
  702 + if (subscribes != null && subscribes.size() > 0) {
  703 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  704 + subscribe.response(null, jsonObject);
  705 + }
  706 + }
  707 + mediaServerService.zlmServerOnline(zlmServerConfig);
  708 + });
  709 +
  710 + return HookResult.SUCCESS();
  711 + }
  712 +
  713 + /**
  714 + * 发送rtp(startSendRtp)被动关闭时回调
  715 + */
  716 + @ResponseBody
  717 + @PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8")
  718 + public HookResult onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param) {
  719 +
  720 + logger.info("[ZLM HOOK] rtp发送关闭:{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream());
  721 +
  722 + // 查找对应的上级推流,发送停止
  723 + if (!"rtp".equals(param.getApp())) {
  724 + return HookResult.SUCCESS();
  725 + }
  726 + taskExecutor.execute(() -> {
  727 + List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
  728 + if (sendRtpItems.size() > 0) {
  729 + for (SendRtpItem sendRtpItem : sendRtpItems) {
  730 + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
  731 + try {
  732 + commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
  733 + } catch (SipException | InvalidArgumentException | ParseException e) {
  734 + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
  735 + }
  736 + redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
  737 + sendRtpItem.getCallId(), sendRtpItem.getStreamId());
  738 + }
  739 + }
  740 + });
  741 +
  742 + return HookResult.SUCCESS();
  743 + }
  744 +
  745 + /**
  746 + * rtpServer收流超时
  747 + */
  748 + @ResponseBody
  749 + @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
  750 + public HookResult onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param) {
  751 + logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
  752 +
  753 + taskExecutor.execute(() -> {
  754 + JSONObject json = (JSONObject) JSON.toJSON(param);
  755 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
  756 + if (subscribes != null && subscribes.size() > 0) {
  757 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  758 + subscribe.response(null, json);
  759 + }
  760 + }
  761 + });
  762 +
  763 + return HookResult.SUCCESS();
  764 + }
  765 +
  766 + private Map<String, String> urlParamToMap(String params) {
  767 + HashMap<String, String> map = new HashMap<>();
  768 + if (ObjectUtils.isEmpty(params)) {
  769 + return map;
  770 + }
  771 + String[] paramsArray = params.split("&");
  772 + if (paramsArray.length == 0) {
  773 + return map;
  774 + }
  775 + for (String param : paramsArray) {
  776 + String[] paramArray = param.split("=");
  777 + if (paramArray.length == 2) {
  778 + map.put(paramArray[0], paramArray[1]);
  779 + }
  780 + }
  781 + return map;
  782 + }
785 } 783 }
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -36,7 +36,7 @@ public class ZLMRESTfulUtils { @@ -36,7 +36,7 @@ public class ZLMRESTfulUtils {
36 // 设置连接超时时间 36 // 设置连接超时时间
37 httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS); 37 httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS);
38 // 设置读取超时时间 38 // 设置读取超时时间
39 - httpClientBuilder.readTimeout(15,TimeUnit.SECONDS); 39 + httpClientBuilder.readTimeout(10,TimeUnit.SECONDS);
40 // 设置连接池 40 // 设置连接池
41 httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES)); 41 httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES));
42 if (logger.isDebugEnabled()) { 42 if (logger.isDebugEnabled()) {
@@ -189,6 +189,7 @@ public class ZLMRESTfulUtils { @@ -189,6 +189,7 @@ public class ZLMRESTfulUtils {
189 FileOutputStream outStream = new FileOutputStream(snapFile); 189 FileOutputStream outStream = new FileOutputStream(snapFile);
190 190
191 outStream.write(Objects.requireNonNull(response.body()).bytes()); 191 outStream.write(Objects.requireNonNull(response.body()).bytes());
  192 + outStream.flush();
192 outStream.close(); 193 outStream.close();
193 } else { 194 } else {
194 logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message())); 195 logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message()));
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResult.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.dto.hook;
  2 +
  3 +public class HookResult {
  4 +
  5 + private int code;
  6 + private String msg;
  7 +
  8 +
  9 + public HookResult() {
  10 + }
  11 +
  12 + public HookResult(int code, String msg) {
  13 + this.code = code;
  14 + this.msg = msg;
  15 + }
  16 +
  17 + public static HookResult SUCCESS(){
  18 + return new HookResult(0, "success");
  19 + }
  20 +
  21 + public int getCode() {
  22 + return code;
  23 + }
  24 +
  25 + public void setCode(int code) {
  26 + this.code = code;
  27 + }
  28 +
  29 + public String getMsg() {
  30 + return msg;
  31 + }
  32 +
  33 + public void setMsg(String msg) {
  34 + this.msg = msg;
  35 + }
  36 +}
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.dto.hook;
  2 +
  3 +public class HookResultForOnPublish extends HookResult{
  4 +
  5 + private boolean enable_audio;
  6 + private boolean enable_mp4;
  7 + private int mp4_max_second;
  8 +
  9 + public HookResultForOnPublish() {
  10 + }
  11 +
  12 + public static HookResultForOnPublish SUCCESS(){
  13 + return new HookResultForOnPublish(0, "success");
  14 + }
  15 +
  16 + public HookResultForOnPublish(int code, String msg) {
  17 + setCode(code);
  18 + setMsg(msg);
  19 + }
  20 +
  21 + public boolean isEnable_audio() {
  22 + return enable_audio;
  23 + }
  24 +
  25 + public void setEnable_audio(boolean enable_audio) {
  26 + this.enable_audio = enable_audio;
  27 + }
  28 +
  29 + public boolean isEnable_mp4() {
  30 + return enable_mp4;
  31 + }
  32 +
  33 + public void setEnable_mp4(boolean enable_mp4) {
  34 + this.enable_mp4 = enable_mp4;
  35 + }
  36 +
  37 + public int getMp4_max_second() {
  38 + return mp4_max_second;
  39 + }
  40 +
  41 + public void setMp4_max_second(int mp4_max_second) {
  42 + this.mp4_max_second = mp4_max_second;
  43 + }
  44 +}
src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
@@ -152,6 +152,10 @@ public class GbStreamServiceImpl implements IGbStreamService { @@ -152,6 +152,10 @@ public class GbStreamServiceImpl implements IGbStreamService {
152 152
153 @Override 153 @Override
154 public void sendCatalogMsg(GbStream gbStream, String type) { 154 public void sendCatalogMsg(GbStream gbStream, String type) {
  155 + if (gbStream == null || type == null) {
  156 + logger.warn("[发送目录订阅]类型:流信息或类型为NULL");
  157 + return;
  158 + }
155 List<GbStream> gbStreams = new ArrayList<>(); 159 List<GbStream> gbStreams = new ArrayList<>();
156 if (gbStream.getGbId() != null) { 160 if (gbStream.getGbId() != null) {
157 gbStreams.add(gbStream); 161 gbStreams.add(gbStream);
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -33,6 +33,7 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo; @@ -33,6 +33,7 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo;
33 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 33 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
34 import com.genersoft.iot.vmp.storager.dao.MediaServerMapper; 34 import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
35 import com.genersoft.iot.vmp.utils.DateUtil; 35 import com.genersoft.iot.vmp.utils.DateUtil;
  36 +import com.genersoft.iot.vmp.utils.JsonUtil;
36 import com.genersoft.iot.vmp.utils.redis.RedisUtil; 37 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
37 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; 38 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
38 import okhttp3.OkHttpClient; 39 import okhttp3.OkHttpClient;
@@ -241,7 +242,10 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -241,7 +242,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
241 String onlineKey = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); 242 String onlineKey = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
242 for (Object mediaServerKey : mediaServerKeys) { 243 for (Object mediaServerKey : mediaServerKeys) {
243 String key = (String) mediaServerKey; 244 String key = (String) mediaServerKey;
244 - MediaServerItem mediaServerItem = (MediaServerItem) RedisUtil.get(key); 245 + MediaServerItem mediaServerItem = JsonUtil.redisJsonToObject(key, MediaServerItem.class);
  246 + if (Objects.isNull(mediaServerItem)) {
  247 + continue;
  248 + }
245 // 检查状态 249 // 检查状态
246 Double aDouble = RedisUtil.zScore(onlineKey, mediaServerItem.getId()); 250 Double aDouble = RedisUtil.zScore(onlineKey, mediaServerItem.getId());
247 if (aDouble != null) { 251 if (aDouble != null) {
@@ -293,7 +297,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -293,7 +297,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
293 return null; 297 return null;
294 } 298 }
295 String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerId; 299 String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerId;
296 - return (MediaServerItem)RedisUtil.get(key); 300 + return JsonUtil.redisJsonToObject(key, MediaServerItem.class);
297 } 301 }
298 302
299 303
@@ -410,8 +414,10 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -410,8 +414,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
410 SsrcConfig ssrcConfig = new SsrcConfig(zlmServerConfig.getGeneralMediaServerId(), null, sipConfig.getDomain()); 414 SsrcConfig ssrcConfig = new SsrcConfig(zlmServerConfig.getGeneralMediaServerId(), null, sipConfig.getDomain());
411 serverItem.setSsrcConfig(ssrcConfig); 415 serverItem.setSsrcConfig(ssrcConfig);
412 }else { 416 }else {
413 - MediaServerItem mediaServerItemInRedis = (MediaServerItem)RedisUtil.get(key);  
414 - serverItem.setSsrcConfig(mediaServerItemInRedis.getSsrcConfig()); 417 + MediaServerItem mediaServerItemInRedis = JsonUtil.redisJsonToObject(key, MediaServerItem.class);
  418 + if (Objects.nonNull(mediaServerItemInRedis)) {
  419 + serverItem.setSsrcConfig(mediaServerItemInRedis.getSsrcConfig());
  420 + }
415 } 421 }
416 RedisUtil.set(key, serverItem); 422 RedisUtil.set(key, serverItem);
417 resetOnlineServerItem(serverItem); 423 resetOnlineServerItem(serverItem);
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
@@ -184,7 +184,9 @@ public class StreamPushServiceImpl implements IStreamPushService { @@ -184,7 +184,9 @@ public class StreamPushServiceImpl implements IStreamPushService {
184 @Override 184 @Override
185 public boolean stop(String app, String streamId) { 185 public boolean stop(String app, String streamId) {
186 StreamPushItem streamPushItem = streamPushMapper.selectOne(app, streamId); 186 StreamPushItem streamPushItem = streamPushMapper.selectOne(app, streamId);
187 - gbStreamService.sendCatalogMsg(streamPushItem, CatalogEvent.DEL); 187 + if (streamPushItem != null) {
  188 + gbStreamService.sendCatalogMsg(streamPushItem, CatalogEvent.DEL);
  189 + }
188 190
189 platformGbStreamMapper.delByAppAndStream(app, streamId); 191 platformGbStreamMapper.delByAppAndStream(app, streamId);
190 gbStreamMapper.del(app, streamId); 192 gbStreamMapper.del(app, streamId);
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java
@@ -62,16 +62,16 @@ public class RedisAlarmMsgListener implements MessageListener { @@ -62,16 +62,16 @@ public class RedisAlarmMsgListener implements MessageListener {
62 } 62 }
63 String gbId = alarmChannelMessage.getGbId(); 63 String gbId = alarmChannelMessage.getGbId();
64 64
65 - DeviceAlarm deviceAlarm = new DeviceAlarm();  
66 - deviceAlarm.setCreateTime(DateUtil.getNow());  
67 - deviceAlarm.setChannelId(gbId);  
68 - deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription());  
69 - deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());  
70 - deviceAlarm.setAlarmPriority("1");  
71 - deviceAlarm.setAlarmTime(DateUtil.getNowForISO8601());  
72 - deviceAlarm.setAlarmType("1");  
73 - deviceAlarm.setLongitude(0D);  
74 - deviceAlarm.setLatitude(0D); 65 + DeviceAlarm deviceAlarm = new DeviceAlarm();
  66 + deviceAlarm.setCreateTime(DateUtil.getNow());
  67 + deviceAlarm.setChannelId(gbId);
  68 + deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription());
  69 + deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());
  70 + deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType());
  71 + deviceAlarm.setAlarmPriority("1");
  72 + deviceAlarm.setAlarmTime(DateUtil.getNowForISO8601());
  73 + deviceAlarm.setLongitude(0);
  74 + deviceAlarm.setLatitude(0);
75 75
76 if (ObjectUtils.isEmpty(gbId)) { 76 if (ObjectUtils.isEmpty(gbId)) {
77 // 发送给所有的上级 77 // 发送给所有的上级
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
@@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.storager; @@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.storager;
2 2
3 import com.genersoft.iot.vmp.gb28181.bean.*; 3 import com.genersoft.iot.vmp.gb28181.bean.*;
4 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; 4 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
5 -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;  
6 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; 5 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
7 import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo; 6 import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
8 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; 7 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
@@ -186,7 +185,13 @@ public interface IVideoManagerStorage { @@ -186,7 +185,13 @@ public interface IVideoManagerStorage {
186 185
187 Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId); 186 Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId);
188 187
189 - 188 + /**
  189 + * 针对deviceinfo指令的查询接口
  190 + * @param platformId 平台id
  191 + * @param channelId 通道id
  192 + * @return 设备信息
  193 + */
  194 + Device queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId);
190 /** 195 /**
191 * 添加Mobile Position设备移动位置 196 * 添加Mobile Position设备移动位置
192 * @param mobilePosition 197 * @param mobilePosition
@@ -324,6 +329,8 @@ public interface IVideoManagerStorage { @@ -324,6 +329,8 @@ public interface IVideoManagerStorage {
324 */ 329 */
325 boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList); 330 boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList);
326 331
  332 + boolean updateChannels(String deviceId, List<DeviceChannel> deviceChannelList);
  333 +
327 /** 334 /**
328 * 获取目录信息 335 * 获取目录信息
329 * @param platformId 336 * @param platformId
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java
1 package com.genersoft.iot.vmp.storager.dao; 1 package com.genersoft.iot.vmp.storager.dao;
2 2
3 import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; 3 import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
4 -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;  
5 -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;  
6 -import org.apache.ibatis.annotations.*; 4 +import org.apache.ibatis.annotations.Delete;
  5 +import org.apache.ibatis.annotations.Insert;
  6 +import org.apache.ibatis.annotations.Mapper;
  7 +import org.apache.ibatis.annotations.Select;
7 import org.springframework.stereotype.Repository; 8 import org.springframework.stereotype.Repository;
8 9
9 import java.util.List; 10 import java.util.List;
@@ -20,7 +21,7 @@ public interface DeviceAlarmMapper { @@ -20,7 +21,7 @@ public interface DeviceAlarmMapper {
20 int add(DeviceAlarm alarm); 21 int add(DeviceAlarm alarm);
21 22
22 23
23 - @Select(value = {" <script>" + 24 + @Select( value = {" <script>" +
24 " SELECT * FROM device_alarm " + 25 " SELECT * FROM device_alarm " +
25 " WHERE 1=1 " + 26 " WHERE 1=1 " +
26 " <if test=\"deviceId != null\" > AND deviceId = #{deviceId}</if>" + 27 " <if test=\"deviceId != null\" > AND deviceId = #{deviceId}</if>" +
src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java
@@ -107,4 +107,14 @@ public interface PlatformChannelMapper { @@ -107,4 +107,14 @@ public interface PlatformChannelMapper {
107 "DELETE FROM platform_gb_channel WHERE platformId=#{platformId} and catalogId=#{catalogId}" + 107 "DELETE FROM platform_gb_channel WHERE platformId=#{platformId} and catalogId=#{catalogId}" +
108 "</script>") 108 "</script>")
109 int delChannelForGBByCatalogId(String platformId, String catalogId); 109 int delChannelForGBByCatalogId(String platformId, String catalogId);
  110 +
  111 + @Select("select dc.channelId deviceId,dc.name,d.manufacturer,d.model,d.firmware\n" +
  112 + "from platform_gb_channel pgc\n" +
  113 + " left join device_channel dc on dc.id = pgc.deviceChannelId\n" +
  114 + " left join device d on dc.deviceId = d.deviceId\n" +
  115 + "where dc.channelId = #{channelId} and pgc.platformId=#{platformId}")
  116 + List<Device> queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId);
  117 +
  118 + @Select("SELECT pgc.platformId FROM platform_gb_channel pgc left join device_channel dc on dc.id = pgc.deviceChannelId WHERE dc.channelId='${channelId}'")
  119 + List<String> queryParentPlatformByChannelId(String channelId);
110 } 120 }
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
@@ -17,6 +17,7 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage; @@ -17,6 +17,7 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
17 import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; 17 import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
18 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo; 18 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
19 import com.genersoft.iot.vmp.utils.DateUtil; 19 import com.genersoft.iot.vmp.utils.DateUtil;
  20 +import com.genersoft.iot.vmp.utils.JsonUtil;
20 import com.genersoft.iot.vmp.utils.SystemInfoUtils; 21 import com.genersoft.iot.vmp.utils.SystemInfoUtils;
21 import com.genersoft.iot.vmp.utils.redis.RedisUtil; 22 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
22 import org.slf4j.Logger; 23 import org.slf4j.Logger;
@@ -157,7 +158,10 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -157,7 +158,10 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
157 } 158 }
158 for (Object player : players) { 159 for (Object player : players) {
159 String key = (String) player; 160 String key = (String) player;
160 - StreamInfo streamInfo = (StreamInfo) RedisUtil.get(key); 161 + StreamInfo streamInfo = JsonUtil.redisJsonToObject(key, StreamInfo.class);
  162 + if (Objects.isNull(streamInfo)) {
  163 + continue;
  164 + }
161 streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getChannelId(), streamInfo); 165 streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getChannelId(), streamInfo);
162 } 166 }
163 return streamInfos; 167 return streamInfos;
@@ -624,8 +628,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -624,8 +628,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
624 @Override 628 @Override
625 public ThirdPartyGB queryMemberNoGBId(String queryKey) { 629 public ThirdPartyGB queryMemberNoGBId(String queryKey) {
626 String key = VideoManagerConstants.WVP_STREAM_GB_ID_PREFIX + queryKey; 630 String key = VideoManagerConstants.WVP_STREAM_GB_ID_PREFIX + queryKey;
627 - JSONObject jsonObject = (JSONObject)RedisUtil.get(key);  
628 - return jsonObject.to(ThirdPartyGB.class); 631 + return JsonUtil.redisJsonToObject(key, ThirdPartyGB.class);
629 } 632 }
630 633
631 @Override 634 @Override
@@ -664,7 +667,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -664,7 +667,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
664 @Override 667 @Override
665 public Device getDevice(String deviceId) { 668 public Device getDevice(String deviceId) {
666 String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId; 669 String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId;
667 - return (Device)RedisUtil.get(key); 670 + return JsonUtil.redisJsonToObject(key, Device.class);
668 } 671 }
669 672
670 @Override 673 @Override
@@ -676,7 +679,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -676,7 +679,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
676 @Override 679 @Override
677 public GPSMsgInfo getGpsMsgInfo(String gbId) { 680 public GPSMsgInfo getGpsMsgInfo(String gbId) {
678 String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gbId; 681 String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gbId;
679 - return (GPSMsgInfo)RedisUtil.get(key); 682 + return JsonUtil.redisJsonToObject(key, GPSMsgInfo.class);
680 } 683 }
681 684
682 @Override 685 @Override
@@ -686,9 +689,9 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -686,9 +689,9 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
686 List<Object> keys = RedisUtil.scan(scanKey); 689 List<Object> keys = RedisUtil.scan(scanKey);
687 for (Object o : keys) { 690 for (Object o : keys) {
688 String key = (String) o; 691 String key = (String) o;
689 - GPSMsgInfo gpsMsgInfo = (GPSMsgInfo) RedisUtil.get(key);  
690 - if (!gpsMsgInfo.isStored()) { // 只取没有存过得  
691 - result.add((GPSMsgInfo) RedisUtil.get(key)); 692 + GPSMsgInfo gpsMsgInfo = JsonUtil.redisJsonToObject(key, GPSMsgInfo.class);
  693 + if (Objects.nonNull(gpsMsgInfo) && !gpsMsgInfo.isStored()) { // 只取没有存过得
  694 + result.add(JsonUtil.redisJsonToObject(key, GPSMsgInfo.class));
692 } 695 }
693 } 696 }
694 697
@@ -710,7 +713,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -710,7 +713,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
710 @Override 713 @Override
711 public StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream) { 714 public StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream) {
712 String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream ; 715 String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream ;
713 - return (StreamAuthorityInfo) RedisUtil.get(key); 716 + return JsonUtil.redisJsonToObject(key, StreamAuthorityInfo.class);
714 717
715 } 718 }
716 719
@@ -721,7 +724,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -721,7 +724,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
721 List<Object> keys = RedisUtil.scan(scanKey); 724 List<Object> keys = RedisUtil.scan(scanKey);
722 for (Object o : keys) { 725 for (Object o : keys) {
723 String key = (String) o; 726 String key = (String) o;
724 - result.add((StreamAuthorityInfo) RedisUtil.get(key)); 727 + result.add(JsonUtil.redisJsonToObject(key, StreamAuthorityInfo.class));
725 } 728 }
726 return result; 729 return result;
727 } 730 }
@@ -735,7 +738,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -735,7 +738,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
735 List<Object> keys = RedisUtil.scan(scanKey); 738 List<Object> keys = RedisUtil.scan(scanKey);
736 if (keys.size() > 0) { 739 if (keys.size() > 0) {
737 String key = (String) keys.get(0); 740 String key = (String) keys.get(0);
738 - result = (OnStreamChangedHookParam)RedisUtil.get(key); 741 + result = JsonUtil.redisJsonToObject(key, OnStreamChangedHookParam.class);
739 } 742 }
740 743
741 return result; 744 return result;
@@ -827,7 +830,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -827,7 +830,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
827 830
828 @Override 831 @Override
829 public void sendAlarmMsg(AlarmChannelMessage msg) { 832 public void sendAlarmMsg(AlarmChannelMessage msg) {
830 - String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM; 833 + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE;
831 logger.info("[redis发送通知] 报警{}: {}", key, JSON.toJSON(msg)); 834 logger.info("[redis发送通知] 报警{}: {}", key, JSON.toJSON(msg));
832 RedisUtil.convertAndSend(key, (JSONObject)JSON.toJSON(msg)); 835 RedisUtil.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
833 } 836 }
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
@@ -126,6 +126,15 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { @@ -126,6 +126,15 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
126 if (allChannelMap.containsKey(deviceChannel.getChannelId())) { 126 if (allChannelMap.containsKey(deviceChannel.getChannelId())) {
127 deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId()); 127 deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId());
128 deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio()); 128 deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio());
  129 + if (allChannelMap.get(deviceChannel.getChannelId()).getStatus() !=deviceChannel.getStatus()){
  130 + List<String> strings = platformChannelMapper.queryParentPlatformByChannelId(deviceChannel.getChannelId());
  131 + if (!CollectionUtils.isEmpty(strings)){
  132 + strings.forEach(platformId->{
  133 + eventPublisher.catalogEventPublish(platformId, deviceChannel, deviceChannel.getStatus()==1?CatalogEvent.ON:CatalogEvent.OFF);
  134 + });
  135 + }
  136 +
  137 + }
129 } 138 }
130 channels.add(deviceChannel); 139 channels.add(deviceChannel);
131 if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) { 140 if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) {
@@ -187,6 +196,119 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { @@ -187,6 +196,119 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
187 196
188 } 197 }
189 198
  199 +
  200 + @Override
  201 + public boolean updateChannels(String deviceId, List<DeviceChannel> deviceChannelList) {
  202 + if (CollectionUtils.isEmpty(deviceChannelList)) {
  203 + return false;
  204 + }
  205 + List<DeviceChannel> allChannels = deviceChannelMapper.queryAllChannels(deviceId);
  206 + Map<String,DeviceChannel> allChannelMap = new ConcurrentHashMap<>();
  207 + if (allChannels.size() > 0) {
  208 + for (DeviceChannel deviceChannel : allChannels) {
  209 + allChannelMap.put(deviceChannel.getChannelId(), deviceChannel);
  210 + }
  211 + }
  212 + List<DeviceChannel> addChannels = new ArrayList<>();
  213 + List<DeviceChannel> updateChannels = new ArrayList<>();
  214 +
  215 +
  216 + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
  217 + // 数据去重
  218 + StringBuilder stringBuilder = new StringBuilder();
  219 + Map<String, Integer> subContMap = new HashMap<>();
  220 + if (deviceChannelList.size() > 0) {
  221 + // 数据去重
  222 + Set<String> gbIdSet = new HashSet<>();
  223 + for (DeviceChannel deviceChannel : deviceChannelList) {
  224 + if (!gbIdSet.contains(deviceChannel.getChannelId())) {
  225 + gbIdSet.add(deviceChannel.getChannelId());
  226 + if (allChannelMap.containsKey(deviceChannel.getChannelId())) {
  227 + deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId());
  228 + deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio());
  229 + updateChannels.add(deviceChannel);
  230 + }else {
  231 + addChannels.add(deviceChannel);
  232 + }
  233 + if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) {
  234 + if (subContMap.get(deviceChannel.getParentId()) == null) {
  235 + subContMap.put(deviceChannel.getParentId(), 1);
  236 + }else {
  237 + Integer count = subContMap.get(deviceChannel.getParentId());
  238 + subContMap.put(deviceChannel.getParentId(), count++);
  239 + }
  240 + }
  241 + }else {
  242 + stringBuilder.append(deviceChannel.getChannelId()).append(",");
  243 + }
  244 + }
  245 + if (addChannels.size() > 0) {
  246 + for (DeviceChannel channel : addChannels) {
  247 + if (subContMap.get(channel.getChannelId()) != null){
  248 + channel.setSubCount(subContMap.get(channel.getChannelId()));
  249 + }
  250 + }
  251 + }
  252 + if (updateChannels.size() > 0) {
  253 + for (DeviceChannel channel : updateChannels) {
  254 + if (subContMap.get(channel.getChannelId()) != null){
  255 + channel.setSubCount(subContMap.get(channel.getChannelId()));
  256 + }
  257 + }
  258 + }
  259 +
  260 + }
  261 + if (stringBuilder.length() > 0) {
  262 + logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder);
  263 + }
  264 + if(CollectionUtils.isEmpty(updateChannels) && CollectionUtils.isEmpty(addChannels) ){
  265 + logger.info("通道更新,数据为空={}" , deviceChannelList);
  266 + return false;
  267 + }
  268 + try {
  269 + int limitCount = 300;
  270 + boolean result = false;
  271 + if (addChannels.size() > 0) {
  272 + if (addChannels.size() > limitCount) {
  273 + for (int i = 0; i < addChannels.size(); i += limitCount) {
  274 + int toIndex = i + limitCount;
  275 + if (i + limitCount > addChannels.size()) {
  276 + toIndex = addChannels.size();
  277 + }
  278 + result = result || deviceChannelMapper.batchAdd(addChannels.subList(i, toIndex)) < 0;
  279 + }
  280 + }else {
  281 + result = result || deviceChannelMapper.batchAdd(addChannels) < 0;
  282 + }
  283 + }
  284 + if (updateChannels.size() > 0) {
  285 + if (updateChannels.size() > limitCount) {
  286 + for (int i = 0; i < updateChannels.size(); i += limitCount) {
  287 + int toIndex = i + limitCount;
  288 + if (i + limitCount > updateChannels.size()) {
  289 + toIndex = updateChannels.size();
  290 + }
  291 + result = result || deviceChannelMapper.batchUpdate(updateChannels.subList(i, toIndex)) < 0;
  292 + }
  293 + }else {
  294 + result = result || deviceChannelMapper.batchUpdate(updateChannels) < 0;
  295 + }
  296 + }
  297 + if (result) {
  298 + //事务回滚
  299 + dataSourceTransactionManager.rollback(transactionStatus);
  300 + }else {
  301 + //手动提交
  302 + dataSourceTransactionManager.commit(transactionStatus);
  303 + }
  304 + return true;
  305 + }catch (Exception e) {
  306 + e.printStackTrace();
  307 + dataSourceTransactionManager.rollback(transactionStatus);
  308 + return false;
  309 + }
  310 + }
  311 +
190 @Override 312 @Override
191 public void deviceChannelOnline(String deviceId, String channelId) { 313 public void deviceChannelOnline(String deviceId, String channelId) {
192 deviceChannelMapper.online(deviceId, channelId); 314 deviceChannelMapper.online(deviceId, channelId);
@@ -464,6 +586,20 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { @@ -464,6 +586,20 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
464 586
465 } 587 }
466 588
  589 + @Override
  590 + public Device queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId) {
  591 + List<Device> devices = platformChannelMapper.queryDeviceInfoByPlatformIdAndChannelId(platformId, channelId);
  592 + if (devices.size() > 1) {
  593 + // 出现长度大于0的时候肯定是国标通道的ID重复了
  594 + logger.warn("国标ID存在重复:{}", channelId);
  595 + }
  596 + if (devices.size() == 0) {
  597 + return null;
  598 + }else {
  599 + return devices.get(0);
  600 + }
  601 + }
  602 +
467 /** 603 /**
468 * 查询最新移动位置 604 * 查询最新移动位置
469 * @param deviceId 605 * @param deviceId
src/main/java/com/genersoft/iot/vmp/utils/JsonUtil.java 0 → 100644
  1 +package com.genersoft.iot.vmp.utils;
  2 +
  3 +import com.alibaba.fastjson2.JSON;
  4 +import com.alibaba.fastjson2.JSONObject;
  5 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
  6 +
  7 +import java.util.Objects;
  8 +
  9 +/**
  10 + * JsonUtil
  11 + *
  12 + * @author KunLong-Luo
  13 + * @version 1.0.0
  14 + * @since 2023/2/2 15:24
  15 + */
  16 +public final class JsonUtil {
  17 +
  18 + private JsonUtil() {
  19 + }
  20 +
  21 + /**
  22 + * safe json type conversion
  23 + *
  24 + * @param key redis key
  25 + * @param clazz cast type
  26 + * @param <T>
  27 + * @return result type
  28 + */
  29 + public static <T> T redisJsonToObject(String key, Class<T> clazz) {
  30 + Object jsonObject = RedisUtil.get(key);
  31 + if (Objects.isNull(jsonObject)) {
  32 + return null;
  33 + }
  34 + return clazz.cast(jsonObject);
  35 + }
  36 +}
0 \ No newline at end of file 37 \ No newline at end of file
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java
@@ -110,7 +110,7 @@ public class DeviceControl { @@ -110,7 +110,7 @@ public class DeviceControl {
110 msg.setKey(key); 110 msg.setKey(key);
111 msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", event.statusCode, event.msg)); 111 msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", event.statusCode, event.msg));
112 resultHolder.invokeAllResult(msg); 112 resultHolder.invokeAllResult(msg);
113 - }); 113 + },null);
114 } catch (InvalidArgumentException | SipException | ParseException e) { 114 } catch (InvalidArgumentException | SipException | ParseException e) {
115 logger.error("[命令发送失败] 开始/停止录像: {}", e.getMessage()); 115 logger.error("[命令发送失败] 开始/停止录像: {}", e.getMessage());
116 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); 116 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
@@ -143,7 +143,7 @@ public class DeviceControl { @@ -143,7 +143,7 @@ public class DeviceControl {
143 msg.setKey(key); 143 msg.setKey(key);
144 msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", event.statusCode, event.msg)); 144 msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", event.statusCode, event.msg));
145 resultHolder.invokeResult(msg); 145 resultHolder.invokeResult(msg);
146 - }); 146 + },null);
147 } catch (InvalidArgumentException | SipException | ParseException e) { 147 } catch (InvalidArgumentException | SipException | ParseException e) {
148 logger.error("[命令发送失败] 布防/撤防操作: {}", e.getMessage()); 148 logger.error("[命令发送失败] 布防/撤防操作: {}", e.getMessage());
149 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage()); 149 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage());
@@ -192,7 +192,7 @@ public class DeviceControl { @@ -192,7 +192,7 @@ public class DeviceControl {
192 msg.setKey(key); 192 msg.setKey(key);
193 msg.setData(String.format("报警复位操作失败,错误码: %s, %s", event.statusCode, event.msg)); 193 msg.setData(String.format("报警复位操作失败,错误码: %s, %s", event.statusCode, event.msg));
194 resultHolder.invokeResult(msg); 194 resultHolder.invokeResult(msg);
195 - }); 195 + },null);
196 } catch (InvalidArgumentException | SipException | ParseException e) { 196 } catch (InvalidArgumentException | SipException | ParseException e) {
197 logger.error("[命令发送失败] 报警复位: {}", e.getMessage()); 197 logger.error("[命令发送失败] 报警复位: {}", e.getMessage());
198 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); 198 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
@@ -274,7 +274,7 @@ public class DeviceControl { @@ -274,7 +274,7 @@ public class DeviceControl {
274 msg.setKey(key); 274 msg.setKey(key);
275 msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", event.statusCode, event.msg)); 275 msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", event.statusCode, event.msg));
276 resultHolder.invokeResult(msg); 276 resultHolder.invokeResult(msg);
277 - }); 277 + },null);
278 } catch (InvalidArgumentException | SipException | ParseException e) { 278 } catch (InvalidArgumentException | SipException | ParseException e) {
279 logger.error("[命令发送失败] 看守位控制: {}", e.getMessage()); 279 logger.error("[命令发送失败] 看守位控制: {}", e.getMessage());
280 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); 280 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
1 package com.genersoft.iot.vmp.vmanager.gb28181.playback; 1 package com.genersoft.iot.vmp.vmanager.gb28181.playback;
2 2
3 import com.genersoft.iot.vmp.common.StreamInfo; 3 import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.conf.UserSetting;
4 import com.genersoft.iot.vmp.conf.exception.ControllerException; 5 import com.genersoft.iot.vmp.conf.exception.ControllerException;
5 import com.genersoft.iot.vmp.conf.exception.ServiceException; 6 import com.genersoft.iot.vmp.conf.exception.ServiceException;
6 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; 7 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
@@ -64,13 +65,16 @@ public class PlaybackController { @@ -64,13 +65,16 @@ public class PlaybackController {
64 @Autowired 65 @Autowired
65 private DeferredResultHolder resultHolder; 66 private DeferredResultHolder resultHolder;
66 67
  68 + @Autowired
  69 + private UserSetting userSetting;
  70 +
67 @Operation(summary = "开始视频回放") 71 @Operation(summary = "开始视频回放")
68 @Parameter(name = "deviceId", description = "设备国标编号", required = true) 72 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
69 @Parameter(name = "channelId", description = "通道国标编号", required = true) 73 @Parameter(name = "channelId", description = "通道国标编号", required = true)
70 @Parameter(name = "startTime", description = "开始时间", required = true) 74 @Parameter(name = "startTime", description = "开始时间", required = true)
71 @Parameter(name = "endTime", description = "结束时间", required = true) 75 @Parameter(name = "endTime", description = "结束时间", required = true)
72 @GetMapping("/start/{deviceId}/{channelId}") 76 @GetMapping("/start/{deviceId}/{channelId}")
73 - public DeferredResult<WVPResult<StreamContent>> play(@PathVariable String deviceId, @PathVariable String channelId, 77 + public DeferredResult<WVPResult<StreamContent>> start(@PathVariable String deviceId, @PathVariable String channelId,
74 String startTime, String endTime) { 78 String startTime, String endTime) {
75 79
76 if (logger.isDebugEnabled()) { 80 if (logger.isDebugEnabled()) {
@@ -79,7 +83,7 @@ public class PlaybackController { @@ -79,7 +83,7 @@ public class PlaybackController {
79 83
80 String uuid = UUID.randomUUID().toString(); 84 String uuid = UUID.randomUUID().toString();
81 String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId; 85 String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId;
82 - DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(30000L); 86 + DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
83 resultHolder.put(key, uuid, result); 87 resultHolder.put(key, uuid, result);
84 88
85 WVPResult<StreamContent> wvpResult = new WVPResult<>(); 89 WVPResult<StreamContent> wvpResult = new WVPResult<>();
src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
@@ -11,17 +11,13 @@ import com.genersoft.iot.vmp.utils.DateUtil; @@ -11,17 +11,13 @@ import com.genersoft.iot.vmp.utils.DateUtil;
11 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; 11 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
12 import com.genersoft.iot.vmp.vmanager.bean.WVPResult; 12 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
13 import com.github.pagehelper.PageInfo; 13 import com.github.pagehelper.PageInfo;
14 -  
15 import io.swagger.v3.oas.annotations.Operation; 14 import io.swagger.v3.oas.annotations.Operation;
16 import io.swagger.v3.oas.annotations.Parameter; 15 import io.swagger.v3.oas.annotations.Parameter;
17 import io.swagger.v3.oas.annotations.tags.Tag; 16 import io.swagger.v3.oas.annotations.tags.Tag;
18 import org.springframework.beans.factory.annotation.Autowired; 17 import org.springframework.beans.factory.annotation.Autowired;
19 -import org.springframework.http.HttpStatus;  
20 -import org.springframework.http.ResponseEntity;  
21 import org.springframework.security.authentication.AuthenticationManager; 18 import org.springframework.security.authentication.AuthenticationManager;
22 import org.springframework.util.DigestUtils; 19 import org.springframework.util.DigestUtils;
23 import org.springframework.util.ObjectUtils; 20 import org.springframework.util.ObjectUtils;
24 -import org.springframework.util.StringUtils;  
25 import org.springframework.web.bind.annotation.*; 21 import org.springframework.web.bind.annotation.*;
26 22
27 import javax.security.sasl.AuthenticationException; 23 import javax.security.sasl.AuthenticationException;
@@ -90,7 +86,7 @@ public class UserController { @@ -90,7 +86,7 @@ public class UserController {
90 86
91 87
92 @PostMapping("/add") 88 @PostMapping("/add")
93 - @Operation(summary = "停止视频回放") 89 + @Operation(summary = "添加用户")
94 @Parameter(name = "username", description = "用户名", required = true) 90 @Parameter(name = "username", description = "用户名", required = true)
95 @Parameter(name = "password", description = "密码(未md5加密的密码)", required = true) 91 @Parameter(name = "password", description = "密码(未md5加密的密码)", required = true)
96 @Parameter(name = "roleId", description = "角色ID", required = true) 92 @Parameter(name = "roleId", description = "角色ID", required = true)
src/main/resources/all-application.yml
@@ -167,7 +167,7 @@ user-settings: @@ -167,7 +167,7 @@ user-settings:
167 senior-sdp: false 167 senior-sdp: false
168 # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认) 168 # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认)
169 save-position-history: false 169 save-position-history: false
170 - # 点播等待超时时间,单位:毫秒 170 + # 点播/录像回放 等待超时时间,单位:毫秒
171 play-timeout: 18000 171 play-timeout: 18000
172 # 上级点播等待超时时间,单位:毫秒 172 # 上级点播等待超时时间,单位:毫秒
173 platform-play-timeout: 60000 173 platform-play-timeout: 60000
src/main/resources/application-dev.yml
1 spring: 1 spring:
2 - # [可选]上传文件大小限制  
3 - servlet:  
4 - multipart:  
5 - max-file-size: 10MB  
6 - max-request-size: 100MB  
7 - # REDIS数据库配置  
8 - redis:  
9 - # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1  
10 - host: 127.0.0.1  
11 - # [必须修改] 端口号  
12 - port: 6379  
13 - # [可选] 数据库 DB  
14 - database: 6  
15 - # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接  
16 - password: face2020  
17 - # [可选] 超时时间  
18 - timeout: 10000  
19 - # mysql数据源  
20 - datasource:  
21 - type: com.alibaba.druid.pool.DruidDataSource  
22 - driver-class-name: com.mysql.cj.jdbc.Driver  
23 - url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true  
24 - username: root  
25 - password: 123456  
26 - druid:  
27 - initialSize: 10 # 连接池初始化连接数  
28 - maxActive: 200 # 连接池最大连接数  
29 - minIdle: 5 # 连接池最小空闲连接数  
30 - maxWait: 60000 # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。  
31 - keepAlive: true # 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。  
32 - validationQuery: select 1 # 检测连接是否有效sql,要求是查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。  
33 - testWhileIdle: true # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。  
34 - testOnBorrow: false # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。  
35 - testOnReturn: false # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。  
36 - poolPreparedStatements: false # 是否開啟PSCache,並且指定每個連線上PSCache的大小  
37 - timeBetweenEvictionRunsMillis: 60000 # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒  
38 - minEvictableIdleTimeMillis: 300000 # 配置一個連線在池中最小生存的時間,單位是毫秒  
39 - filters: stat,slf4j # 配置监控统计拦截的filters,监控统计用的filter:sta, 日志用的filter:log4j  
40 - useGlobalDataSourceStat: true # 合并多个DruidDataSource的监控数据  
41 - # 通过connectProperties属性来打开mergeSql功能;慢SQL记录  
42 - connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000  
43 - #stat-view-servlet.url-pattern: /admin/druid/* 2 + # [可选]上传文件大小限制
  3 + servlet:
  4 + multipart:
  5 + max-file-size: 10MB
  6 + max-request-size: 100MB
  7 + # REDIS数据库配置
  8 + redis:
  9 + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
  10 + host: 127.0.0.1
  11 + # [必须修改] 端口号
  12 + port: 6379
  13 + # [可选] 数据库 DB
  14 + database: 6
  15 + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
  16 + password: face2020
  17 + # [可选] 超时时间
  18 + timeout: 10000
  19 + # mysql数据源
  20 + datasource:
  21 + type: com.alibaba.druid.pool.DruidDataSource
  22 + driver-class-name: com.mysql.cj.jdbc.Driver
  23 + url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
  24 + username: root
  25 + password: 123456
  26 + druid:
  27 + initialSize: 10 # 连接池初始化连接数
  28 + maxActive: 200 # 连接池最大连接数
  29 + minIdle: 5 # 连接池最小空闲连接数
  30 + maxWait: 60000 # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
  31 + keepAlive: true # 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。
  32 + validationQuery: select 1 # 检测连接是否有效sql,要求是查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
  33 + testWhileIdle: true # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
  34 + testOnBorrow: false # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
  35 + testOnReturn: false # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
  36 + poolPreparedStatements: false # 是否開啟PSCache,並且指定每個連線上PSCache的大小
  37 + timeBetweenEvictionRunsMillis: 60000 # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒
  38 + minEvictableIdleTimeMillis: 300000 # 配置一個連線在池中最小生存的時間,單位是毫秒
  39 + filters: stat,slf4j # 配置监控统计拦截的filters,监控统计用的filter:sta, 日志用的filter:log4j
  40 + useGlobalDataSourceStat: true # 合并多个DruidDataSource的监控数据
  41 + # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
  42 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000
  43 + #stat-view-servlet.url-pattern: /admin/druid/*
44 44
45 #[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 45 #[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
46 server: 46 server:
47 - port: 18080 47 + port: 18080
48 48
49 # 作为28181服务器的配置 49 # 作为28181服务器的配置
50 sip: 50 sip:
51 - # [必须修改] 本机的IP  
52 - ip: 192.168.41.16  
53 - # [可选] 28181服务监听的端口  
54 - port: 5060  
55 - # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007)  
56 - # 后两位为行业编码,定义参照附录D.3  
57 - # 3701020049标识山东济南历下区 信息行业接入  
58 - # [可选]  
59 - domain: 4401020049  
60 - # [可选]  
61 - id: 44010200492000000001  
62 - # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验  
63 - password: admin123 51 + # [必须修改] 本机的IP
  52 + ip: 192.168.41.16
  53 + # [可选] 28181服务监听的端口
  54 + port: 5060
  55 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007)
  56 + # 后两位为行业编码,定义参照附录D.3
  57 + # 3701020049标识山东济南历下区 信息行业接入
  58 + # [可选]
  59 + domain: 4401020049
  60 + # [可选]
  61 + id: 44010200492000000001
  62 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验
  63 + password: admin123
64 64
65 #zlm 默认服务器配置 65 #zlm 默认服务器配置
66 media: 66 media:
67 - id: FQ3TF8yT83wh5Wvz  
68 - # [必须修改] zlm服务器的内网IP  
69 - ip: 192.168.41.16  
70 - # [必须修改] zlm服务器的http.port  
71 - http-port: 8091  
72 - # [可选] zlm服务器的hook.admin_params=secret  
73 - secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc  
74 - # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试  
75 - rtp:  
76 - # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输  
77 - enable: true  
78 - # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功  
79 - port-range: 30000,30500 # 端口范围  
80 - # [可选] 国标级联在此范围内选择端口发送媒体流,  
81 - send-port-range: 30000,30500 # 端口范围  
82 - # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用  
83 - record-assist-port: 18081 67 + id: FQ3TF8yT83wh5Wvz
  68 + # [必须修改] zlm服务器的内网IP
  69 + ip: 192.168.41.16
  70 + # [必须修改] zlm服务器的http.port
  71 + http-port: 8091
  72 + # [可选] zlm服务器的hook.admin_params=secret
  73 + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
  74 + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试
  75 + rtp:
  76 + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
  77 + enable: true
  78 + # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功
  79 + port-range: 30000,30500 # 端口范围
  80 + # [可选] 国标级联在此范围内选择端口发送媒体流,
  81 + send-port-range: 30000,30500 # 端口范围
  82 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用
  83 + record-assist-port: 18081
84 # [可选] 日志配置, 一般不需要改 84 # [可选] 日志配置, 一般不需要改
85 logging: 85 logging:
86 - config: classpath:logback-spring-local.xml 86 + config: classpath:logback-spring-local.xml
src/main/resources/application.yml
1 spring: 1 spring:
  2 + application:
  3 + name: wvp
2 profiles: 4 profiles:
3 active: local 5 active: local
  6 + # flayway相关配置
  7 + flyway:
  8 + enabled: true #是否启用flyway(默认true)
  9 + locations: classpath:db/migration #这个路径指的是fly版本控制的sql语句存放的路径,可以多个,可以给每个环境使用不同位置,比如classpath:db/migration,classpath:test/db/migration
  10 + baseline-on-migrate: true #开启自动创建flyway元数据表标识 默认: false
  11 + # 与 baseline-on-migrate: true 搭配使用,将当前数据库初始版本设置为0
  12 + baseline-version: 0
  13 + clean-disabled: true #禁止flyway执行清理
  14 + # 假如已经执行了版本1和版本3,如果增加了一个版本2,下面这个选项将会允许执行版本2的脚本
  15 + out-of-order: true
  16 + table: flyway_schema_history_${spring.application.name} #用于记录所有的版本变化记录
4 \ No newline at end of file 17 \ No newline at end of file
sql/mysql.sql renamed to src/main/resources/db/migration/V2.6.7_20230201__初始化.sql
@@ -39,6 +39,7 @@ CREATE TABLE `device` ( @@ -39,6 +39,7 @@ CREATE TABLE `device` (
39 `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 39 `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
40 `port` int DEFAULT NULL, 40 `port` int DEFAULT NULL,
41 `expires` int DEFAULT NULL, 41 `expires` int DEFAULT NULL,
  42 + `keepaliveIntervalTime` int DEFAULT NULL,
42 `subscribeCycleForCatalog` int DEFAULT NULL, 43 `subscribeCycleForCatalog` int DEFAULT NULL,
43 `hostAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, 44 `hostAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
44 `charset` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 45 `charset` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
web_src/build/utils.js
@@ -47,7 +47,8 @@ exports.cssLoaders = function (options) { @@ -47,7 +47,8 @@ exports.cssLoaders = function (options) {
47 if (options.extract) { 47 if (options.extract) {
48 return ExtractTextPlugin.extract({ 48 return ExtractTextPlugin.extract({
49 use: loaders, 49 use: loaders,
50 - fallback: 'vue-style-loader' 50 + fallback: 'vue-style-loader',
  51 + publicPath: '../../'
51 }) 52 })
52 } else { 53 } else {
53 return ['vue-style-loader'].concat(loaders) 54 return ['vue-style-loader'].concat(loaders)
web_src/config/index.js
@@ -8,8 +8,8 @@ module.exports = { @@ -8,8 +8,8 @@ module.exports = {
8 dev: { 8 dev: {
9 9
10 // Paths 10 // Paths
11 - assetsSubDirectory: 'static',  
12 - assetsPublicPath: '/', 11 + assetsSubDirectory: './static',
  12 + assetsPublicPath: './',
13 proxyTable: { 13 proxyTable: {
14 '/debug': { 14 '/debug': {
15 target: 'https://default.wvp-pro.cn:18080', 15 target: 'https://default.wvp-pro.cn:18080',
@@ -61,7 +61,7 @@ module.exports = { @@ -61,7 +61,7 @@ module.exports = {
61 // Paths 61 // Paths
62 assetsRoot: path.resolve(__dirname, '../../src/main/resources/static/'), 62 assetsRoot: path.resolve(__dirname, '../../src/main/resources/static/'),
63 assetsSubDirectory: './static', 63 assetsSubDirectory: './static',
64 - assetsPublicPath: '/', 64 + assetsPublicPath: './',
65 65
66 /** 66 /**
67 * Source Maps 67 * Source Maps
web_src/src/components/CloudRecord.vue
@@ -133,7 +133,7 @@ @@ -133,7 +133,7 @@
133 let that = this; 133 let that = this;
134 this.$axios({ 134 this.$axios({
135 method: 'get', 135 method: 'get',
136 - url:`/record_proxy/${that.mediaServerId}/api/record/list`, 136 + url:`./record_proxy/${that.mediaServerId}/api/record/list`,
137 params: { 137 params: {
138 page: that.currentPage, 138 page: that.currentPage,
139 count: that.count 139 count: that.count
@@ -185,7 +185,7 @@ @@ -185,7 +185,7 @@
185 let that = this; 185 let that = this;
186 this.$axios({ 186 this.$axios({
187 method: 'delete', 187 method: 'delete',
188 - url:`/record_proxy/api/record/delete`, 188 + url:`./record_proxy/api/record/delete`,
189 params: { 189 params: {
190 page: that.currentPage, 190 page: that.currentPage,
191 count: that.count 191 count: that.count
web_src/src/components/CloudRecordDetail.vue
1 <template> 1 <template>
2 <div id="recordDetail"> 2 <div id="recordDetail">
3 <el-container> 3 <el-container>
4 -  
5 - <el-aside width="300px">  
6 - 4 + <el-aside width="260px">
7 <div class="record-list-box-box"> 5 <div class="record-list-box-box">
8 - <el-date-picker size="mini" v-model="chooseDate" :picker-options="pickerOptions" type="date" value-format="yyyy-MM-dd" placeholder="日期" @change="dateChange()"></el-date-picker> 6 + <div style="margin-top: 20px">
  7 + <el-date-picker size="mini" style="width: 160px" v-model="chooseDate" :picker-options="pickerOptions" type="date" value-format="yyyy-MM-dd" placeholder="日期" @change="dateChange()"></el-date-picker>
  8 + <el-button size="mini" type="primary" icon="fa fa-cloud-download" style="margin: auto; margin-left: 12px " title="裁剪合并" @click="drawerOpen"></el-button>
  9 + </div>
9 <div class="record-list-box" :style="recordListStyle"> 10 <div class="record-list-box" :style="recordListStyle">
10 <ul v-if="detailFiles.length >0" class="infinite-list record-list" v-infinite-scroll="infiniteScroll" > 11 <ul v-if="detailFiles.length >0" class="infinite-list record-list" v-infinite-scroll="infiniteScroll" >
11 - <li v-for="item in detailFiles" class="infinite-list-item record-list-item" > 12 + <li v-for="(item,index) in detailFiles" :key="index" class="infinite-list-item record-list-item" >
12 <el-tag v-if="choosedFile != item" @click="chooseFile(item)"> 13 <el-tag v-if="choosedFile != item" @click="chooseFile(item)">
13 <i class="el-icon-video-camera" ></i> 14 <i class="el-icon-video-camera" ></i>
14 {{ item.substring(0,17)}} 15 {{ item.substring(0,17)}}
@@ -24,9 +25,7 @@ @@ -24,9 +25,7 @@
24 <div v-if="detailFiles.length ==0" class="record-list-no-val" >暂无数据</div> 25 <div v-if="detailFiles.length ==0" class="record-list-no-val" >暂无数据</div>
25 </div> 26 </div>
26 27
27 - <div class="record-list-option">  
28 - <el-button size="mini" type="primary" icon="fa fa-cloud-download" style="margin: auto; " title="裁剪合并" @click="drawerOpen"></el-button>  
29 - </div> 28 +
30 </el-aside> 29 </el-aside>
31 <el-main style="padding: 22px"> 30 <el-main style="padding: 22px">
32 <div class="playBox" :style="playerStyle"> 31 <div class="playBox" :style="playerStyle">
@@ -45,7 +44,7 @@ @@ -45,7 +44,7 @@
45 :marks="playTimeSliderMarks"> 44 :marks="playTimeSliderMarks">
46 </el-slider> 45 </el-slider>
47 <div class="slider-val-box"> 46 <div class="slider-val-box">
48 - <div class="slider-val" v-for="item of detailFiles" :style="'width:' + getDataWidth(item) + '%; left:' + getDataLeft(item) + '%'"></div> 47 + <div class="slider-val" v-for="(item,index) of detailFiles" :key="index" :style="'width:' + getDataWidth(item) + '%; left:' + getDataLeft(item) + '%'"></div>
49 </div> 48 </div>
50 </div> 49 </div>
51 50
@@ -62,7 +61,7 @@ @@ -62,7 +61,7 @@
62 <el-tab-pane name="running"> 61 <el-tab-pane name="running">
63 <span slot="label"><i class="el-icon-scissors"></i>进行中</span> 62 <span slot="label"><i class="el-icon-scissors"></i>进行中</span>
64 <ul class="task-list"> 63 <ul class="task-list">
65 - <li class="task-list-item" v-for="item in taskListForRuning"> 64 + <li class="task-list-item" v-for="(item,index) in taskListForRuning" :key="index">
66 <div class="task-list-item-box"> 65 <div class="task-list-item-box">
67 <span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span> 66 <span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span>
68 <el-progress :percentage="(parseFloat(item.percentage)*100).toFixed(1)"></el-progress> 67 <el-progress :percentage="(parseFloat(item.percentage)*100).toFixed(1)"></el-progress>
@@ -74,10 +73,10 @@ @@ -74,10 +73,10 @@
74 <el-tab-pane name="ended"> 73 <el-tab-pane name="ended">
75 <span slot="label"><i class="el-icon-finished"></i>已完成</span> 74 <span slot="label"><i class="el-icon-finished"></i>已完成</span>
76 <ul class="task-list"> 75 <ul class="task-list">
77 - <li class="task-list-item" v-for="item in taskListEnded"> 76 + <li class="task-list-item" v-for="(item, index) in taskListEnded" :key="index">
78 <div class="task-list-item-box" style="height: 2rem;line-height: 2rem;"> 77 <div class="task-list-item-box" style="height: 2rem;line-height: 2rem;">
79 <span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span> 78 <span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span>
80 - <a class="el-icon-download download-btn" :href="basePath + '/download.html?url=../' + item.recordFile" target="_blank"> 79 + <a class="el-icon-download download-btn" :href="mediaServerPath + '/download.html?url=../' + item.recordFile" target="_blank">
81 </a> 80 </a>
82 </div> 81 </div>
83 </li> 82 </li>
@@ -116,7 +115,7 @@ @@ -116,7 +115,7 @@
116 props: ['recordFile', 'mediaServerId', 'dateFiles', 'mediaServerPath'], 115 props: ['recordFile', 'mediaServerId', 'dateFiles', 'mediaServerPath'],
117 data() { 116 data() {
118 return { 117 return {
119 - basePath: `${this.mediaServerPath}`, 118 + basePath: `${this.mediaServerPath}/record`,
120 dateFilesObj: [], 119 dateFilesObj: [],
121 detailFiles: [], 120 detailFiles: [],
122 chooseDate: null, 121 chooseDate: null,
@@ -147,6 +146,7 @@ @@ -147,6 +146,7 @@
147 "margin-bottom": "20px", 146 "margin-bottom": "20px",
148 "height": this.winHeight + "px", 147 "height": this.winHeight + "px",
149 }, 148 },
  149 + timeFormat:'00:00:00',
150 winHeight: window.innerHeight - 240, 150 winHeight: window.innerHeight - 240,
151 playTime: 0, 151 playTime: 0,
152 playTimeSliderMarks: { 152 playTimeSliderMarks: {
@@ -213,7 +213,7 @@ @@ -213,7 +213,7 @@
213 this.currentPage = 1; 213 this.currentPage = 1;
214 this.sliderMIn= 0; 214 this.sliderMIn= 0;
215 this.sliderMax= 86400; 215 this.sliderMax= 86400;
216 - let chooseFullDate = new Date(this.chooseDate + " " + "00:00:00"); 216 + let chooseFullDate = new Date(this.chooseDate +" " + this.timeFormat);
217 if (chooseFullDate.getFullYear() !== this.queryDate.getFullYear() 217 if (chooseFullDate.getFullYear() !== this.queryDate.getFullYear()
218 || chooseFullDate.getMonth() !== this.queryDate.getMonth()){ 218 || chooseFullDate.getMonth() !== this.queryDate.getMonth()){
219 // this.getDateInYear() 219 // this.getDateInYear()
@@ -222,8 +222,8 @@ @@ -222,8 +222,8 @@
222 if (this.detailFiles.length > 0){ 222 if (this.detailFiles.length > 0){
223 let timeForFile = this.getTimeForFile(this.detailFiles[0]); 223 let timeForFile = this.getTimeForFile(this.detailFiles[0]);
224 let lastTimeForFile = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1]); 224 let lastTimeForFile = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1]);
225 - let timeNum = timeForFile[0].getTime() - new Date(this.chooseDate + " " + "00:00:00").getTime()  
226 - let lastTimeNum = lastTimeForFile[1].getTime() - new Date(this.chooseDate + " " + "00:00:00").getTime() 225 + let timeNum = timeForFile[0].getTime() - new Date(this.chooseDate + " " + this.timeFormat).getTime()
  226 + let lastTimeNum = lastTimeForFile[1].getTime() - new Date(this.chooseDate + " " + this.timeFormat).getTime()
227 227
228 this.playTime = parseInt(timeNum/1000) 228 this.playTime = parseInt(timeNum/1000)
229 this.sliderMIn = parseInt(timeNum/1000 - timeNum/1000%(60*60)) 229 this.sliderMIn = parseInt(timeNum/1000 - timeNum/1000%(60*60))
@@ -241,7 +241,7 @@ @@ -241,7 +241,7 @@
241 let that = this; 241 let that = this;
242 that.$axios({ 242 that.$axios({
243 method: 'get', 243 method: 'get',
244 - url:`/record_proxy/${that.mediaServerId}/api/record/file/list`, 244 + url:`./record_proxy/${that.mediaServerId}/api/record/file/list`,
245 params: { 245 params: {
246 app: that.recordFile.app, 246 app: that.recordFile.app,
247 stream: that.recordFile.stream, 247 stream: that.recordFile.stream,
@@ -281,14 +281,14 @@ @@ -281,14 +281,14 @@
281 }, 281 },
282 getDataLeft(item){ 282 getDataLeft(item){
283 let timeForFile = this.getTimeForFile(item); 283 let timeForFile = this.getTimeForFile(item);
284 - let differenceTime = timeForFile[0].getTime() - new Date(this.chooseDate + " 00:00:00").getTime() 284 + let differenceTime = timeForFile[0].getTime() - new Date(this.chooseDate + " " + this.timeFormat).getTime()
285 return parseFloat((differenceTime - this.sliderMIn * 1000)/((this.sliderMax - this.sliderMIn)*1000))*100 ; 285 return parseFloat((differenceTime - this.sliderMIn * 1000)/((this.sliderMax - this.sliderMIn)*1000))*100 ;
286 }, 286 },
287 playTimeChange(val){ 287 playTimeChange(val){
288 let minTime = this.getTimeForFile(this.detailFiles[0])[0] 288 let minTime = this.getTimeForFile(this.detailFiles[0])[0]
289 let maxTime = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1])[1]; 289 let maxTime = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1])[1];
290 this.chooseFile(null); 290 this.chooseFile(null);
291 - let timeMilli = new Date(this.chooseDate + " 00:00:00").getTime() + val*1000 291 + let timeMilli = new Date(this.chooseDate + " " + this.timeFormat).getTime() + val*1000
292 if (timeMilli >= minTime.getTime() && timeMilli <= maxTime.getTime()){ 292 if (timeMilli >= minTime.getTime() && timeMilli <= maxTime.getTime()){
293 for (let i = 0; i < this.detailFiles.length; i++) { 293 for (let i = 0; i < this.detailFiles.length; i++) {
294 let timeForFile = this.getTimeForFile(this.detailFiles[i]); 294 let timeForFile = this.getTimeForFile(this.detailFiles[i]);
@@ -302,10 +302,20 @@ @@ -302,10 +302,20 @@
302 }, 302 },
303 getTimeForFile(file){ 303 getTimeForFile(file){
304 let timeStr = file.substring(0,17); 304 let timeStr = file.substring(0,17);
305 - let starTime = new Date(this.chooseDate + " " + timeStr.split("-")[0]);  
306 - let endTime = new Date(this.chooseDate + " " + timeStr.split("-")[1]); 305 + if(timeStr.indexOf("~") > 0){
  306 + timeStr = timeStr.replaceAll("-",":")
  307 + }
  308 + let timeArr = timeStr.split("~");
  309 + let starTime = new Date(this.chooseDate + " " + timeArr[0]);
  310 + let endTime = new Date(this.chooseDate + " " + timeArr[1]);
  311 + if(this.checkIsOver24h(starTime,endTime)){
  312 + endTime = new Date(this.chooseDate + " " + "23:59:59");
  313 + }
307 return [starTime, endTime, endTime.getTime() - starTime.getTime()]; 314 return [starTime, endTime, endTime.getTime() - starTime.getTime()];
308 }, 315 },
  316 + checkIsOver24h(starTime,endTime){
  317 + return starTime > endTime;
  318 + },
309 playTimeFormat(val){ 319 playTimeFormat(val){
310 let h = parseInt(val/3600); 320 let h = parseInt(val/3600);
311 let m = parseInt((val - h*3600)/60); 321 let m = parseInt((val - h*3600)/60);
@@ -330,7 +340,7 @@ @@ -330,7 +340,7 @@
330 let that = this; 340 let that = this;
331 this.$axios({ 341 this.$axios({
332 method: 'delete', 342 method: 'delete',
333 - url:`/record_proxy/${that.mediaServerId}/api/record/delete`, 343 + url:`./record_proxy/${that.mediaServerId}/api/record/delete`,
334 params: { 344 params: {
335 page: that.currentPage, 345 page: that.currentPage,
336 count: that.count 346 count: that.count
@@ -349,7 +359,7 @@ @@ -349,7 +359,7 @@
349 that.dateFilesObj = {}; 359 that.dateFilesObj = {};
350 this.$axios({ 360 this.$axios({
351 method: 'get', 361 method: 'get',
352 - url:`/record_proxy/${that.mediaServerId}/api/record/date/list`, 362 + url:`./record_proxy/${that.mediaServerId}/api/record/date/list`,
353 params: { 363 params: {
354 app: that.recordFile.app, 364 app: that.recordFile.app,
355 stream: that.recordFile.stream 365 stream: that.recordFile.stream
@@ -398,7 +408,7 @@ @@ -398,7 +408,7 @@
398 let that = this; 408 let that = this;
399 this.$axios({ 409 this.$axios({
400 method: 'get', 410 method: 'get',
401 - url:`/record_proxy/${that.mediaServerId}/api/record/file/download/task/add`, 411 + url:`./record_proxy/${that.mediaServerId}/api/record/file/download/task/add`,
402 params: { 412 params: {
403 app: that.recordFile.app, 413 app: that.recordFile.app,
404 stream: that.recordFile.stream, 414 stream: that.recordFile.stream,
@@ -423,7 +433,7 @@ @@ -423,7 +433,7 @@
423 let that = this; 433 let that = this;
424 this.$axios({ 434 this.$axios({
425 method: 'get', 435 method: 'get',
426 - url:`/record_proxy/${that.mediaServerId}/api/record/file/download/task/list`, 436 + url:`./record_proxy/${that.mediaServerId}/api/record/file/download/task/list`,
427 params: { 437 params: {
428 isEnd: isEnd, 438 isEnd: isEnd,
429 } 439 }
web_src/src/components/DeviceList.vue
@@ -152,7 +152,7 @@ export default { @@ -152,7 +152,7 @@ export default {
152 this.getDeviceListLoading = true; 152 this.getDeviceListLoading = true;
153 this.$axios({ 153 this.$axios({
154 method: 'get', 154 method: 'get',
155 - url: `/api/device/query/devices`, 155 + url: `./api/device/query/devices`,
156 params: { 156 params: {
157 page: this.currentPage, 157 page: this.currentPage,
158 count: this.count 158 count: this.count
@@ -182,7 +182,7 @@ export default { @@ -182,7 +182,7 @@ export default {
182 }).then(() => { 182 }).then(() => {
183 this.$axios({ 183 this.$axios({
184 method: 'delete', 184 method: 'delete',
185 - url: `/api/device/query/devices/${row.deviceId}/delete` 185 + url: `./api/device/query/devices/${row.deviceId}/delete`
186 }).then((res) => { 186 }).then((res) => {
187 this.getDeviceList(); 187 this.getDeviceList();
188 }).catch((error) => { 188 }).catch((error) => {
@@ -208,7 +208,7 @@ export default { @@ -208,7 +208,7 @@ export default {
208 let that = this; 208 let that = this;
209 this.$axios({ 209 this.$axios({
210 method: 'get', 210 method: 'get',
211 - url: '/api/device/query/devices/' + itemData.deviceId + '/sync' 211 + url: './api/device/query/devices/' + itemData.deviceId + '/sync'
212 }).then((res) => { 212 }).then((res) => {
213 console.log("刷新设备结果:" + JSON.stringify(res)); 213 console.log("刷新设备结果:" + JSON.stringify(res));
214 if (res.data.code !== 0) { 214 if (res.data.code !== 0) {
@@ -242,7 +242,7 @@ export default { @@ -242,7 +242,7 @@ export default {
242 await this.$axios({ 242 await this.$axios({
243 method: 'get', 243 method: 'get',
244 async: false, 244 async: false,
245 - url: `/api/device/query/${deviceId}/sync_status/`, 245 + url: `./api/device/query/${deviceId}/sync_status/`,
246 }).then((res) => { 246 }).then((res) => {
247 if (res.data.code == 0) { 247 if (res.data.code == 0) {
248 if (res.data.data.errorMsg !== null) { 248 if (res.data.data.errorMsg !== null) {
@@ -261,7 +261,7 @@ export default { @@ -261,7 +261,7 @@ export default {
261 let that = this; 261 let that = this;
262 this.$axios({ 262 this.$axios({
263 method: 'post', 263 method: 'post',
264 - url: '/api/device/query/transport/' + row.deviceId + '/' + row.streamMode 264 + url: './api/device/query/transport/' + row.deviceId + '/' + row.streamMode
265 }).then(function (res) { 265 }).then(function (res) {
266 266
267 }).catch(function (e) { 267 }).catch(function (e) {
web_src/src/components/GBRecordDetail.vue
@@ -197,7 +197,7 @@ @@ -197,7 +197,7 @@
197 this.detailFiles = []; 197 this.detailFiles = [];
198 this.$axios({ 198 this.$axios({
199 method: 'get', 199 method: 'get',
200 - url: '/api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' + this.endTime 200 + url: './api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' + this.endTime
201 }).then((res)=>{ 201 }).then((res)=>{
202 this.recordsLoading = false; 202 this.recordsLoading = false;
203 if(res.data.code === 0) { 203 if(res.data.code === 0) {
@@ -249,7 +249,7 @@ @@ -249,7 +249,7 @@
249 } else { 249 } else {
250 this.$axios({ 250 this.$axios({
251 method: 'get', 251 method: 'get',
252 - url: '/api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' + 252 + url: './api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' +
253 this.endTime 253 this.endTime
254 }).then((res)=> { 254 }).then((res)=> {
255 if (res.data.code === 0) { 255 if (res.data.code === 0) {
@@ -273,7 +273,7 @@ @@ -273,7 +273,7 @@
273 console.log('前端控制:播放'); 273 console.log('前端控制:播放');
274 this.$axios({ 274 this.$axios({
275 method: 'get', 275 method: 'get',
276 - url: '/api/playback/resume/' + this.streamId 276 + url: './api/playback/resume/' + this.streamId
277 }).then((res)=> { 277 }).then((res)=> {
278 this.$refs["recordVideoPlayer"].play(this.videoUrl) 278 this.$refs["recordVideoPlayer"].play(this.videoUrl)
279 }); 279 });
@@ -282,14 +282,14 @@ @@ -282,14 +282,14 @@
282 console.log('前端控制:暂停'); 282 console.log('前端控制:暂停');
283 this.$axios({ 283 this.$axios({
284 method: 'get', 284 method: 'get',
285 - url: '/api/playback/pause/' + this.streamId 285 + url: './api/playback/pause/' + this.streamId
286 }).then(function (res) {}); 286 }).then(function (res) {});
287 }, 287 },
288 gbScale(command){ 288 gbScale(command){
289 console.log('前端控制:倍速 ' + command); 289 console.log('前端控制:倍速 ' + command);
290 this.$axios({ 290 this.$axios({
291 method: 'get', 291 method: 'get',
292 - url: `/api/playback/speed/${this.streamId }/${command}` 292 + url: `./api/playback/speed/${this.streamId }/${command}`
293 }).then(function (res) {}); 293 }).then(function (res) {});
294 }, 294 },
295 downloadRecord: function (row) { 295 downloadRecord: function (row) {
@@ -311,7 +311,7 @@ @@ -311,7 +311,7 @@
311 }else { 311 }else {
312 this.$axios({ 312 this.$axios({
313 method: 'get', 313 method: 'get',
314 - url: '/api/gb_record/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' + 314 + url: './api/gb_record/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
315 row.endTime + '&downloadSpeed=4' 315 row.endTime + '&downloadSpeed=4'
316 }).then( (res)=> { 316 }).then( (res)=> {
317 if (res.data.code === 0) { 317 if (res.data.code === 0) {
@@ -332,7 +332,7 @@ @@ -332,7 +332,7 @@
332 this.videoUrl = ''; 332 this.videoUrl = '';
333 this.$axios({ 333 this.$axios({
334 method: 'get', 334 method: 'get',
335 - url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.streamId 335 + url: './api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.streamId
336 }).then((res)=> { 336 }).then((res)=> {
337 if (callback) callback(res) 337 if (callback) callback(res)
338 }); 338 });
@@ -342,7 +342,7 @@ @@ -342,7 +342,7 @@
342 this.videoUrl = ''; 342 this.videoUrl = '';
343 this.$axios({ 343 this.$axios({
344 method: 'get', 344 method: 'get',
345 - url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId 345 + url: './api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId
346 }).then(function (res) { 346 }).then(function (res) {
347 if (callback) callback() 347 if (callback) callback()
348 }); 348 });
web_src/src/components/Login.vue
@@ -81,7 +81,7 @@ export default { @@ -81,7 +81,7 @@ export default {
81 81
82 this.$axios({ 82 this.$axios({
83 method: 'get', 83 method: 'get',
84 - url:"/api/user/login", 84 + url:"./api/user/login",
85 params: loginParam 85 params: loginParam
86 }).then(function (res) { 86 }).then(function (res) {
87 window.clearTimeout(timeoutTask) 87 window.clearTimeout(timeoutTask)
web_src/src/components/ParentPlatformList.vue
@@ -128,7 +128,7 @@ export default { @@ -128,7 +128,7 @@ export default {
128 var that = this; 128 var that = this;
129 that.$axios({ 129 that.$axios({
130 method: 'delete', 130 method: 'delete',
131 - url:`/api/platform/delete/${platform.serverGBId}` 131 + url:`./api/platform/delete/${platform.serverGBId}`
132 }).then(function (res) { 132 }).then(function (res) {
133 if (res.data.code === 0) { 133 if (res.data.code === 0) {
134 that.$message({ 134 that.$message({
@@ -162,7 +162,7 @@ export default { @@ -162,7 +162,7 @@ export default {
162 162
163 this.$axios({ 163 this.$axios({
164 method: 'get', 164 method: 'get',
165 - url:`/api/platform/query/${that.count}/${that.currentPage}` 165 + url:`./api/platform/query/${that.count}/${that.currentPage}`
166 }).then(function (res) { 166 }).then(function (res) {
167 if (res.data.code === 0) { 167 if (res.data.code === 0) {
168 that.total = res.data.data.total; 168 that.total = res.data.data.total;
web_src/src/components/PushVideoList.vue
@@ -171,7 +171,7 @@ export default { @@ -171,7 +171,7 @@ export default {
171 this.getDeviceListLoading = true; 171 this.getDeviceListLoading = true;
172 this.$axios({ 172 this.$axios({
173 method: 'get', 173 method: 'get',
174 - url: `/api/push/list`, 174 + url: `./api/push/list`,
175 params: { 175 params: {
176 page: that.currentPage, 176 page: that.currentPage,
177 count: that.count, 177 count: that.count,
@@ -197,7 +197,7 @@ export default { @@ -197,7 +197,7 @@ export default {
197 this.getListLoading = true; 197 this.getListLoading = true;
198 this.$axios({ 198 this.$axios({
199 method: 'get', 199 method: 'get',
200 - url: '/api/push/getPlayUrl', 200 + url: './api/push/getPlayUrl',
201 params: { 201 params: {
202 app: row.app, 202 app: row.app,
203 stream: row.stream, 203 stream: row.stream,
@@ -223,7 +223,7 @@ export default { @@ -223,7 +223,7 @@ export default {
223 let that = this; 223 let that = this;
224 that.$axios({ 224 that.$axios({
225 method: "post", 225 method: "post",
226 - url: "/api/push/stop", 226 + url: "./api/push/stop",
227 params: { 227 params: {
228 app: row.app, 228 app: row.app,
229 streamId: row.stream 229 streamId: row.stream
@@ -247,7 +247,7 @@ export default { @@ -247,7 +247,7 @@ export default {
247 let that = this; 247 let that = this;
248 that.$axios({ 248 that.$axios({
249 method: "delete", 249 method: "delete",
250 - url: "/api/push/remove_form_gb", 250 + url: "./api/push/remove_form_gb",
251 data: row 251 data: row
252 }).then((res) => { 252 }).then((res) => {
253 if (res.data.code === 0) { 253 if (res.data.code === 0) {
@@ -274,7 +274,7 @@ export default { @@ -274,7 +274,7 @@ export default {
274 let that = this; 274 let that = this;
275 that.$axios({ 275 that.$axios({
276 method: "delete", 276 method: "delete",
277 - url: "/api/push/batchStop", 277 + url: "./api/push/batchStop",
278 data: { 278 data: {
279 gbStreams: this.multipleSelection 279 gbStreams: this.multipleSelection
280 } 280 }
web_src/src/components/StreamProxyList.vue
@@ -167,7 +167,7 @@ @@ -167,7 +167,7 @@
167 let that = this; 167 let that = this;
168 this.$axios({ 168 this.$axios({
169 method: 'get', 169 method: 'get',
170 - url:`/api/proxy/list`, 170 + url:`./api/proxy/list`,
171 params: { 171 params: {
172 page: that.currentPage, 172 page: that.currentPage,
173 count: that.count 173 count: that.count
@@ -190,7 +190,7 @@ @@ -190,7 +190,7 @@
190 addOnvif: function(){ 190 addOnvif: function(){
191 this.$axios({ 191 this.$axios({
192 method: 'get', 192 method: 'get',
193 - url:`/api/onvif/search?timeout=3000`, 193 + url:`./api/onvif/search?timeout=3000`,
194 }).then((res) =>{ 194 }).then((res) =>{
195 if (res.data.code === 0 ){ 195 if (res.data.code === 0 ){
196 if (res.data.data.length > 0) { 196 if (res.data.data.length > 0) {
@@ -218,7 +218,7 @@ @@ -218,7 +218,7 @@
218 let that = this; 218 let that = this;
219 this.$axios({ 219 this.$axios({
220 method: 'get', 220 method: 'get',
221 - url:`/api/push/getPlayUrl`, 221 + url:`./api/push/getPlayUrl`,
222 params: { 222 params: {
223 app: row.app, 223 app: row.app,
224 stream: row.stream, 224 stream: row.stream,
@@ -247,7 +247,7 @@ @@ -247,7 +247,7 @@
247 let that = this; 247 let that = this;
248 that.$axios({ 248 that.$axios({
249 method:"delete", 249 method:"delete",
250 - url:"/api/proxy/del", 250 + url:"./api/proxy/del",
251 params:{ 251 params:{
252 app: row.app, 252 app: row.app,
253 stream: row.stream 253 stream: row.stream
@@ -263,7 +263,7 @@ @@ -263,7 +263,7 @@
263 this.$set(row, 'startBtnLoading', true) 263 this.$set(row, 'startBtnLoading', true)
264 this.$axios({ 264 this.$axios({
265 method: 'get', 265 method: 'get',
266 - url:`/api/proxy/start`, 266 + url:`./api/proxy/start`,
267 params: { 267 params: {
268 app: row.app, 268 app: row.app,
269 stream: row.stream 269 stream: row.stream
@@ -295,7 +295,7 @@ @@ -295,7 +295,7 @@
295 let that = this; 295 let that = this;
296 this.$axios({ 296 this.$axios({
297 method: 'get', 297 method: 'get',
298 - url:`/api/proxy/stop`, 298 + url:`./api/proxy/stop`,
299 params: { 299 params: {
300 app: row.app, 300 app: row.app,
301 stream: row.stream 301 stream: row.stream
web_src/src/components/UserManager.vue
@@ -99,7 +99,7 @@ export default { @@ -99,7 +99,7 @@ export default {
99 this.getUserListLoading = true; 99 this.getUserListLoading = true;
100 this.$axios({ 100 this.$axios({
101 method: 'get', 101 method: 'get',
102 - url: `/api/user/users`, 102 + url: `./api/user/users`,
103 params: { 103 params: {
104 page: that.currentPage, 104 page: that.currentPage,
105 count: that.count 105 count: that.count
@@ -141,7 +141,7 @@ export default { @@ -141,7 +141,7 @@ export default {
141 }).then(() => { 141 }).then(() => {
142 this.$axios({ 142 this.$axios({
143 method: 'delete', 143 method: 'delete',
144 - url: `/api/user/delete?id=${row.id}` 144 + url: `./api/user/delete?id=${row.id}`
145 }).then((res) => { 145 }).then((res) => {
146 this.getUserList(); 146 this.getUserList();
147 }).catch((error) => { 147 }).catch((error) => {
web_src/src/components/channelList.vue
@@ -206,7 +206,7 @@ export default { @@ -206,7 +206,7 @@ export default {
206 if (typeof (this.$route.params.deviceId) == "undefined") return; 206 if (typeof (this.$route.params.deviceId) == "undefined") return;
207 this.$axios({ 207 this.$axios({
208 method: 'get', 208 method: 'get',
209 - url: `/api/device/query/devices/${this.$route.params.deviceId}/channels`, 209 + url: `./api/device/query/devices/${this.$route.params.deviceId}/channels`,
210 params: { 210 params: {
211 page: that.currentPage, 211 page: that.currentPage,
212 count: that.count, 212 count: that.count,
@@ -238,7 +238,7 @@ export default { @@ -238,7 +238,7 @@ export default {
238 let that = this; 238 let that = this;
239 this.$axios({ 239 this.$axios({
240 method: 'get', 240 method: 'get',
241 - url: '/api/play/start/' + deviceId + '/' + channelId 241 + url: './api/play/start/' + deviceId + '/' + channelId
242 }).then(function (res) { 242 }).then(function (res) {
243 console.log(res) 243 console.log(res)
244 that.isLoging = false; 244 that.isLoging = false;
@@ -278,7 +278,7 @@ export default { @@ -278,7 +278,7 @@ export default {
278 var that = this; 278 var that = this;
279 this.$axios({ 279 this.$axios({
280 method: 'get', 280 method: 'get',
281 - url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId 281 + url: './api/play/stop/' + this.deviceId + "/" + itemData.channelId
282 }).then(function (res) { 282 }).then(function (res) {
283 that.initData(); 283 that.initData();
284 }).catch(function (error) { 284 }).catch(function (error) {
@@ -334,7 +334,7 @@ export default { @@ -334,7 +334,7 @@ export default {
334 if (!this.showTree) { 334 if (!this.showTree) {
335 this.$axios({ 335 this.$axios({
336 method: 'get', 336 method: 'get',
337 - url: `/api/device/query/sub_channels/${this.deviceId}/${this.parentChannelId}/channels`, 337 + url: `./api/device/query/sub_channels/${this.deviceId}/${this.parentChannelId}/channels`,
338 params: { 338 params: {
339 page: this.currentPage, 339 page: this.currentPage,
340 count: this.count, 340 count: this.count,
@@ -358,7 +358,7 @@ export default { @@ -358,7 +358,7 @@ export default {
358 }else { 358 }else {
359 this.$axios({ 359 this.$axios({
360 method: 'get', 360 method: 'get',
361 - url: `/api/device/query/tree/channel/${this.deviceId}`, 361 + url: `./api/device/query/tree/channel/${this.deviceId}`,
362 params: { 362 params: {
363 parentId: this.parentChannelId, 363 parentId: this.parentChannelId,
364 page: this.currentPage, 364 page: this.currentPage,
@@ -387,7 +387,7 @@ export default { @@ -387,7 +387,7 @@ export default {
387 updateChannel: function (row) { 387 updateChannel: function (row) {
388 this.$axios({ 388 this.$axios({
389 method: 'post', 389 method: 'post',
390 - url: `/api/device/query/channel/update/${this.deviceId}`, 390 + url: `./api/device/query/channel/update/${this.deviceId}`,
391 params: row 391 params: row
392 }).then(function (res) { 392 }).then(function (res) {
393 console.log(JSON.stringify(res)); 393 console.log(JSON.stringify(res));
web_src/src/components/console.vue
@@ -114,7 +114,7 @@ export default { @@ -114,7 +114,7 @@ export default {
114 getSystemInfo: function (){ 114 getSystemInfo: function (){
115 this.$axios({ 115 this.$axios({
116 method: 'get', 116 method: 'get',
117 - url: `/api/server/system/info`, 117 + url: `./api/server/system/info`,
118 }).then( (res)=> { 118 }).then( (res)=> {
119 if (res.data.code === 0) { 119 if (res.data.code === 0) {
120 this.$refs.consoleCPU.setData(res.data.data.cpu) 120 this.$refs.consoleCPU.setData(res.data.data.cpu)
@@ -128,7 +128,7 @@ export default { @@ -128,7 +128,7 @@ export default {
128 getLoad: function (){ 128 getLoad: function (){
129 this.$axios({ 129 this.$axios({
130 method: 'get', 130 method: 'get',
131 - url: `/api/server/media_server/load`, 131 + url: `./api/server/media_server/load`,
132 }).then( (res)=> { 132 }).then( (res)=> {
133 if (res.data.code === 0) { 133 if (res.data.code === 0) {
134 this.$refs.consoleNodeLoad.setData(res.data.data) 134 this.$refs.consoleNodeLoad.setData(res.data.data)
@@ -139,7 +139,7 @@ export default { @@ -139,7 +139,7 @@ export default {
139 getResourceInfo: function (){ 139 getResourceInfo: function (){
140 this.$axios({ 140 this.$axios({
141 method: 'get', 141 method: 'get',
142 - url: `/api/server/resource/info`, 142 + url: `./api/server/resource/info`,
143 }).then( (res)=> { 143 }).then( (res)=> {
144 if (res.data.code === 0) { 144 if (res.data.code === 0) {
145 this.$refs.consoleResource.setData(res.data.data) 145 this.$refs.consoleResource.setData(res.data.data)
@@ -151,7 +151,7 @@ export default { @@ -151,7 +151,7 @@ export default {
151 151
152 this.$axios({ 152 this.$axios({
153 method: 'get', 153 method: 'get',
154 - url: `/api/server/system/configInfo`, 154 + url: `./api/server/system/configInfo`,
155 }).then( (res)=> { 155 }).then( (res)=> {
156 console.log(res) 156 console.log(res)
157 if (res.data.code === 0) { 157 if (res.data.code === 0) {
web_src/src/components/dialog/MediaServerEdit.vue
@@ -335,7 +335,7 @@ export default { @@ -335,7 +335,7 @@ export default {
335 var that = this; 335 var that = this;
336 await that.$axios({ 336 await that.$axios({
337 method: 'get', 337 method: 'get',
338 - url:`/api/platform/exit/${deviceGbId}` 338 + url:`./api/platform/exit/${deviceGbId}`
339 }).then(function (res) { 339 }).then(function (res) {
340 result = res.data; 340 result = res.data;
341 }).catch(function (error) { 341 }).catch(function (error) {
web_src/src/components/dialog/StreamProxyEdit.vue
@@ -195,7 +195,7 @@ export default { @@ -195,7 +195,7 @@ export default {
195 let that = this; 195 let that = this;
196 this.$axios({ 196 this.$axios({
197 method: 'get', 197 method: 'get',
198 - url:`/api/platform/query/10000/1` 198 + url:`./api/platform/query/10000/1`
199 }).then(function (res) { 199 }).then(function (res) {
200 that.platformList = res.data.data.list; 200 that.platformList = res.data.data.list;
201 }).catch(function (error) { 201 }).catch(function (error) {
@@ -212,7 +212,7 @@ export default { @@ -212,7 +212,7 @@ export default {
212 if (that.proxyParam.mediaServerId !== "auto"){ 212 if (that.proxyParam.mediaServerId !== "auto"){
213 that.$axios({ 213 that.$axios({
214 method: 'get', 214 method: 'get',
215 - url:`/api/proxy/ffmpeg_cmd/list`, 215 + url:`./api/proxy/ffmpeg_cmd/list`,
216 params: { 216 params: {
217 mediaServerId: that.proxyParam.mediaServerId 217 mediaServerId: that.proxyParam.mediaServerId
218 } 218 }
@@ -230,7 +230,7 @@ export default { @@ -230,7 +230,7 @@ export default {
230 this.noneReaderHandler(); 230 this.noneReaderHandler();
231 this.$axios({ 231 this.$axios({
232 method: 'post', 232 method: 'post',
233 - url:`/api/proxy/save`, 233 + url:`./api/proxy/save`,
234 data: this.proxyParam 234 data: this.proxyParam
235 }).then((res)=> { 235 }).then((res)=> {
236 this.dialogLoading = false; 236 this.dialogLoading = false;
@@ -261,7 +261,7 @@ export default { @@ -261,7 +261,7 @@ export default {
261 var that = this; 261 var that = this;
262 await that.$axios({ 262 await that.$axios({
263 method: 'get', 263 method: 'get',
264 - url:`/api/platform/exit/${deviceGbId}` 264 + url:`./api/platform/exit/${deviceGbId}`
265 }).then(function (res) { 265 }).then(function (res) {
266 result = res.data; 266 result = res.data;
267 }).catch(function (error) { 267 }).catch(function (error) {
web_src/src/components/dialog/SyncChannelProgress.vue
@@ -55,7 +55,7 @@ export default { @@ -55,7 +55,7 @@ export default {
55 getProgress(){ 55 getProgress(){
56 this.$axios({ 56 this.$axios({
57 method: 'get', 57 method: 'get',
58 - url:`/api/device/query/${this.deviceId}/sync_status/`, 58 + url:`./api/device/query/${this.deviceId}/sync_status/`,
59 }).then((res) => { 59 }).then((res) => {
60 if (res.data.code === 0) { 60 if (res.data.code === 0) {
61 if (!this.syncFlag) { 61 if (!this.syncFlag) {
web_src/src/components/dialog/addUser.vue
@@ -100,7 +100,7 @@ export default { @@ -100,7 +100,7 @@ export default {
100 onSubmit: function () { 100 onSubmit: function () {
101 this.$axios({ 101 this.$axios({
102 method: 'post', 102 method: 'post',
103 - url: "/api/user/add", 103 + url: "./api/user/add",
104 params: { 104 params: {
105 username: this.username, 105 username: this.username,
106 password: this.password, 106 password: this.password,
@@ -139,7 +139,7 @@ export default { @@ -139,7 +139,7 @@ export default {
139 139
140 this.$axios({ 140 this.$axios({
141 method: 'get', 141 method: 'get',
142 - url: "/api/role/all" 142 + url: "./api/role/all"
143 }).then((res) => { 143 }).then((res) => {
144 this.loading = true; 144 this.loading = true;
145 if (res.data.code === 0) { 145 if (res.data.code === 0) {
web_src/src/components/dialog/catalogEdit.vue
@@ -116,7 +116,7 @@ export default { @@ -116,7 +116,7 @@ export default {
116 console.log(this.form); 116 console.log(this.form);
117 this.$axios({ 117 this.$axios({
118 method:"post", 118 method:"post",
119 - url:`/api/platform/catalog/${!this.isEdit? "add":"edit"}`, 119 + url:`./api/platform/catalog/${!this.isEdit? "add":"edit"}`,
120 data: this.form 120 data: this.form
121 }).then((res)=> { 121 }).then((res)=> {
122 if (res.data.code === 0) { 122 if (res.data.code === 0) {
web_src/src/components/dialog/changePassword.vue
@@ -90,7 +90,7 @@ export default { @@ -90,7 +90,7 @@ export default {
90 onSubmit: function () { 90 onSubmit: function () {
91 this.$axios({ 91 this.$axios({
92 method: 'post', 92 method: 'post',
93 - url:"/api/user/changePassword", 93 + url:"./api/user/changePassword",
94 params: { 94 params: {
95 oldPassword: crypto.createHash('md5').update(this.oldPassword, "utf8").digest('hex'), 95 oldPassword: crypto.createHash('md5').update(this.oldPassword, "utf8").digest('hex'),
96 password: this.newPassword 96 password: this.newPassword
web_src/src/components/dialog/changePasswordForAdmin.vue
@@ -85,7 +85,7 @@ export default { @@ -85,7 +85,7 @@ export default {
85 onSubmit: function () { 85 onSubmit: function () {
86 this.$axios({ 86 this.$axios({
87 method: 'post', 87 method: 'post',
88 - url:"/api/user/changePasswordForAdmin", 88 + url:"./api/user/changePasswordForAdmin",
89 params: { 89 params: {
90 password: this.newPassword, 90 password: this.newPassword,
91 userId: this.form.id, 91 userId: this.form.id,
web_src/src/components/dialog/changePushKey.vue
@@ -65,7 +65,7 @@ export default { @@ -65,7 +65,7 @@ export default {
65 onSubmit: function () { 65 onSubmit: function () {
66 this.$axios({ 66 this.$axios({
67 method: 'post', 67 method: 'post',
68 - url:"/api/user/changePushKey", 68 + url:"./api/user/changePushKey",
69 params: { 69 params: {
70 pushKey: this.newPushKey, 70 pushKey: this.newPushKey,
71 userId: this.form.id, 71 userId: this.form.id,