Commit 197f80581b63e0ae116bd06243894e744d522d6c

Authored by 648540858
2 parents ec95a429 f2c62105

Merge branch 'main' into main2

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
Showing 95 changed files with 2221 additions and 1087 deletions
README.md
... ... @@ -98,6 +98,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
98 98 - [X] 支持推流鉴权
99 99 - [X] 支持接口鉴权
100 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 77 **编译完成一般是这个样子,中间没有报红的错误信息**
78 78 ![编译成功](_media/img.png)
79 79  
80   -### 5.3 打包项目, 生成可执行jar
  80 +### 5.3 生成可执行jar
81 81 ```bash
82 82 cd wvp-GB28181-pro
83 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 92 接下来[配置服务](./_content/introduction/config.md)
88 93  
89 94  
... ...
doc/_content/introduction/config.md
... ... @@ -31,6 +31,7 @@ java -jar wvp-pro-*.jar
31 31 ## 2 配置WVP-PRO
32 32 ### 2.1 Mysql数据库配置
33 33 首先你需要创建一个名为wvp(也可使用其他名字)的数据库,并使用sql/mysql.sql导入数据库,初始化数据库结构。
  34 +(这里注意,取决于版本,新版的sql文件夹下有update.sql,补丁包,一定要注意运行导入)
34 35 在application-dev.yml中配置(使用1.2方式的是在jar包的同级目录的application.yml)配置数据库连接,包括数据库连接信息,密码。
35 36 ### 2.2 Redis数据库配置
36 37 配置wvp中的redis连接信息,建议wvp自己单独使用一个db。
... ... @@ -116,4 +117,4 @@ user-settings:
116 117  
117 118  
118 119 如果配置信息无误,你可以启动zlm,再启动wvp来测试了,启动成功的话,你可以在wvp的日志下看到zlm已连接的提示。
119   -接下来[部署到服务器](./_content/introduction/deployment.md), 如何你只是本地运行直接再本地运行即可。
120 120 \ No newline at end of file
  121 +接下来[部署到服务器](./_content/introduction/deployment.md), 如何你只是本地运行直接再本地运行即可。
... ...
doc/_content/introduction/deployment.md
... ... @@ -27,7 +27,9 @@
27 27 ```shell
28 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 33 **启动ZLM**
32 34 ```shell
33 35 nohup ./MediaServer -d -m 3 &
... ...
... ... @@ -14,6 +14,7 @@
14 14 <version>2.6.7</version>
15 15 <name>web video platform</name>
16 16 <description>国标28181视频平台</description>
  17 + <packaging>${project.packaging}</packaging>
17 18  
18 19 <repositories>
19 20 <repository>
... ... @@ -56,6 +57,42 @@
56 57 <asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>
57 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 96 <dependencies>
60 97 <dependency>
61 98 <groupId>org.springframework.boot</groupId>
... ... @@ -242,8 +279,8 @@
242 279 <artifactId>spring-boot-starter-test</artifactId>
243 280 <!-- <scope>test</scope>-->
244 281 </dependency>
245   - </dependencies>
246 282  
  283 + </dependencies>
247 284  
248 285  
249 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 0 \ No newline at end of file
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
1 1 package com.genersoft.iot.vmp;
2 2  
3   -import java.util.logging.LogManager;
4   -
5 3 import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport;
6   -import com.genersoft.iot.vmp.storager.impl.RedisCatchStorageImpl;
7 4 import com.genersoft.iot.vmp.utils.GitUtil;
8 5 import com.genersoft.iot.vmp.utils.SpringBeanFactory;
9 6 import org.slf4j.Logger;
10 7 import org.slf4j.LoggerFactory;
11   -import org.springframework.beans.factory.annotation.Autowired;
12 8 import org.springframework.boot.SpringApplication;
13 9 import org.springframework.boot.autoconfigure.SpringBootApplication;
  10 +import org.springframework.boot.builder.SpringApplicationBuilder;
14 11 import org.springframework.boot.web.servlet.ServletComponentScan;
  12 +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
15 13 import org.springframework.context.ConfigurableApplicationContext;
16 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 26 @SpringBootApplication
23 27 @EnableScheduling
24 28 @EnableDruidSupport
25   -public class VManageBootstrap extends LogManager {
  29 +public class VManageBootstrap extends SpringBootServletInitializer {
26 30  
27 31 private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class);
28 32  
... ... @@ -41,6 +45,21 @@ public class VManageBootstrap extends LogManager {
41 45 context.close();
42 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 10 import org.slf4j.LoggerFactory;
11 11 import org.springframework.beans.factory.annotation.Autowired;
12 12 import org.springframework.http.HttpStatus;
  13 +import org.springframework.stereotype.Component;
13 14 import org.springframework.util.ObjectUtils;
14 15 import org.springframework.web.filter.OncePerRequestFilter;
15 16  
... ... @@ -23,6 +24,7 @@ import java.io.IOException;
23 24 * @author lin
24 25 */
25 26 @WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true)
  27 +@Component
26 28 public class ApiAccessFilter extends OncePerRequestFilter {
27 29  
28 30 private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class);
... ... @@ -48,7 +50,7 @@ public class ApiAccessFilter extends OncePerRequestFilter {
48 50  
49 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 55 LogDto logDto = new LogDto();
54 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 2  
3 3 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
4 4 import com.genersoft.iot.vmp.service.IMediaServerService;
5   -import org.apache.catalina.connector.ClientAbortException;
6 5 import org.apache.http.HttpHost;
7 6 import org.apache.http.HttpRequest;
8 7 import org.apache.http.HttpResponse;
... ... @@ -15,11 +14,9 @@ import org.springframework.boot.web.servlet.ServletRegistrationBean;
15 14 import org.springframework.context.annotation.Bean;
16 15 import org.springframework.context.annotation.Configuration;
17 16 import org.springframework.util.ObjectUtils;
18   -import org.springframework.util.StringUtils;
19 17  
20 18 import javax.servlet.ServletException;
21 19 import javax.servlet.http.HttpServletRequest;
22   -import javax.servlet.http.HttpServletResponse;
23 20 import java.io.IOException;
24 21 import java.net.ConnectException;
25 22  
... ... @@ -77,9 +74,7 @@ public class ProxyServletConfig {
77 74 } catch (IOException ioException) {
78 75 if (ioException instanceof ConnectException) {
79 76 logger.error("zlm 连接失败");
80   - } else if (ioException instanceof ClientAbortException) {
81   - logger.error("zlm: 用户已中断连接,代理终止");
82   - } else {
  77 + } else {
83 78 logger.error("zlm 代理失败: ", e);
84 79 }
85 80 } catch (RuntimeException exception){
... ... @@ -195,9 +190,7 @@ public class ProxyServletConfig {
195 190 } catch (IOException ioException) {
196 191 if (ioException instanceof ConnectException) {
197 192 logger.error("录像服务 连接失败");
198   - } else if (ioException instanceof ClientAbortException) {
199   - logger.error("录像服务:用户已中断连接,代理终止");
200   - } else {
  193 + }else {
201 194 logger.error("录像服务 代理失败: ", e);
202 195 }
203 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 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2  
  3 +
3 4 /**
4 5 * 通过redis分发报警消息
5 6 */
... ... @@ -8,12 +9,14 @@ public class AlarmChannelMessage {
8 9 * 国标编号
9 10 */
10 11 private String gbId;
11   -
12 12 /**
13 13 * 报警编号
14 14 */
15 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 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 50 public String getAlarmDescription() {
40 51 return alarmDescription;
41 52 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarmMethod.java
... ... @@ -37,4 +37,18 @@ public enum DeviceAlarmMethod {
37 37 public int getVal() {
38 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 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2  
  3 +
3 4 import java.time.Instant;
4 5 import java.util.List;
5 6  
... ... @@ -20,6 +21,8 @@ public class RecordInfo {
20 21  
21 22 private int sumNum;
22 23  
  24 + private int count;
  25 +
23 26 private Instant lastTime;
24 27  
25 28 private List<RecordItem> recordList;
... ... @@ -79,4 +82,12 @@ public class RecordInfo {
79 82 public void setLastTime(Instant lastTime) {
80 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 1 package com.genersoft.iot.vmp.gb28181.event.record;
2 2  
3 3 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
  4 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
4 5 import org.slf4j.Logger;
5 6 import org.slf4j.LoggerFactory;
  7 +import org.springframework.beans.factory.annotation.Autowired;
6 8 import org.springframework.context.ApplicationListener;
7 9 import org.springframework.stereotype.Component;
8 10  
... ... @@ -20,25 +22,46 @@ public class RecordEndEventListener implements ApplicationListener&lt;RecordEndEven
20 22  
21 23 private final static Logger logger = LoggerFactory.getLogger(RecordEndEventListener.class);
22 24  
  25 + private Map<String, RecordEndEventHandler> handlerMap = new ConcurrentHashMap<>();
23 26 public interface RecordEndEventHandler{
24 27 void handler(RecordInfo recordInfo);
25 28 }
26 29  
27   - private Map<String, RecordEndEventHandler> handlerMap = new ConcurrentHashMap<>();
28   -
29 30 @Override
30 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 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 55 public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) {
42 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 109  
110 110 for (String deviceId : keys) {
111 111 CatalogData catalogData = data.get(deviceId);
112   - if ( catalogData.getLastTime().isBefore(instantBefore5S)) { // 超过五秒收不到消息任务超时, 只更新这一部分数据
  112 + if ( catalogData.getLastTime().isBefore(instantBefore5S)) {
  113 + // 超过五秒收不到消息任务超时, 只更新这一部分数据, 收到数据与声明的总数一致,则重置通道数据,数据不全则只对收到的数据做更新操作
113 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 122 if (catalogData.getTotal() != catalogData.getChannelList().size()) {
116   - String errorMsg = "更新成功,共" + catalogData.getTotal() + "条,已更新" + catalogData.getChannelList().size() + "条";
117   - catalogData.setErrorMsg(errorMsg);
  123 +
118 124 }
119 125 }else if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.ready)) {
120 126 String errorMsg = "同步失败,等待回复超时";
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/session/RecordDataCatch.java
1 1 package com.genersoft.iot.vmp.gb28181.session;
2 2  
3 3 import com.genersoft.iot.vmp.gb28181.bean.*;
  4 +import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEventListener;
4 5 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
5 6 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
6 7 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
... ... @@ -23,14 +24,17 @@ public class RecordDataCatch {
23 24  
24 25 @Autowired
25 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 32 String key = deviceId + sn;
30 33 RecordInfo recordInfo = data.get(key);
31 34 if (recordInfo == null) {
32 35 recordInfo = new RecordInfo();
33 36 recordInfo.setDeviceId(deviceId);
  37 + recordInfo.setChannelId(channelId);
34 38 recordInfo.setSn(sn.trim());
35 39 recordInfo.setSumNum(sumNum);
36 40 recordInfo.setRecordList(Collections.synchronizedList(new ArrayList<>()));
... ... @@ -67,6 +71,7 @@ public class RecordDataCatch {
67 71 msg.setKey(msgKey);
68 72 msg.setData(recordInfo);
69 73 deferredResultHolder.invokeAllResult(msg);
  74 + recordEndEventListener.delEndEventHandler(recordInfo.getDeviceId(),recordInfo.getChannelId());
70 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 4 import com.genersoft.iot.vmp.conf.UserSetting;
5 5 import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
6 6 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
  7 +import com.genersoft.iot.vmp.utils.JsonUtil;
7 8 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
8 9 import gov.nist.javax.sip.message.SIPResponse;
9 10 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -134,7 +135,7 @@ public class VideoStreamSessionManager {
134 135 List<SsrcTransaction> result= new ArrayList<>();
135 136 for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
136 137 String key = (String)ssrcTransactionKeys.get(i);
137   - SsrcTransaction ssrcTransaction = (SsrcTransaction)RedisUtil.get(key);
  138 + SsrcTransaction ssrcTransaction = JsonUtil.redisJsonToObject(key, SsrcTransaction.class);
138 139 result.add(ssrcTransaction);
139 140 }
140 141 return result;
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java
... ... @@ -47,61 +47,65 @@ public class SIPSender {
47 47 }
48 48  
49 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 182 * @param channelId 预览通道
183 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 196 *
197 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 205 * @param alarmMethod 报警方式(可选)
206 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 211 * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
... ... @@ -214,17 +214,19 @@ public interface ISIPCommander {
214 214 * @param channelId 预览通道
215 215 */
216 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
... ... @@ -69,12 +69,11 @@ public interface ISIPCommanderForPlatform {
69 69 * 向上级回复DeviceInfo查询信息
70 70 *
71 71 * @param parentPlatform 平台信息
72   - * @param sn
73   - * @param fromTag
  72 + * @param sn SN
  73 + * @param fromTag FROM头的tag信息
74 74 * @return
75 75 */
76   - void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag)
77   - throws SipException, InvalidArgumentException, ParseException;
  76 + void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException;
78 77  
79 78 /**
80 79 * 向上级回复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 32 import org.springframework.context.annotation.DependsOn;
33 33 import org.springframework.stereotype.Component;
34 34 import org.springframework.util.ObjectUtils;
  35 +import org.springframework.util.StringUtils;
35 36  
36 37 import javax.sip.InvalidArgumentException;
37 38 import javax.sip.ResponseEvent;
... ... @@ -711,7 +712,7 @@ public class SIPCommander implements ISIPCommander {
711 712 * @param recordCmdStr 录像命令:Record / StopRecord
712 713 */
713 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 716 StringBuffer cmdXml = new StringBuffer(200);
716 717 String charset = device.getCharset();
717 718 cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
... ... @@ -729,7 +730,7 @@ public class SIPCommander implements ISIPCommander {
729 730  
730 731  
731 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 764 * @param guardCmdStr "SetGuard"/"ResetGuard"
764 765 */
765 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 769 StringBuffer cmdXml = new StringBuffer(200);
769 770 String charset = device.getCharset();
... ... @@ -778,7 +779,7 @@ public class SIPCommander implements ISIPCommander {
778 779  
779 780  
780 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 788 * @param device 视频设备
788 789 */
789 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 793 StringBuffer cmdXml = new StringBuffer(200);
793 794 String charset = device.getCharset();
... ... @@ -814,7 +815,7 @@ public class SIPCommander implements ISIPCommander {
814 815  
815 816  
816 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 851 * 看守位控制命令
851 852 *
852 853 * @param device 视频设备
  854 + * @param channelId 通道id,非通道则是设备本身
  855 + * @param frontCmd 上级平台的指令,如果存在则直接下发
853 856 * @param enabled 看守位使能:1 = 开启,0 = 关闭
854 857 * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
855 858 * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
856 859 */
857 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 863 StringBuffer cmdXml = new StringBuffer(200);
861 864 String charset = device.getCharset();
... ... @@ -890,7 +893,7 @@ public class SIPCommander implements ISIPCommander {
890 893  
891 894  
892 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
... ... @@ -81,6 +81,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
81 81 @Autowired
82 82 private VideoStreamSessionManager streamSession;
83 83  
  84 + @Autowired
  85 + private DynamicTask dynamicTask;
  86 +
84 87 @Override
85 88 public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
86 89 register(parentPlatform, null, null, errorEvent, okEvent, false, true);
... ... @@ -129,13 +132,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
129 132 public String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException {
130 133 String characterSet = parentPlatform.getCharacterSet();
131 134 StringBuffer keepaliveXml = new StringBuffer(200);
132   - keepaliveXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
133   - keepaliveXml.append("<Notify>\r\n");
134   - keepaliveXml.append("<CmdType>Keepalive</CmdType>\r\n");
135   - keepaliveXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
136   - keepaliveXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
137   - keepaliveXml.append("<Status>OK</Status>\r\n");
138   - keepaliveXml.append("</Notify>\r\n");
  135 + keepaliveXml.append("<?xml version=\"1.0\" encoding=\"")
  136 + .append(characterSet).append("\"?>\r\n")
  137 + .append("<Notify>\r\n")
  138 + .append("<CmdType>Keepalive</CmdType>\r\n")
  139 + .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
  140 + .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
  141 + .append("<Status>OK</Status>\r\n")
  142 + .append("</Notify>\r\n");
139 143  
140 144 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
141 145  
... ... @@ -153,7 +157,6 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
153 157 * 向上级回复通道信息
154 158 * @param channel 通道信息
155 159 * @param parentPlatform 平台信息
156   - * @return
157 160 */
158 161 @Override
159 162 public void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException {
... ... @@ -180,18 +183,18 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
180 183 if ( parentPlatform ==null) {
181 184 return ;
182 185 }
183   - sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0);
  186 + sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0, true);
184 187 }
185 188 private String getCatalogXml(List<DeviceChannel> channels, String sn, ParentPlatform parentPlatform, int size) {
186 189 String characterSet = parentPlatform.getCharacterSet();
187 190 StringBuffer catalogXml = new StringBuffer(600);
188   - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet +"\"?>\r\n");
189   - catalogXml.append("<Response>\r\n");
190   - catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
191   - catalogXml.append("<SN>" +sn + "</SN>\r\n");
192   - catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
193   - catalogXml.append("<SumNum>" + size + "</SumNum>\r\n");
194   - catalogXml.append("<DeviceList Num=\"" + channels.size() +"\">\r\n");
  191 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet +"\"?>\r\n")
  192 + .append("<Response>\r\n")
  193 + .append("<CmdType>Catalog</CmdType>\r\n")
  194 + .append("<SN>" +sn + "</SN>\r\n")
  195 + .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
  196 + .append("<SumNum>" + size + "</SumNum>\r\n")
  197 + .append("<DeviceList Num=\"" + channels.size() +"\">\r\n");
195 198 if (channels.size() > 0) {
196 199 for (DeviceChannel channel : channels) {
197 200 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
... ... @@ -242,7 +245,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
242 245 return catalogXml.toString();
243 246 }
244 247  
245   - private void sendCatalogResponse(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag, int index) throws SipException, InvalidArgumentException, ParseException {
  248 + private void sendCatalogResponse(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag, int index, boolean sendAfterResponse) throws SipException, InvalidArgumentException, ParseException {
246 249 if (index >= channels.size()) {
247 250 return;
248 251 }
... ... @@ -256,15 +259,49 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
256 259 // callid
257 260 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
258 261  
259   - Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);
260   - sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, eventResult -> {
261   - int indexNext = index + parentPlatform.getCatalogGroup();
262   - try {
263   - sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext);
264   - } catch (SipException | InvalidArgumentException | ParseException e) {
265   - logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
266   - }
267   - });
  262 + SIPRequest request = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);
  263 +
  264 + String timeoutTaskKey = "catalog_task_" + parentPlatform.getServerGBId() + sn;
  265 +
  266 + String callId = request.getCallIdHeader().getCallId();
  267 +
  268 + if (sendAfterResponse) {
  269 + // 默认按照收到200回复后发送下一条, 如果超时收不到回复,就以30毫秒的间隔直接发送。
  270 + dynamicTask.startDelay(timeoutTaskKey, ()->{
  271 + sipSubscribe.removeOkSubscribe(callId);
  272 + int indexNext = index + parentPlatform.getCatalogGroup();
  273 + try {
  274 + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false);
  275 + } catch (SipException | InvalidArgumentException | ParseException e) {
  276 + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
  277 + }
  278 + }, 3000);
  279 + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> {
  280 + logger.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg);
  281 + dynamicTask.stop(timeoutTaskKey);
  282 + }, eventResult -> {
  283 + dynamicTask.stop(timeoutTaskKey);
  284 + int indexNext = index + parentPlatform.getCatalogGroup();
  285 + try {
  286 + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, true);
  287 + } catch (SipException | InvalidArgumentException | ParseException e) {
  288 + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
  289 + }
  290 + });
  291 + }else {
  292 + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> {
  293 + logger.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg);
  294 + dynamicTask.stop(timeoutTaskKey);
  295 + }, null);
  296 + dynamicTask.startDelay(timeoutTaskKey, ()->{
  297 + int indexNext = index + parentPlatform.getCatalogGroup();
  298 + try {
  299 + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false);
  300 + } catch (SipException | InvalidArgumentException | ParseException e) {
  301 + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
  302 + }
  303 + }, 30);
  304 + }
268 305 }
269 306  
270 307 /**
... ... @@ -275,7 +312,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
275 312 * @return
276 313 */
277 314 @Override
278   - public void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException {
  315 + public void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException {
279 316 if (parentPlatform == null) {
280 317 return;
281 318 }
... ... @@ -285,11 +322,11 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
285 322 deviceInfoXml.append("<Response>\r\n");
286 323 deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
287 324 deviceInfoXml.append("<SN>" +sn + "</SN>\r\n");
288   - deviceInfoXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
289   - deviceInfoXml.append("<DeviceName>" + parentPlatform.getName() + "</DeviceName>\r\n");
290   - deviceInfoXml.append("<Manufacturer>wvp</Manufacturer>\r\n");
291   - deviceInfoXml.append("<Model>wvp-28181-2.0</Model>\r\n");
292   - deviceInfoXml.append("<Firmware>2.0.202107</Firmware>\r\n");
  325 + deviceInfoXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  326 + deviceInfoXml.append("<DeviceName>" + device.getName() + "</DeviceName>\r\n");
  327 + deviceInfoXml.append("<Manufacturer>" + device.getManufacturer() + "</Manufacturer>\r\n");
  328 + deviceInfoXml.append("<Model>" + device.getModel() + "</Model>\r\n");
  329 + deviceInfoXml.append("<Firmware>" + device.getFirmware() + "</Firmware>\r\n");
293 330 deviceInfoXml.append("<Result>OK</Result>\r\n");
294 331 deviceInfoXml.append("</Response>\r\n");
295 332  
... ... @@ -314,15 +351,15 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
314 351 String statusStr = (status==1)?"ONLINE":"OFFLINE";
315 352 String characterSet = parentPlatform.getCharacterSet();
316 353 StringBuffer deviceStatusXml = new StringBuffer(600);
317   - deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
318   - deviceStatusXml.append("<Response>\r\n");
319   - deviceStatusXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
320   - deviceStatusXml.append("<SN>" +sn + "</SN>\r\n");
321   - deviceStatusXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
322   - deviceStatusXml.append("<Result>OK</Result>\r\n");
323   - deviceStatusXml.append("<Online>"+statusStr+"</Online>\r\n");
324   - deviceStatusXml.append("<Status>OK</Status>\r\n");
325   - deviceStatusXml.append("</Response>\r\n");
  354 + deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  355 + .append("<Response>\r\n")
  356 + .append("<CmdType>DeviceStatus</CmdType>\r\n")
  357 + .append("<SN>" +sn + "</SN>\r\n")
  358 + .append("<DeviceID>" + channelId + "</DeviceID>\r\n")
  359 + .append("<Result>OK</Result>\r\n")
  360 + .append("<Online>"+statusStr+"</Online>\r\n")
  361 + .append("<Status>OK</Status>\r\n")
  362 + .append("</Response>\r\n");
326 363  
327 364 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
328 365  
... ... @@ -341,18 +378,18 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
341 378  
342 379 String characterSet = parentPlatform.getCharacterSet();
343 380 StringBuffer deviceStatusXml = new StringBuffer(600);
344   - deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
345   - deviceStatusXml.append("<Notify>\r\n");
346   - deviceStatusXml.append("<CmdType>MobilePosition</CmdType>\r\n");
347   - deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
348   - deviceStatusXml.append("<DeviceID>" + gpsMsgInfo.getId() + "</DeviceID>\r\n");
349   - deviceStatusXml.append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n");
350   - deviceStatusXml.append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n");
351   - deviceStatusXml.append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n");
352   - deviceStatusXml.append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n");
353   - deviceStatusXml.append("<Direction>" + gpsMsgInfo.getDirection() + "</Direction>\r\n");
354   - deviceStatusXml.append("<Altitude>" + gpsMsgInfo.getAltitude() + "</Altitude>\r\n");
355   - deviceStatusXml.append("</Notify>\r\n");
  381 + deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  382 + .append("<Notify>\r\n")
  383 + .append("<CmdType>MobilePosition</CmdType>\r\n")
  384 + .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
  385 + .append("<DeviceID>" + gpsMsgInfo.getId() + "</DeviceID>\r\n")
  386 + .append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n")
  387 + .append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n")
  388 + .append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n")
  389 + .append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n")
  390 + .append("<Direction>" + gpsMsgInfo.getDirection() + "</Direction>\r\n")
  391 + .append("<Altitude>" + gpsMsgInfo.getAltitude() + "</Altitude>\r\n")
  392 + .append("</Notify>\r\n");
356 393  
357 394 sendNotify(parentPlatform, deviceStatusXml.toString(), subscribeInfo, eventResult -> {
358 395 logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg);
... ... @@ -369,21 +406,21 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
369 406 deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm));
370 407 String characterSet = parentPlatform.getCharacterSet();
371 408 StringBuffer deviceStatusXml = new StringBuffer(600);
372   - deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
373   - deviceStatusXml.append("<Notify>\r\n");
374   - deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");
375   - deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
376   - deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
377   - deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
378   - deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
379   - deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
380   - deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
381   - deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
382   - deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
383   - deviceStatusXml.append("<info>\r\n");
384   - deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");
385   - deviceStatusXml.append("</info>\r\n");
386   - deviceStatusXml.append("</Notify>\r\n");
  409 + deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  410 + .append("<Notify>\r\n")
  411 + .append("<CmdType>Alarm</CmdType>\r\n")
  412 + .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
  413 + .append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n")
  414 + .append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n")
  415 + .append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n")
  416 + .append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n")
  417 + .append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n")
  418 + .append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n")
  419 + .append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n")
  420 + .append("<info>\r\n")
  421 + .append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n")
  422 + .append("</info>\r\n")
  423 + .append("</Notify>\r\n");
387 424  
388 425 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
389 426  
... ... @@ -442,13 +479,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
442 479 StringBuffer catalogXml = new StringBuffer(600);
443 480  
444 481 String characterSet = parentPlatform.getCharacterSet();
445   - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
446   - catalogXml.append("<Notify>\r\n");
447   - catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
448   - catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
449   - catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
450   - catalogXml.append("<SumNum>1</SumNum>\r\n");
451   - catalogXml.append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
  482 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  483 + .append("<Notify>\r\n")
  484 + .append("<CmdType>Catalog</CmdType>\r\n")
  485 + .append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
  486 + .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
  487 + .append("<SumNum>1</SumNum>\r\n")
  488 + .append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
452 489 if (channels.size() > 0) {
453 490 for (DeviceChannel channel : channels) {
454 491 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
... ... @@ -469,16 +506,16 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
469 506 catalogXml.append("<Parental>" + channel.getParental() + "</Parental>\r\n");
470 507 if (channel.getParental() == 0) {
471 508 // 通道项
472   - catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n");
473   - catalogXml.append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n");
474   - catalogXml.append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n");
475   - catalogXml.append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n");
  509 + catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n")
  510 + .append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n")
  511 + .append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n")
  512 + .append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n");
476 513  
477 514 if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性
478   - catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n");
479   - catalogXml.append("<Owner> " + channel.getOwner()+ "</Owner>\r\n");
480   - catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n");
481   - catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
  515 + catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n")
  516 + .append("<Owner> " + channel.getOwner()+ "</Owner>\r\n")
  517 + .append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n")
  518 + .append("<Address>" + channel.getAddress() + "</Address>\r\n");
482 519 }
483 520 if (!"presence".equals(subscribeInfo.getEventType())) {
484 521 catalogXml.append("<Event>" + type + "</Event>\r\n");
... ... @@ -488,8 +525,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
488 525 catalogXml.append("</Item>\r\n");
489 526 }
490 527 }
491   - catalogXml.append("</DeviceList>\r\n");
492   - catalogXml.append("</Notify>\r\n");
  528 + catalogXml.append("</DeviceList>\r\n")
  529 + .append("</Notify>\r\n");
493 530 return catalogXml.toString();
494 531 }
495 532  
... ... @@ -535,26 +572,26 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
535 572  
536 573 String characterSet = parentPlatform.getCharacterSet();
537 574 StringBuffer catalogXml = new StringBuffer(600);
538   - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
539   - catalogXml.append("<Notify>\r\n");
540   - catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
541   - catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
542   - catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
543   - catalogXml.append("<SumNum>1</SumNum>\r\n");
544   - catalogXml.append("<DeviceList Num=\" " + channels.size() + " \">\r\n");
  575 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  576 + .append("<Notify>\r\n")
  577 + .append("<CmdType>Catalog</CmdType>\r\n")
  578 + .append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
  579 + .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
  580 + .append("<SumNum>1</SumNum>\r\n")
  581 + .append("<DeviceList Num=\" " + channels.size() + " \">\r\n");
545 582 if (channels.size() > 0) {
546 583 for (DeviceChannel channel : channels) {
547 584 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
548 585 channel.setParentId(parentPlatform.getDeviceGBId());
549 586 }
550   - catalogXml.append("<Item>\r\n");
551   - catalogXml.append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n");
552   - catalogXml.append("<Event>" + type + "</Event>\r\n");
553   - catalogXml.append("</Item>\r\n");
  587 + catalogXml.append("<Item>\r\n")
  588 + .append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n")
  589 + .append("<Event>" + type + "</Event>\r\n")
  590 + .append("</Item>\r\n");
554 591 }
555 592 }
556   - catalogXml.append("</DeviceList>\r\n");
557   - catalogXml.append("</Notify>\r\n");
  593 + catalogXml.append("</DeviceList>\r\n")
  594 + .append("</Notify>\r\n");
558 595 return catalogXml.toString();
559 596 }
560 597 @Override
... ... @@ -564,12 +601,12 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
564 601 }
565 602 String characterSet = parentPlatform.getCharacterSet();
566 603 StringBuffer recordXml = new StringBuffer(600);
567   - recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
568   - recordXml.append("<Response>\r\n");
569   - recordXml.append("<CmdType>RecordInfo</CmdType>\r\n");
570   - recordXml.append("<SN>" +recordInfo.getSn() + "</SN>\r\n");
571   - recordXml.append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n");
572   - recordXml.append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
  604 + recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  605 + .append("<Response>\r\n")
  606 + .append("<CmdType>RecordInfo</CmdType>\r\n")
  607 + .append("<SN>" +recordInfo.getSn() + "</SN>\r\n")
  608 + .append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n")
  609 + .append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
573 610 if (recordInfo.getRecordList() == null ) {
574 611 recordXml.append("<RecordList Num=\"0\">\r\n");
575 612 }else {
... ... @@ -578,12 +615,12 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
578 615 for (RecordItem recordItem : recordInfo.getRecordList()) {
579 616 recordXml.append("<Item>\r\n");
580 617 if (deviceChannel != null) {
581   - recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n");
582   - recordXml.append("<Name>" + recordItem.getName() + "</Name>\r\n");
583   - recordXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n");
584   - recordXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n");
585   - recordXml.append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n");
586   - recordXml.append("<Type>" + recordItem.getType() + "</Type>\r\n");
  618 + recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n")
  619 + .append("<Name>" + recordItem.getName() + "</Name>\r\n")
  620 + .append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n")
  621 + .append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n")
  622 + .append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n")
  623 + .append("<Type>" + recordItem.getType() + "</Type>\r\n");
587 624 if (!ObjectUtils.isEmpty(recordItem.getFileSize())) {
588 625 recordXml.append("<FileSize>" + recordItem.getFileSize() + "</FileSize>\r\n");
589 626 }
... ... @@ -596,8 +633,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
596 633 }
597 634 }
598 635  
599   - recordXml.append("</RecordList>\r\n");
600   - recordXml.append("</Response>\r\n");
  636 + recordXml.append("</RecordList>\r\n")
  637 + .append("</Response>\r\n");
601 638  
602 639 // callid
603 640 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
... ... @@ -616,13 +653,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
616 653  
617 654 String characterSet = parentPlatform.getCharacterSet();
618 655 StringBuffer mediaStatusXml = new StringBuffer(200);
619   - mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
620   - mediaStatusXml.append("<Notify>\r\n");
621   - mediaStatusXml.append("<CmdType>MediaStatus</CmdType>\r\n");
622   - mediaStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
623   - mediaStatusXml.append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n");
624   - mediaStatusXml.append("<NotifyType>121</NotifyType>\r\n");
625   - mediaStatusXml.append("</Notify>\r\n");
  656 + mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  657 + .append("<Notify>\r\n")
  658 + .append("<CmdType>MediaStatus</CmdType>\r\n")
  659 + .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
  660 + .append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n")
  661 + .append("<NotifyType>121</NotifyType>\r\n")
  662 + .append("</Notify>\r\n");
626 663  
627 664 SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, mediaStatusXml.toString(),
628 665 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 16  
17 17 import javax.sip.*;
18 18 import javax.sip.address.Address;
19   -import javax.sip.address.AddressFactory;
20 19 import javax.sip.address.SipURI;
21 20 import javax.sip.header.ContentTypeHeader;
22 21 import javax.sip.header.ExpiresHeader;
... ... @@ -42,15 +41,6 @@ public abstract class SIPRequestProcessorParent {
42 41 @Autowired
43 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 44 public HeaderFactory getHeaderFactory() {
55 45 try {
56 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 275 }
276 276 return;
277 277 } else {
278   - logger.info("通道不存在,返回404");
  278 + logger.info("通道不存在,返回404: {}", channelId);
279 279 try {
280 280 // 通道不存在,发404,资源不存在
281 281 responseAck(request, Response.NOT_FOUND);
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
2 2  
  3 +import com.genersoft.iot.vmp.conf.ServiceInfo;
3 4 import com.genersoft.iot.vmp.conf.SipConfig;
4 5 import com.genersoft.iot.vmp.conf.UserSetting;
5 6 import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
... ... @@ -82,6 +83,19 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
82 83 RequestEventExt evtExt = (RequestEventExt) evt;
83 84 String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort();
84 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 99 SIPRequest request = (SIPRequest)evt.getRequest();
86 100 Response response = null;
87 101 boolean passwordCorrect = false;
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
1 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 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 7 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  8 +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
6 9 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
7 10 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
8 11 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
... ... @@ -19,17 +22,14 @@ import org.springframework.beans.factory.annotation.Qualifier;
19 22 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
20 23 import org.springframework.stereotype.Component;
21 24 import org.springframework.util.ObjectUtils;
22   -import org.springframework.util.StringUtils;
23 25  
24 26 import javax.sip.*;
25 27 import javax.sip.address.SipURI;
26   -import javax.sip.header.HeaderAddress;
27   -import javax.sip.header.ToHeader;
28 28 import javax.sip.message.Response;
29 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 34 @Component
35 35 public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
... ... @@ -81,7 +81,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
81 81 } catch (InvalidArgumentException | ParseException | SipException e) {
82 82 logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
83 83 }
84   - taskExecutor.execute(()->{
  84 + taskExecutor.execute(() -> {
85 85 // 远程启动
86 86 // try {
87 87 // Thread.sleep(3000);
... ... @@ -101,13 +101,12 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
101 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 110 Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
112 111 if (deviceForPlatform == null) {
113 112 try {
... ... @@ -117,25 +116,240 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
117 116 }
118 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 181 }
182 182 }
183 183 logger.info("[收到报警通知]内容:{}", JSON.toJSONString(deviceAlarm));
184   - if ("7".equals(deviceAlarm.getAlarmMethod()) ) {
  184 + // 作者自用判断,其他小伙伴需要此消息可以自行修改,但是不要提在pr里
  185 + if (DeviceAlarmMethod.Other.getVal() == Integer.parseInt(deviceAlarm.getAlarmMethod())) {
185 186 // 发送给平台的报警信息。 发送redis通知
  187 + logger.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm));
186 188 AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
187 189 alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
188 190 alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
  191 + alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
189 192 alarmChannelMessage.setGbId(channelId);
190 193 redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
191 194 continue;
... ... @@ -264,6 +267,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
264 267 alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
265 268 alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
266 269 alarmChannelMessage.setGbId(channelId);
  270 + alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
267 271 redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
268 272 return;
269 273 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd;
2 2  
3 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 7 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
6   -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
7 8 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
8 9 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
9 10 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
... ... @@ -63,7 +64,6 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem
63 64 @Override
64 65 public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
65 66  
66   - String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + parentPlatform.getServerGBId();
67 67 FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
68 68 try {
69 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 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
7 7 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
8 8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
  9 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
9 10 import gov.nist.javax.sip.message.SIPRequest;
10 11 import org.dom4j.Element;
11 12 import org.slf4j.Logger;
... ... @@ -21,6 +22,8 @@ import javax.sip.header.FromHeader;
21 22 import javax.sip.message.Response;
22 23 import java.text.ParseException;
23 24  
  25 +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
  26 +
24 27 @Component
25 28 public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
26 29  
... ... @@ -32,6 +35,8 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp
32 35  
33 36 @Autowired
34 37 private SIPCommanderFroPlatform cmderFroPlatform;
  38 + @Autowired
  39 + private IVideoManagerStorage storager;
35 40  
36 41 @Override
37 42 public void afterPropertiesSet() throws Exception {
... ... @@ -52,10 +57,20 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp
52 57 responseAck((SIPRequest) evt.getRequest(), Response.OK);
53 58 } catch (SipException | InvalidArgumentException | ParseException e) {
54 59 logger.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage());
  60 + return;
55 61 }
56 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 72 try {
58   - cmderFroPlatform.deviceInfoResponse(parentPlatform, sn, fromHeader.getTag());
  73 + cmderFroPlatform.deviceInfoResponse(parentPlatform,device, sn, fromHeader.getTag());
59 74 } catch (SipException | InvalidArgumentException | ParseException e) {
60 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 102 Element recordListElement = rootElementForCharset.element("RecordList");
103 103 if (recordListElement == null || sumNum == 0) {
104 104 logger.info("无录像数据");
  105 + int count = recordDataCatch.put(take.getDevice().getDeviceId(),channelId, sn, sumNum, new ArrayList<>());
  106 + recordInfo.setCount(count);
105 107 eventPublisher.recordEndEventPush(recordInfo);
106   - recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, new ArrayList<>());
107 108 releaseRequest(take.getDevice().getDeviceId(), sn);
108 109 } else {
109 110 Iterator<Element> recordListIterator = recordListElement.elementIterator();
... ... @@ -137,12 +138,11 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
137 138 recordList.add(record);
138 139 }
139 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 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 146 if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){
147 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 1 package com.genersoft.iot.vmp.gb28181.utils;
2 2  
  3 +import com.alibaba.fastjson2.JSON;
3 4 import com.alibaba.fastjson2.JSONArray;
4 5 import com.alibaba.fastjson2.JSONObject;
5 6 import com.genersoft.iot.vmp.gb28181.bean.Device;
... ... @@ -15,12 +16,16 @@ import org.dom4j.io.SAXReader;
15 16 import org.slf4j.Logger;
16 17 import org.slf4j.LoggerFactory;
17 18 import org.springframework.util.ObjectUtils;
18   -import org.springframework.util.StringUtils;
  19 +import org.springframework.util.ReflectionUtils;
19 20  
20 21 import javax.sip.RequestEvent;
21 22 import javax.sip.message.Request;
22 23 import java.io.ByteArrayInputStream;
23 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 29 import java.util.*;
25 30  
26 31 /**
... ... @@ -411,4 +416,76 @@ public class XmlUtil {
411 416 }
412 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 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 10 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
11 11 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
12 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 15 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
14 16 import com.genersoft.iot.vmp.media.zlm.dto.HookType;
15 17 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
... ... @@ -19,7 +21,10 @@ import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
19 21 import com.genersoft.iot.vmp.service.*;
20 22 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
21 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 26 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
  27 +import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
23 28 import org.slf4j.Logger;
24 29 import org.slf4j.LoggerFactory;
25 30 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -27,6 +32,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
27 32 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
28 33 import org.springframework.util.ObjectUtils;
29 34 import org.springframework.web.bind.annotation.*;
  35 +import org.springframework.web.context.request.async.DeferredResult;
30 36  
31 37 import javax.servlet.http.HttpServletRequest;
32 38 import javax.sip.InvalidArgumentException;
... ... @@ -35,20 +41,21 @@ import java.text.ParseException;
35 41 import java.util.HashMap;
36 42 import java.util.List;
37 43 import java.util.Map;
  44 +import java.util.UUID;
38 45  
39   -/**
  46 +/**
40 47 * @description:针对 ZLMediaServer的hook事件监听
41 48 * @author: swwheihei
42   - * @date: 2020年5月8日 上午10:46:48
  49 + * @date: 2020年5月8日 上午10:46:48
43 50 */
44 51 @RestController
45 52 @RequestMapping("/index/hook")
46 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 60 @Autowired
54 61 private ISIPCommanderForPlatform commanderFroPlatform;
... ... @@ -59,269 +66,246 @@ public class ZLMHttpHookListener {
59 66 @Autowired
60 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());
  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 + }
131 276  
132 277 JSONObject ret = new JSONObject();
133 278 ret.put("code", 0);
134 279 ret.put("msg", "success");
135 280  
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   - JSONObject ret = new JSONObject();
292   - ret.put("code", 0);
293   - ret.put("msg", "success");
294   -
295   - JSONObject json = (JSONObject) JSON.toJSON(param);
296   - MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
297   - if (mediaInfo == null) {
298   - return ret;
299   - }
300   - taskExecutor.execute(()-> {
301   - ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
302   - if (subscribe != null ) {
303   - subscribe.response(mediaInfo, json);
304   - }
305   - // 流消失移除redis play
306   - List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
307   - if (param.isRegist()) {
308   - if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
309   - || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
310   - || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
311   -
312   - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
313   - if (streamAuthorityInfo == null) {
314   - streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
315   - } else {
316   - streamAuthorityInfo.setOriginType(param.getOriginType());
317   - streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
318   - }
319   - redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
320   - }
321   - } else {
322   - redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
323   - }
324   - });
  281 + JSONObject json = (JSONObject) JSON.toJSON(param);
  282 + taskExecutor.execute(() -> {
  283 + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
  284 + if (subscribe != null) {
  285 + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
  286 + if (mediaInfo != null) {
  287 + subscribe.response(mediaInfo, json);
  288 + }
  289 + }
  290 + // 流消失移除redis play
  291 + List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
  292 + if (param.isRegist()) {
  293 + if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
  294 + || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
  295 + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
  296 +
  297 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
  298 + if (streamAuthorityInfo == null) {
  299 + streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
  300 + } else {
  301 + streamAuthorityInfo.setOriginType(param.getOriginType());
  302 + streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
  303 + }
  304 + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
  305 + }
  306 + } else {
  307 + redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
  308 + }
325 309  
326 310 if ("rtsp".equals(param.getSchema())){
327 311 logger.info("流变化:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
... ... @@ -462,70 +446,57 @@ public class ZLMHttpHookListener {
462 446 GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
463 447 if (gbStream != null) {
464 448 // eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
465   - }
466   - zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
467   - }
468   - if (type != null) {
469   - // 发送流变化redis消息
470   - JSONObject jsonObject = new JSONObject();
471   - jsonObject.put("serverId", userSetting.getServerId());
472   - jsonObject.put("app", param.getApp());
473   - jsonObject.put("stream", param.getStream());
474   - jsonObject.put("register", param.isRegist());
475   - jsonObject.put("mediaServerId", param.getMediaServerId());
476   - redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
477   - }
478   - }
479   - }
480   - }
481   - if (!param.isRegist()) {
482   - List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
483   - if (sendRtpItems.size() > 0) {
484   - for (SendRtpItem sendRtpItem : sendRtpItems) {
485   - if (sendRtpItem.getApp().equals(param.getApp())) {
486   - String platformId = sendRtpItem.getPlatformId();
487   - ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
488   - Device device = deviceService.getDevice(platformId);
489   -
490   - try {
491   - if (platform != null) {
492   - commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
493   - }else {
494   - if (sendRtpItem.isOnlyAudio()) {
495   - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
496   - if (audioBroadcastCatch != null) {
497   -// playService.stopAudioBroadcast(device.getDeviceId(), sendRtpItem.getChannelId());
498   - if ("talk".equals(param.getApp())) {
499   -// cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
500   - }else {
501   -// cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
502   - }
503   - }
504   - }
505   -
506   -
507   -
508   - }
509   - } catch (SipException | InvalidArgumentException | ParseException e) {
510   - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
511   - }
512   - }
513   - }
514   - }
515   - }
516   - }
517   -
518   -
519   - return ret;
520   - }
521   -
522   - /**
523   - * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。
524   - *
525   - */
526   - @ResponseBody
527   - @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
528   - public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param){
  449 + }
  450 + zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
  451 + }
  452 + if (type != null) {
  453 + // 发送流变化redis消息
  454 + JSONObject jsonObject = new JSONObject();
  455 + jsonObject.put("serverId", userSetting.getServerId());
  456 + jsonObject.put("app", param.getApp());
  457 + jsonObject.put("stream", param.getStream());
  458 + jsonObject.put("register", param.isRegist());
  459 + jsonObject.put("mediaServerId", param.getMediaServerId());
  460 + redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
  461 + }
  462 + }
  463 + }
  464 + }
  465 + if (!param.isRegist()) {
  466 + List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
  467 + if (sendRtpItems.size() > 0) {
  468 + for (SendRtpItem sendRtpItem : sendRtpItems) {
  469 + if (sendRtpItem.getApp().equals(param.getApp())) {
  470 + String platformId = sendRtpItem.getPlatformId();
  471 + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
  472 + Device device = deviceService.getDevice(platformId);
  473 +
  474 + try {
  475 + if (platform != null) {
  476 + commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
  477 + } else {
  478 + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId());
  479 + }
  480 + } catch (SipException | InvalidArgumentException | ParseException |
  481 + SsrcTransactionNotFoundException e) {
  482 + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
  483 + }
  484 + }
  485 + }
  486 + }
  487 + }
  488 + }
  489 + });
  490 +
  491 + return HookResult.SUCCESS();
  492 + }
  493 +
  494 + /**
  495 + * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。
  496 + */
  497 + @ResponseBody
  498 + @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
  499 + public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) {
529 500  
530 501 logger.info("[ZLM HOOK]流无人观看:{]->{}->{}/{}" + param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
531 502 JSONObject ret = new JSONObject();
... ... @@ -566,215 +537,243 @@ public class ZLMHttpHookListener {
566 537 }
567 538 }
568 539  
569   - redisCatchStorage.stopPlay(streamInfoForPlayCatch);
570   - storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
571   - return ret;
572   - }
573   - // 录像回放
574   - StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, param.getStream(), null);
575   - if (streamInfoForPlayBackCatch != null ) {
576   - if (streamInfoForPlayBackCatch.isPause()) {
577   - ret.put("close", false);
578   - }else {
579   - Device device = deviceService.getDevice(streamInfoForPlayBackCatch.getDeviceID());
580   - if (device != null) {
581   - try {
582   - cmder.streamByeCmd(device,streamInfoForPlayBackCatch.getChannelId(),
583   - streamInfoForPlayBackCatch.getStream(), null);
584   - } catch (InvalidArgumentException | ParseException | SipException |
585   - SsrcTransactionNotFoundException e) {
586   - logger.error("[无人观看]回放, 发送BYE失败 {}", e.getMessage());
587   - }
588   - }
589   - redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),
590   - streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
591   - }
592   - return ret;
593   - }
594   - // 录像下载
595   - StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, param.getStream(), null);
596   - // 进行录像下载时无人观看不断流
597   - if (streamInfoForDownload != null) {
598   - ret.put("close", false);
599   - return ret;
600   - }
601   - }else {
602   - // 非国标流 推流/拉流代理
603   - // 拉流代理
604   - StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
605   - if (streamProxyItem != null ) {
606   - if (streamProxyItem.isEnable_remove_none_reader()) {
607   - // 无人观看自动移除
608   - ret.put("close", true);
609   - streamProxyService.del(param.getApp(), param.getStream());
610   - String url = streamProxyItem.getUrl() != null?streamProxyItem.getUrl():streamProxyItem.getSrc_url();
611   - logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url);
612   - }else if (streamProxyItem.isEnable_disable_none_reader()) {
613   - // 无人观看停用
614   - ret.put("close", true);
615   - // 修改数据
616   - streamProxyService.stop(param.getApp(), param.getStream());
617   - }else {
618   - // 无人观看不做处理
619   - ret.put("close", false);
620   - }
621   - return ret;
622   - }
623   - // 推流具有主动性,暂时不做处理
  540 + redisCatchStorage.stopPlay(streamInfoForPlayCatch);
  541 + storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
  542 + return ret;
  543 + }
  544 + // 录像回放
  545 + StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, param.getStream(), null);
  546 + if (streamInfoForPlayBackCatch != null) {
  547 + if (streamInfoForPlayBackCatch.isPause()) {
  548 + ret.put("close", false);
  549 + } else {
  550 + Device device = deviceService.getDevice(streamInfoForPlayBackCatch.getDeviceID());
  551 + if (device != null) {
  552 + try {
  553 + cmder.streamByeCmd(device, streamInfoForPlayBackCatch.getChannelId(),
  554 + streamInfoForPlayBackCatch.getStream(), null);
  555 + } catch (InvalidArgumentException | ParseException | SipException |
  556 + SsrcTransactionNotFoundException e) {
  557 + logger.error("[无人观看]回放, 发送BYE失败 {}", e.getMessage());
  558 + }
  559 + }
  560 + redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),
  561 + streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
  562 + }
  563 + return ret;
  564 + }
  565 + // 录像下载
  566 + StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, param.getStream(), null);
  567 + // 进行录像下载时无人观看不断流
  568 + if (streamInfoForDownload != null) {
  569 + ret.put("close", false);
  570 + return ret;
  571 + }
  572 + } else {
  573 + // 非国标流 推流/拉流代理
  574 + // 拉流代理
  575 + StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
  576 + if (streamProxyItem != null) {
  577 + if (streamProxyItem.isEnable_remove_none_reader()) {
  578 + // 无人观看自动移除
  579 + ret.put("close", true);
  580 + streamProxyService.del(param.getApp(), param.getStream());
  581 + String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrc_url();
  582 + logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url);
  583 + } else if (streamProxyItem.isEnable_disable_none_reader()) {
  584 + // 无人观看停用
  585 + ret.put("close", true);
  586 + // 修改数据
  587 + streamProxyService.stop(param.getApp(), param.getStream());
  588 + } else {
  589 + // 无人观看不做处理
  590 + ret.put("close", false);
  591 + }
  592 + return ret;
  593 + }
  594 + // 推流具有主动性,暂时不做处理
624 595 // StreamPushItem streamPushItem = streamPushService.getPush(app, streamId);
625 596 // if (streamPushItem != null) {
626 597 // // TODO 发送停止
627 598 //
628 599 // }
629   - }
630   - return ret;
631   - }
632   -
633   - /**
634   - * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。
635   - *
636   - */
637   - @ResponseBody
638   - @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
639   - public JSONObject onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param){
640   - logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
641   - taskExecutor.execute(()->{
642   - MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
643   - if (userSetting.isAutoApplyPlay() && mediaInfo != null) {
644   - if ("rtp".equals(param.getApp())) {
645   - if (mediaInfo.isRtpEnable()) {
646   - String[] s = param.getStream().split("_");
647   - if (s.length == 2) {
648   - String deviceId = s[0];
649   - String channelId = s[1];
650   - Device device = redisCatchStorage.getDevice(deviceId);
651   - if (device != null) {
652   - playService.play(mediaInfo,deviceId, channelId, null, null, null);
653   - }
654   - }
655   - }
656   - }else {
657   - // 拉流代理
658   - StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
659   - if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) {
660   - streamProxyService.start(param.getApp(), param.getStream());
661   - }
662   - }
663   - }
664   - });
665   -
666   -
667   - JSONObject ret = new JSONObject();
668   - ret.put("code", 0);
669   - ret.put("msg", "success");
670   - return ret;
671   - }
672   -
673   - /**
674   - * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。
675   - *
676   - */
677   - @ResponseBody
678   - @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
679   - public JSONObject onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject){
680   -
681   - jsonObject.put("ip", request.getRemoteAddr());
682   - ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
683   - zlmServerConfig.setIp(request.getRemoteAddr());
684   - logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
685   - taskExecutor.execute(()->{
686   - List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
687   - if (subscribes != null && subscribes.size() > 0) {
688   - for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
689   - subscribe.response(null, jsonObject);
690   - }
691   - }
692   - mediaServerService.zlmServerOnline(zlmServerConfig);
693   - });
694   -
695   - JSONObject ret = new JSONObject();
696   - ret.put("code", 0);
697   - ret.put("msg", "success");
698   - return ret;
699   - }
700   -
701   - /**
702   - * 发送rtp(startSendRtp)被动关闭时回调
703   - */
704   - @ResponseBody
705   - @PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8")
706   - public JSONObject onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param){
707   -
708   - logger.info("[ZLM HOOK] rtp发送关闭:{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream());
709   -
710   - JSONObject ret = new JSONObject();
711   - ret.put("code", 0);
712   - ret.put("msg", "success");
713   -
714   - // 查找对应的上级推流,发送停止
715   - if (!"rtp".equals(param.getApp())) {
716   - return ret;
717   - }
718   - taskExecutor.execute(()->{
719   - List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
720   - if (sendRtpItems.size() > 0) {
721   - for (SendRtpItem sendRtpItem : sendRtpItems) {
722   - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
723   - try {
724   - commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
725   - } catch (SipException | InvalidArgumentException | ParseException e) {
726   - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
727   - }
728   - redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
729   - sendRtpItem.getCallId(), sendRtpItem.getStreamId());
730   - }
731   - }
732   - });
733   -
734   -
735   - return ret;
736   - }
737   -
738   - /**
739   - * rtpServer收流超时
740   - */
741   - @ResponseBody
742   - @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
743   - public JSONObject onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param){
744   - logger.info("[ZLM HOOK] rtpServer rtp超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
745   -
746   - JSONObject ret = new JSONObject();
747   - ret.put("code", 0);
748   - ret.put("msg", "success");
749   -
750   - taskExecutor.execute(()->{
751   - JSONObject json = (JSONObject) JSON.toJSON(param);
752   - List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
753   - if (subscribes != null && subscribes.size() > 0) {
754   - for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
755   - subscribe.response(null, json);
756   - }
757   - }
758   - });
759   -
760   - return ret;
761   - }
762   -
763   - private Map<String, String> urlParamToMap(String params) {
764   - HashMap<String, String> map = new HashMap<>();
765   - if (ObjectUtils.isEmpty(params)) {
766   - return map;
767   - }
768   - String[] paramsArray = params.split("&");
769   - if (paramsArray.length == 0) {
770   - return map;
771   - }
772   - for (String param : paramsArray) {
773   - String[] paramArray = param.split("=");
774   - if (paramArray.length == 2){
775   - map.put(paramArray[0], paramArray[1]);
776   - }
777   - }
778   - return map;
779   - }
  600 + }
  601 + return ret;
  602 + }
  603 +
  604 + /**
  605 + * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。
  606 + */
  607 + @ResponseBody
  608 + @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
  609 + public DeferredResult<HookResult> onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) {
  610 + logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  611 +
  612 + DeferredResult<HookResult> defaultResult = new DeferredResult<>();
  613 +
  614 + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
  615 + if (!userSetting.isAutoApplyPlay() || mediaInfo == null) {
  616 + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
  617 + return defaultResult;
  618 + }
  619 +
  620 + if ("rtp".equals(param.getApp())) {
  621 + String[] s = param.getStream().split("_");
  622 + if (!mediaInfo.isRtpEnable() || s.length != 2) {
  623 + defaultResult.setResult(HookResult.SUCCESS());
  624 + return defaultResult;
  625 + }
  626 + String deviceId = s[0];
  627 + String channelId = s[1];
  628 + Device device = redisCatchStorage.getDevice(deviceId);
  629 + if (device == null) {
  630 + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
  631 + return defaultResult;
  632 + }
  633 + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
  634 + if (deviceChannel == null) {
  635 + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
  636 + return defaultResult;
  637 + }
  638 + logger.info("[ZLM HOOK] 流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  639 + RequestMessage msg = new RequestMessage();
  640 + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
  641 + boolean exist = resultHolder.exist(key, null);
  642 + msg.setKey(key);
  643 + String uuid = UUID.randomUUID().toString();
  644 + msg.setId(uuid);
  645 + DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
  646 + DeferredResultEx<HookResult> deferredResultEx = new DeferredResultEx<>(result);
  647 +
  648 + result.onTimeout(() -> {
  649 + logger.info("点播接口等待超时");
  650 + // 释放rtpserver
  651 + msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
  652 + resultHolder.invokeResult(msg);
  653 + });
  654 + // TODO 在点播未成功的情况下在此调用接口点播会导致返回的流地址ip错误
  655 + deferredResultEx.setFilter(result1 -> {
  656 + WVPResult<StreamInfo> wvpResult1 = (WVPResult<StreamInfo>) result1;
  657 + HookResult resultForEnd = new HookResult();
  658 + resultForEnd.setCode(wvpResult1.getCode());
  659 + resultForEnd.setMsg(wvpResult1.getMsg());
  660 + return resultForEnd;
  661 + });
  662 +
  663 + // 录像查询以channelId作为deviceId查询
  664 + resultHolder.put(key, uuid, deferredResultEx);
  665 +
  666 + if (!exist) {
  667 + playService.play(mediaInfo, deviceId, channelId, null, eventResult -> {
  668 + msg.setData(new HookResult(eventResult.statusCode, eventResult.msg));
  669 + resultHolder.invokeResult(msg);
  670 + }, null);
  671 + }
  672 + return result;
  673 + } else {
  674 + // 拉流代理
  675 + StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
  676 + if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) {
  677 + streamProxyService.start(param.getApp(), param.getStream());
  678 + }
  679 + DeferredResult<HookResult> result = new DeferredResult<>();
  680 + result.setResult(HookResult.SUCCESS());
  681 + return result;
  682 + }
  683 + }
  684 +
  685 + /**
  686 + * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。
  687 + */
  688 + @ResponseBody
  689 + @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
  690 + public HookResult onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject) {
  691 +
  692 + jsonObject.put("ip", request.getRemoteAddr());
  693 + ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
  694 + zlmServerConfig.setIp(request.getRemoteAddr());
  695 + logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
  696 + taskExecutor.execute(() -> {
  697 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
  698 + if (subscribes != null && subscribes.size() > 0) {
  699 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  700 + subscribe.response(null, jsonObject);
  701 + }
  702 + }
  703 + mediaServerService.zlmServerOnline(zlmServerConfig);
  704 + });
  705 +
  706 + return HookResult.SUCCESS();
  707 + }
  708 +
  709 + /**
  710 + * 发送rtp(startSendRtp)被动关闭时回调
  711 + */
  712 + @ResponseBody
  713 + @PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8")
  714 + public HookResult onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param) {
  715 +
  716 + logger.info("[ZLM HOOK] rtp发送关闭:{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream());
  717 +
  718 + // 查找对应的上级推流,发送停止
  719 + if (!"rtp".equals(param.getApp())) {
  720 + return HookResult.SUCCESS();
  721 + }
  722 + taskExecutor.execute(() -> {
  723 + List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
  724 + if (sendRtpItems.size() > 0) {
  725 + for (SendRtpItem sendRtpItem : sendRtpItems) {
  726 + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
  727 + try {
  728 + commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
  729 + } catch (SipException | InvalidArgumentException | ParseException e) {
  730 + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
  731 + }
  732 + redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
  733 + sendRtpItem.getCallId(), sendRtpItem.getStreamId());
  734 + }
  735 + }
  736 + });
  737 +
  738 + return HookResult.SUCCESS();
  739 + }
  740 +
  741 + /**
  742 + * rtpServer收流超时
  743 + */
  744 + @ResponseBody
  745 + @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
  746 + public HookResult onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param) {
  747 + logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
  748 +
  749 + taskExecutor.execute(() -> {
  750 + JSONObject json = (JSONObject) JSON.toJSON(param);
  751 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
  752 + if (subscribes != null && subscribes.size() > 0) {
  753 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  754 + subscribe.response(null, json);
  755 + }
  756 + }
  757 + });
  758 +
  759 + return HookResult.SUCCESS();
  760 + }
  761 +
  762 + private Map<String, String> urlParamToMap(String params) {
  763 + HashMap<String, String> map = new HashMap<>();
  764 + if (ObjectUtils.isEmpty(params)) {
  765 + return map;
  766 + }
  767 + String[] paramsArray = params.split("&");
  768 + if (paramsArray.length == 0) {
  769 + return map;
  770 + }
  771 + for (String param : paramsArray) {
  772 + String[] paramArray = param.split("=");
  773 + if (paramArray.length == 2) {
  774 + map.put(paramArray[0], paramArray[1]);
  775 + }
  776 + }
  777 + return map;
  778 + }
780 779 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
... ... @@ -36,7 +36,7 @@ public class ZLMRESTfulUtils {
36 36 // 设置连接超时时间
37 37 httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS);
38 38 // 设置读取超时时间
39   - httpClientBuilder.readTimeout(15,TimeUnit.SECONDS);
  39 + httpClientBuilder.readTimeout(10,TimeUnit.SECONDS);
40 40 // 设置连接池
41 41 httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES));
42 42 if (logger.isDebugEnabled()) {
... ... @@ -189,6 +189,7 @@ public class ZLMRESTfulUtils {
189 189 FileOutputStream outStream = new FileOutputStream(snapFile);
190 190  
191 191 outStream.write(Objects.requireNonNull(response.body()).bytes());
  192 + outStream.flush();
192 193 outStream.close();
193 194 } else {
194 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 152  
153 153 @Override
154 154 public void sendCatalogMsg(GbStream gbStream, String type) {
  155 + if (gbStream == null || type == null) {
  156 + logger.warn("[发送目录订阅]类型:流信息或类型为NULL");
  157 + return;
  158 + }
155 159 List<GbStream> gbStreams = new ArrayList<>();
156 160 if (gbStream.getGbId() != null) {
157 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 33 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
34 34 import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
35 35 import com.genersoft.iot.vmp.utils.DateUtil;
  36 +import com.genersoft.iot.vmp.utils.JsonUtil;
36 37 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
37 38 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
38 39 import okhttp3.OkHttpClient;
... ... @@ -241,7 +242,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
241 242 String onlineKey = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
242 243 for (Object mediaServerKey : mediaServerKeys) {
243 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 250 Double aDouble = RedisUtil.zScore(onlineKey, mediaServerItem.getId());
247 251 if (aDouble != null) {
... ... @@ -293,7 +297,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
293 297 return null;
294 298 }
295 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 414 SsrcConfig ssrcConfig = new SsrcConfig(zlmServerConfig.getGeneralMediaServerId(), null, sipConfig.getDomain());
411 415 serverItem.setSsrcConfig(ssrcConfig);
412 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 422 RedisUtil.set(key, serverItem);
417 423 resetOnlineServerItem(serverItem);
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
... ... @@ -184,7 +184,9 @@ public class StreamPushServiceImpl implements IStreamPushService {
184 184 @Override
185 185 public boolean stop(String app, String streamId) {
186 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 191 platformGbStreamMapper.delByAppAndStream(app, streamId);
190 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 62 }
63 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 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 2  
3 3 import com.genersoft.iot.vmp.gb28181.bean.*;
4 4 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
5   -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
6 5 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
7 6 import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
8 7 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
... ... @@ -186,7 +185,13 @@ public interface IVideoManagerStorage {
186 185  
187 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 196 * 添加Mobile Position设备移动位置
192 197 * @param mobilePosition
... ... @@ -324,6 +329,8 @@ public interface IVideoManagerStorage {
324 329 */
325 330 boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList);
326 331  
  332 + boolean updateChannels(String deviceId, List<DeviceChannel> deviceChannelList);
  333 +
327 334 /**
328 335 * 获取目录信息
329 336 * @param platformId
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java
1 1 package com.genersoft.iot.vmp.storager.dao;
2 2  
3 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 8 import org.springframework.stereotype.Repository;
8 9  
9 10 import java.util.List;
... ... @@ -20,7 +21,7 @@ public interface DeviceAlarmMapper {
20 21 int add(DeviceAlarm alarm);
21 22  
22 23  
23   - @Select(value = {" <script>" +
  24 + @Select( value = {" <script>" +
24 25 " SELECT * FROM device_alarm " +
25 26 " WHERE 1=1 " +
26 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 107 "DELETE FROM platform_gb_channel WHERE platformId=#{platformId} and catalogId=#{catalogId}" +
108 108 "</script>")
109 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 17 import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
18 18 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
19 19 import com.genersoft.iot.vmp.utils.DateUtil;
  20 +import com.genersoft.iot.vmp.utils.JsonUtil;
20 21 import com.genersoft.iot.vmp.utils.SystemInfoUtils;
21 22 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
22 23 import org.slf4j.Logger;
... ... @@ -157,7 +158,10 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
157 158 }
158 159 for (Object player : players) {
159 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 165 streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getChannelId(), streamInfo);
162 166 }
163 167 return streamInfos;
... ... @@ -624,8 +628,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
624 628 @Override
625 629 public ThirdPartyGB queryMemberNoGBId(String queryKey) {
626 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 634 @Override
... ... @@ -664,7 +667,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
664 667 @Override
665 668 public Device getDevice(String deviceId) {
666 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 673 @Override
... ... @@ -676,7 +679,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
676 679 @Override
677 680 public GPSMsgInfo getGpsMsgInfo(String gbId) {
678 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 685 @Override
... ... @@ -686,9 +689,9 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
686 689 List<Object> keys = RedisUtil.scan(scanKey);
687 690 for (Object o : keys) {
688 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 713 @Override
711 714 public StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream) {
712 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 724 List<Object> keys = RedisUtil.scan(scanKey);
722 725 for (Object o : keys) {
723 726 String key = (String) o;
724   - result.add((StreamAuthorityInfo) RedisUtil.get(key));
  727 + result.add(JsonUtil.redisJsonToObject(key, StreamAuthorityInfo.class));
725 728 }
726 729 return result;
727 730 }
... ... @@ -735,7 +738,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
735 738 List<Object> keys = RedisUtil.scan(scanKey);
736 739 if (keys.size() > 0) {
737 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 744 return result;
... ... @@ -827,7 +830,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
827 830  
828 831 @Override
829 832 public void sendAlarmMsg(AlarmChannelMessage msg) {
830   - String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM;
  833 + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE;
831 834 logger.info("[redis发送通知] 报警{}: {}", key, JSON.toJSON(msg));
832 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 126 if (allChannelMap.containsKey(deviceChannel.getChannelId())) {
127 127 deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId());
128 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 139 channels.add(deviceChannel);
131 140 if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) {
... ... @@ -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 312 @Override
191 313 public void deviceChannelOnline(String deviceId, String channelId) {
192 314 deviceChannelMapper.online(deviceId, channelId);
... ... @@ -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 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 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 110 msg.setKey(key);
111 111 msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", event.statusCode, event.msg));
112 112 resultHolder.invokeAllResult(msg);
113   - });
  113 + },null);
114 114 } catch (InvalidArgumentException | SipException | ParseException e) {
115 115 logger.error("[命令发送失败] 开始/停止录像: {}", e.getMessage());
116 116 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
... ... @@ -143,7 +143,7 @@ public class DeviceControl {
143 143 msg.setKey(key);
144 144 msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", event.statusCode, event.msg));
145 145 resultHolder.invokeResult(msg);
146   - });
  146 + },null);
147 147 } catch (InvalidArgumentException | SipException | ParseException e) {
148 148 logger.error("[命令发送失败] 布防/撤防操作: {}", e.getMessage());
149 149 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage());
... ... @@ -192,7 +192,7 @@ public class DeviceControl {
192 192 msg.setKey(key);
193 193 msg.setData(String.format("报警复位操作失败,错误码: %s, %s", event.statusCode, event.msg));
194 194 resultHolder.invokeResult(msg);
195   - });
  195 + },null);
196 196 } catch (InvalidArgumentException | SipException | ParseException e) {
197 197 logger.error("[命令发送失败] 报警复位: {}", e.getMessage());
198 198 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
... ... @@ -274,7 +274,7 @@ public class DeviceControl {
274 274 msg.setKey(key);
275 275 msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", event.statusCode, event.msg));
276 276 resultHolder.invokeResult(msg);
277   - });
  277 + },null);
278 278 } catch (InvalidArgumentException | SipException | ParseException e) {
279 279 logger.error("[命令发送失败] 看守位控制: {}", e.getMessage());
280 280 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
1 1 package com.genersoft.iot.vmp.vmanager.gb28181.playback;
2 2  
3 3 import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.conf.UserSetting;
4 5 import com.genersoft.iot.vmp.conf.exception.ControllerException;
5 6 import com.genersoft.iot.vmp.conf.exception.ServiceException;
6 7 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
... ... @@ -64,13 +65,16 @@ public class PlaybackController {
64 65 @Autowired
65 66 private DeferredResultHolder resultHolder;
66 67  
  68 + @Autowired
  69 + private UserSetting userSetting;
  70 +
67 71 @Operation(summary = "开始视频回放")
68 72 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
69 73 @Parameter(name = "channelId", description = "通道国标编号", required = true)
70 74 @Parameter(name = "startTime", description = "开始时间", required = true)
71 75 @Parameter(name = "endTime", description = "结束时间", required = true)
72 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 78 String startTime, String endTime) {
75 79  
76 80 if (logger.isDebugEnabled()) {
... ... @@ -79,7 +83,7 @@ public class PlaybackController {
79 83  
80 84 String uuid = UUID.randomUUID().toString();
81 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 87 resultHolder.put(key, uuid, result);
84 88  
85 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 11 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
12 12 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
13 13 import com.github.pagehelper.PageInfo;
14   -
15 14 import io.swagger.v3.oas.annotations.Operation;
16 15 import io.swagger.v3.oas.annotations.Parameter;
17 16 import io.swagger.v3.oas.annotations.tags.Tag;
18 17 import org.springframework.beans.factory.annotation.Autowired;
19   -import org.springframework.http.HttpStatus;
20   -import org.springframework.http.ResponseEntity;
21 18 import org.springframework.security.authentication.AuthenticationManager;
22 19 import org.springframework.util.DigestUtils;
23 20 import org.springframework.util.ObjectUtils;
24   -import org.springframework.util.StringUtils;
25 21 import org.springframework.web.bind.annotation.*;
26 22  
27 23 import javax.security.sasl.AuthenticationException;
... ... @@ -90,7 +86,7 @@ public class UserController {
90 86  
91 87  
92 88 @PostMapping("/add")
93   - @Operation(summary = "停止视频回放")
  89 + @Operation(summary = "添加用户")
94 90 @Parameter(name = "username", description = "用户名", required = true)
95 91 @Parameter(name = "password", description = "密码(未md5加密的密码)", required = true)
96 92 @Parameter(name = "roleId", description = "角色ID", required = true)
... ...
src/main/resources/all-application.yml
... ... @@ -167,7 +167,7 @@ user-settings:
167 167 senior-sdp: false
168 168 # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认)
169 169 save-position-history: false
170   - # 点播等待超时时间,单位:毫秒
  170 + # 点播/录像回放 等待超时时间,单位:毫秒
171 171 play-timeout: 18000
172 172 # 上级点播等待超时时间,单位:毫秒
173 173 platform-play-timeout: 60000
... ...
src/main/resources/application-dev.yml
1 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 45 #[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
46 46 server:
47   - port: 18080
  47 + port: 18080
48 48  
49 49 # 作为28181服务器的配置
50 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 65 #zlm 默认服务器配置
66 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 85 logging:
86   - config: classpath:logback-spring-local.xml
  86 + config: classpath:logback-spring-local.xml
... ...
src/main/resources/application.yml
1 1 spring:
  2 + application:
  3 + name: wvp
2 4 profiles:
3 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 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 39 `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
40 40 `port` int DEFAULT NULL,
41 41 `expires` int DEFAULT NULL,
  42 + `keepaliveIntervalTime` int DEFAULT NULL,
42 43 `subscribeCycleForCatalog` int DEFAULT NULL,
43 44 `hostAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
44 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 47 if (options.extract) {
48 48 return ExtractTextPlugin.extract({
49 49 use: loaders,
50   - fallback: 'vue-style-loader'
  50 + fallback: 'vue-style-loader',
  51 + publicPath: '../../'
51 52 })
52 53 } else {
53 54 return ['vue-style-loader'].concat(loaders)
... ...
web_src/config/index.js
... ... @@ -8,8 +8,8 @@ module.exports = {
8 8 dev: {
9 9  
10 10 // Paths
11   - assetsSubDirectory: 'static',
12   - assetsPublicPath: '/',
  11 + assetsSubDirectory: './static',
  12 + assetsPublicPath: './',
13 13 proxyTable: {
14 14 '/debug': {
15 15 target: 'https://default.wvp-pro.cn:18080',
... ... @@ -61,7 +61,7 @@ module.exports = {
61 61 // Paths
62 62 assetsRoot: path.resolve(__dirname, '../../src/main/resources/static/'),
63 63 assetsSubDirectory: './static',
64   - assetsPublicPath: '/',
  64 + assetsPublicPath: './',
65 65  
66 66 /**
67 67 * Source Maps
... ...
web_src/src/components/CloudRecord.vue
... ... @@ -133,7 +133,7 @@
133 133 let that = this;
134 134 this.$axios({
135 135 method: 'get',
136   - url:`/record_proxy/${that.mediaServerId}/api/record/list`,
  136 + url:`./record_proxy/${that.mediaServerId}/api/record/list`,
137 137 params: {
138 138 page: that.currentPage,
139 139 count: that.count
... ... @@ -185,7 +185,7 @@
185 185 let that = this;
186 186 this.$axios({
187 187 method: 'delete',
188   - url:`/record_proxy/api/record/delete`,
  188 + url:`./record_proxy/api/record/delete`,
189 189 params: {
190 190 page: that.currentPage,
191 191 count: that.count
... ...
web_src/src/components/CloudRecordDetail.vue
1 1 <template>
2 2 <div id="recordDetail">
3 3 <el-container>
4   -
5   - <el-aside width="300px">
6   -
  4 + <el-aside width="260px">
7 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 10 <div class="record-list-box" :style="recordListStyle">
10 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 13 <el-tag v-if="choosedFile != item" @click="chooseFile(item)">
13 14 <i class="el-icon-video-camera" ></i>
14 15 {{ item.substring(0,17)}}
... ... @@ -24,9 +25,7 @@
24 25 <div v-if="detailFiles.length ==0" class="record-list-no-val" >暂无数据</div>
25 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 29 </el-aside>
31 30 <el-main style="padding: 22px">
32 31 <div class="playBox" :style="playerStyle">
... ... @@ -45,7 +44,7 @@
45 44 :marks="playTimeSliderMarks">
46 45 </el-slider>
47 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 48 </div>
50 49 </div>
51 50  
... ... @@ -62,7 +61,7 @@
62 61 <el-tab-pane name="running">
63 62 <span slot="label"><i class="el-icon-scissors"></i>进行中</span>
64 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 65 <div class="task-list-item-box">
67 66 <span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span>
68 67 <el-progress :percentage="(parseFloat(item.percentage)*100).toFixed(1)"></el-progress>
... ... @@ -74,10 +73,10 @@
74 73 <el-tab-pane name="ended">
75 74 <span slot="label"><i class="el-icon-finished"></i>已完成</span>
76 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 77 <div class="task-list-item-box" style="height: 2rem;line-height: 2rem;">
79 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 80 </a>
82 81 </div>
83 82 </li>
... ... @@ -116,7 +115,7 @@
116 115 props: ['recordFile', 'mediaServerId', 'dateFiles', 'mediaServerPath'],
117 116 data() {
118 117 return {
119   - basePath: `${this.mediaServerPath}`,
  118 + basePath: `${this.mediaServerPath}/record`,
120 119 dateFilesObj: [],
121 120 detailFiles: [],
122 121 chooseDate: null,
... ... @@ -147,6 +146,7 @@
147 146 "margin-bottom": "20px",
148 147 "height": this.winHeight + "px",
149 148 },
  149 + timeFormat:'00:00:00',
150 150 winHeight: window.innerHeight - 240,
151 151 playTime: 0,
152 152 playTimeSliderMarks: {
... ... @@ -213,7 +213,7 @@
213 213 this.currentPage = 1;
214 214 this.sliderMIn= 0;
215 215 this.sliderMax= 86400;
216   - let chooseFullDate = new Date(this.chooseDate + " " + "00:00:00");
  216 + let chooseFullDate = new Date(this.chooseDate +" " + this.timeFormat);
217 217 if (chooseFullDate.getFullYear() !== this.queryDate.getFullYear()
218 218 || chooseFullDate.getMonth() !== this.queryDate.getMonth()){
219 219 // this.getDateInYear()
... ... @@ -222,8 +222,8 @@
222 222 if (this.detailFiles.length > 0){
223 223 let timeForFile = this.getTimeForFile(this.detailFiles[0]);
224 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 228 this.playTime = parseInt(timeNum/1000)
229 229 this.sliderMIn = parseInt(timeNum/1000 - timeNum/1000%(60*60))
... ... @@ -241,7 +241,7 @@
241 241 let that = this;
242 242 that.$axios({
243 243 method: 'get',
244   - url:`/record_proxy/${that.mediaServerId}/api/record/file/list`,
  244 + url:`./record_proxy/${that.mediaServerId}/api/record/file/list`,
245 245 params: {
246 246 app: that.recordFile.app,
247 247 stream: that.recordFile.stream,
... ... @@ -281,14 +281,14 @@
281 281 },
282 282 getDataLeft(item){
283 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 285 return parseFloat((differenceTime - this.sliderMIn * 1000)/((this.sliderMax - this.sliderMIn)*1000))*100 ;
286 286 },
287 287 playTimeChange(val){
288 288 let minTime = this.getTimeForFile(this.detailFiles[0])[0]
289 289 let maxTime = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1])[1];
290 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 292 if (timeMilli >= minTime.getTime() && timeMilli <= maxTime.getTime()){
293 293 for (let i = 0; i < this.detailFiles.length; i++) {
294 294 let timeForFile = this.getTimeForFile(this.detailFiles[i]);
... ... @@ -302,10 +302,20 @@
302 302 },
303 303 getTimeForFile(file){
304 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 314 return [starTime, endTime, endTime.getTime() - starTime.getTime()];
308 315 },
  316 + checkIsOver24h(starTime,endTime){
  317 + return starTime > endTime;
  318 + },
309 319 playTimeFormat(val){
310 320 let h = parseInt(val/3600);
311 321 let m = parseInt((val - h*3600)/60);
... ... @@ -330,7 +340,7 @@
330 340 let that = this;
331 341 this.$axios({
332 342 method: 'delete',
333   - url:`/record_proxy/${that.mediaServerId}/api/record/delete`,
  343 + url:`./record_proxy/${that.mediaServerId}/api/record/delete`,
334 344 params: {
335 345 page: that.currentPage,
336 346 count: that.count
... ... @@ -349,7 +359,7 @@
349 359 that.dateFilesObj = {};
350 360 this.$axios({
351 361 method: 'get',
352   - url:`/record_proxy/${that.mediaServerId}/api/record/date/list`,
  362 + url:`./record_proxy/${that.mediaServerId}/api/record/date/list`,
353 363 params: {
354 364 app: that.recordFile.app,
355 365 stream: that.recordFile.stream
... ... @@ -398,7 +408,7 @@
398 408 let that = this;
399 409 this.$axios({
400 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 412 params: {
403 413 app: that.recordFile.app,
404 414 stream: that.recordFile.stream,
... ... @@ -423,7 +433,7 @@
423 433 let that = this;
424 434 this.$axios({
425 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 437 params: {
428 438 isEnd: isEnd,
429 439 }
... ...
web_src/src/components/DeviceList.vue
... ... @@ -152,7 +152,7 @@ export default {
152 152 this.getDeviceListLoading = true;
153 153 this.$axios({
154 154 method: 'get',
155   - url: `/api/device/query/devices`,
  155 + url: `./api/device/query/devices`,
156 156 params: {
157 157 page: this.currentPage,
158 158 count: this.count
... ... @@ -182,7 +182,7 @@ export default {
182 182 }).then(() => {
183 183 this.$axios({
184 184 method: 'delete',
185   - url: `/api/device/query/devices/${row.deviceId}/delete`
  185 + url: `./api/device/query/devices/${row.deviceId}/delete`
186 186 }).then((res) => {
187 187 this.getDeviceList();
188 188 }).catch((error) => {
... ... @@ -208,7 +208,7 @@ export default {
208 208 let that = this;
209 209 this.$axios({
210 210 method: 'get',
211   - url: '/api/device/query/devices/' + itemData.deviceId + '/sync'
  211 + url: './api/device/query/devices/' + itemData.deviceId + '/sync'
212 212 }).then((res) => {
213 213 console.log("刷新设备结果:" + JSON.stringify(res));
214 214 if (res.data.code !== 0) {
... ... @@ -242,7 +242,7 @@ export default {
242 242 await this.$axios({
243 243 method: 'get',
244 244 async: false,
245   - url: `/api/device/query/${deviceId}/sync_status/`,
  245 + url: `./api/device/query/${deviceId}/sync_status/`,
246 246 }).then((res) => {
247 247 if (res.data.code == 0) {
248 248 if (res.data.data.errorMsg !== null) {
... ... @@ -261,7 +261,7 @@ export default {
261 261 let that = this;
262 262 this.$axios({
263 263 method: 'post',
264   - url: '/api/device/query/transport/' + row.deviceId + '/' + row.streamMode
  264 + url: './api/device/query/transport/' + row.deviceId + '/' + row.streamMode
265 265 }).then(function (res) {
266 266  
267 267 }).catch(function (e) {
... ...
web_src/src/components/GBRecordDetail.vue
... ... @@ -197,7 +197,7 @@
197 197 this.detailFiles = [];
198 198 this.$axios({
199 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 201 }).then((res)=>{
202 202 this.recordsLoading = false;
203 203 if(res.data.code === 0) {
... ... @@ -249,7 +249,7 @@
249 249 } else {
250 250 this.$axios({
251 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 253 this.endTime
254 254 }).then((res)=> {
255 255 if (res.data.code === 0) {
... ... @@ -273,7 +273,7 @@
273 273 console.log('前端控制:播放');
274 274 this.$axios({
275 275 method: 'get',
276   - url: '/api/playback/resume/' + this.streamId
  276 + url: './api/playback/resume/' + this.streamId
277 277 }).then((res)=> {
278 278 this.$refs["recordVideoPlayer"].play(this.videoUrl)
279 279 });
... ... @@ -282,14 +282,14 @@
282 282 console.log('前端控制:暂停');
283 283 this.$axios({
284 284 method: 'get',
285   - url: '/api/playback/pause/' + this.streamId
  285 + url: './api/playback/pause/' + this.streamId
286 286 }).then(function (res) {});
287 287 },
288 288 gbScale(command){
289 289 console.log('前端控制:倍速 ' + command);
290 290 this.$axios({
291 291 method: 'get',
292   - url: `/api/playback/speed/${this.streamId }/${command}`
  292 + url: `./api/playback/speed/${this.streamId }/${command}`
293 293 }).then(function (res) {});
294 294 },
295 295 downloadRecord: function (row) {
... ... @@ -311,7 +311,7 @@
311 311 }else {
312 312 this.$axios({
313 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 315 row.endTime + '&downloadSpeed=4'
316 316 }).then( (res)=> {
317 317 if (res.data.code === 0) {
... ... @@ -332,7 +332,7 @@
332 332 this.videoUrl = '';
333 333 this.$axios({
334 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 336 }).then((res)=> {
337 337 if (callback) callback(res)
338 338 });
... ... @@ -342,7 +342,7 @@
342 342 this.videoUrl = '';
343 343 this.$axios({
344 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 346 }).then(function (res) {
347 347 if (callback) callback()
348 348 });
... ...
web_src/src/components/Login.vue
... ... @@ -81,7 +81,7 @@ export default {
81 81  
82 82 this.$axios({
83 83 method: 'get',
84   - url:"/api/user/login",
  84 + url:"./api/user/login",
85 85 params: loginParam
86 86 }).then(function (res) {
87 87 window.clearTimeout(timeoutTask)
... ...
web_src/src/components/ParentPlatformList.vue
... ... @@ -128,7 +128,7 @@ export default {
128 128 var that = this;
129 129 that.$axios({
130 130 method: 'delete',
131   - url:`/api/platform/delete/${platform.serverGBId}`
  131 + url:`./api/platform/delete/${platform.serverGBId}`
132 132 }).then(function (res) {
133 133 if (res.data.code === 0) {
134 134 that.$message({
... ... @@ -162,7 +162,7 @@ export default {
162 162  
163 163 this.$axios({
164 164 method: 'get',
165   - url:`/api/platform/query/${that.count}/${that.currentPage}`
  165 + url:`./api/platform/query/${that.count}/${that.currentPage}`
166 166 }).then(function (res) {
167 167 if (res.data.code === 0) {
168 168 that.total = res.data.data.total;
... ...
web_src/src/components/PushVideoList.vue
... ... @@ -171,7 +171,7 @@ export default {
171 171 this.getDeviceListLoading = true;
172 172 this.$axios({
173 173 method: 'get',
174   - url: `/api/push/list`,
  174 + url: `./api/push/list`,
175 175 params: {
176 176 page: that.currentPage,
177 177 count: that.count,
... ... @@ -197,7 +197,7 @@ export default {
197 197 this.getListLoading = true;
198 198 this.$axios({
199 199 method: 'get',
200   - url: '/api/push/getPlayUrl',
  200 + url: './api/push/getPlayUrl',
201 201 params: {
202 202 app: row.app,
203 203 stream: row.stream,
... ... @@ -223,7 +223,7 @@ export default {
223 223 let that = this;
224 224 that.$axios({
225 225 method: "post",
226   - url: "/api/push/stop",
  226 + url: "./api/push/stop",
227 227 params: {
228 228 app: row.app,
229 229 streamId: row.stream
... ... @@ -247,7 +247,7 @@ export default {
247 247 let that = this;
248 248 that.$axios({
249 249 method: "delete",
250   - url: "/api/push/remove_form_gb",
  250 + url: "./api/push/remove_form_gb",
251 251 data: row
252 252 }).then((res) => {
253 253 if (res.data.code === 0) {
... ... @@ -274,7 +274,7 @@ export default {
274 274 let that = this;
275 275 that.$axios({
276 276 method: "delete",
277   - url: "/api/push/batchStop",
  277 + url: "./api/push/batchStop",
278 278 data: {
279 279 gbStreams: this.multipleSelection
280 280 }
... ...
web_src/src/components/StreamProxyList.vue
... ... @@ -167,7 +167,7 @@
167 167 let that = this;
168 168 this.$axios({
169 169 method: 'get',
170   - url:`/api/proxy/list`,
  170 + url:`./api/proxy/list`,
171 171 params: {
172 172 page: that.currentPage,
173 173 count: that.count
... ... @@ -190,7 +190,7 @@
190 190 addOnvif: function(){
191 191 this.$axios({
192 192 method: 'get',
193   - url:`/api/onvif/search?timeout=3000`,
  193 + url:`./api/onvif/search?timeout=3000`,
194 194 }).then((res) =>{
195 195 if (res.data.code === 0 ){
196 196 if (res.data.data.length > 0) {
... ... @@ -218,7 +218,7 @@
218 218 let that = this;
219 219 this.$axios({
220 220 method: 'get',
221   - url:`/api/push/getPlayUrl`,
  221 + url:`./api/push/getPlayUrl`,
222 222 params: {
223 223 app: row.app,
224 224 stream: row.stream,
... ... @@ -247,7 +247,7 @@
247 247 let that = this;
248 248 that.$axios({
249 249 method:"delete",
250   - url:"/api/proxy/del",
  250 + url:"./api/proxy/del",
251 251 params:{
252 252 app: row.app,
253 253 stream: row.stream
... ... @@ -263,7 +263,7 @@
263 263 this.$set(row, 'startBtnLoading', true)
264 264 this.$axios({
265 265 method: 'get',
266   - url:`/api/proxy/start`,
  266 + url:`./api/proxy/start`,
267 267 params: {
268 268 app: row.app,
269 269 stream: row.stream
... ... @@ -295,7 +295,7 @@
295 295 let that = this;
296 296 this.$axios({
297 297 method: 'get',
298   - url:`/api/proxy/stop`,
  298 + url:`./api/proxy/stop`,
299 299 params: {
300 300 app: row.app,
301 301 stream: row.stream
... ...
web_src/src/components/UserManager.vue
... ... @@ -99,7 +99,7 @@ export default {
99 99 this.getUserListLoading = true;
100 100 this.$axios({
101 101 method: 'get',
102   - url: `/api/user/users`,
  102 + url: `./api/user/users`,
103 103 params: {
104 104 page: that.currentPage,
105 105 count: that.count
... ... @@ -141,7 +141,7 @@ export default {
141 141 }).then(() => {
142 142 this.$axios({
143 143 method: 'delete',
144   - url: `/api/user/delete?id=${row.id}`
  144 + url: `./api/user/delete?id=${row.id}`
145 145 }).then((res) => {
146 146 this.getUserList();
147 147 }).catch((error) => {
... ...
web_src/src/components/channelList.vue
... ... @@ -206,7 +206,7 @@ export default {
206 206 if (typeof (this.$route.params.deviceId) == "undefined") return;
207 207 this.$axios({
208 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 210 params: {
211 211 page: that.currentPage,
212 212 count: that.count,
... ... @@ -238,7 +238,7 @@ export default {
238 238 let that = this;
239 239 this.$axios({
240 240 method: 'get',
241   - url: '/api/play/start/' + deviceId + '/' + channelId
  241 + url: './api/play/start/' + deviceId + '/' + channelId
242 242 }).then(function (res) {
243 243 console.log(res)
244 244 that.isLoging = false;
... ... @@ -278,7 +278,7 @@ export default {
278 278 var that = this;
279 279 this.$axios({
280 280 method: 'get',
281   - url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId
  281 + url: './api/play/stop/' + this.deviceId + "/" + itemData.channelId
282 282 }).then(function (res) {
283 283 that.initData();
284 284 }).catch(function (error) {
... ... @@ -334,7 +334,7 @@ export default {
334 334 if (!this.showTree) {
335 335 this.$axios({
336 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 338 params: {
339 339 page: this.currentPage,
340 340 count: this.count,
... ... @@ -358,7 +358,7 @@ export default {
358 358 }else {
359 359 this.$axios({
360 360 method: 'get',
361   - url: `/api/device/query/tree/channel/${this.deviceId}`,
  361 + url: `./api/device/query/tree/channel/${this.deviceId}`,
362 362 params: {
363 363 parentId: this.parentChannelId,
364 364 page: this.currentPage,
... ... @@ -387,7 +387,7 @@ export default {
387 387 updateChannel: function (row) {
388 388 this.$axios({
389 389 method: 'post',
390   - url: `/api/device/query/channel/update/${this.deviceId}`,
  390 + url: `./api/device/query/channel/update/${this.deviceId}`,
391 391 params: row
392 392 }).then(function (res) {
393 393 console.log(JSON.stringify(res));
... ...
web_src/src/components/console.vue
... ... @@ -114,7 +114,7 @@ export default {
114 114 getSystemInfo: function (){
115 115 this.$axios({
116 116 method: 'get',
117   - url: `/api/server/system/info`,
  117 + url: `./api/server/system/info`,
118 118 }).then( (res)=> {
119 119 if (res.data.code === 0) {
120 120 this.$refs.consoleCPU.setData(res.data.data.cpu)
... ... @@ -128,7 +128,7 @@ export default {
128 128 getLoad: function (){
129 129 this.$axios({
130 130 method: 'get',
131   - url: `/api/server/media_server/load`,
  131 + url: `./api/server/media_server/load`,
132 132 }).then( (res)=> {
133 133 if (res.data.code === 0) {
134 134 this.$refs.consoleNodeLoad.setData(res.data.data)
... ... @@ -139,7 +139,7 @@ export default {
139 139 getResourceInfo: function (){
140 140 this.$axios({
141 141 method: 'get',
142   - url: `/api/server/resource/info`,
  142 + url: `./api/server/resource/info`,
143 143 }).then( (res)=> {
144 144 if (res.data.code === 0) {
145 145 this.$refs.consoleResource.setData(res.data.data)
... ... @@ -151,7 +151,7 @@ export default {
151 151  
152 152 this.$axios({
153 153 method: 'get',
154   - url: `/api/server/system/configInfo`,
  154 + url: `./api/server/system/configInfo`,
155 155 }).then( (res)=> {
156 156 console.log(res)
157 157 if (res.data.code === 0) {
... ...
web_src/src/components/dialog/MediaServerEdit.vue
... ... @@ -335,7 +335,7 @@ export default {
335 335 var that = this;
336 336 await that.$axios({
337 337 method: 'get',
338   - url:`/api/platform/exit/${deviceGbId}`
  338 + url:`./api/platform/exit/${deviceGbId}`
339 339 }).then(function (res) {
340 340 result = res.data;
341 341 }).catch(function (error) {
... ...
web_src/src/components/dialog/StreamProxyEdit.vue
... ... @@ -195,7 +195,7 @@ export default {
195 195 let that = this;
196 196 this.$axios({
197 197 method: 'get',
198   - url:`/api/platform/query/10000/1`
  198 + url:`./api/platform/query/10000/1`
199 199 }).then(function (res) {
200 200 that.platformList = res.data.data.list;
201 201 }).catch(function (error) {
... ... @@ -212,7 +212,7 @@ export default {
212 212 if (that.proxyParam.mediaServerId !== "auto"){
213 213 that.$axios({
214 214 method: 'get',
215   - url:`/api/proxy/ffmpeg_cmd/list`,
  215 + url:`./api/proxy/ffmpeg_cmd/list`,
216 216 params: {
217 217 mediaServerId: that.proxyParam.mediaServerId
218 218 }
... ... @@ -230,7 +230,7 @@ export default {
230 230 this.noneReaderHandler();
231 231 this.$axios({
232 232 method: 'post',
233   - url:`/api/proxy/save`,
  233 + url:`./api/proxy/save`,
234 234 data: this.proxyParam
235 235 }).then((res)=> {
236 236 this.dialogLoading = false;
... ... @@ -261,7 +261,7 @@ export default {
261 261 var that = this;
262 262 await that.$axios({
263 263 method: 'get',
264   - url:`/api/platform/exit/${deviceGbId}`
  264 + url:`./api/platform/exit/${deviceGbId}`
265 265 }).then(function (res) {
266 266 result = res.data;
267 267 }).catch(function (error) {
... ...
web_src/src/components/dialog/SyncChannelProgress.vue
... ... @@ -55,7 +55,7 @@ export default {
55 55 getProgress(){
56 56 this.$axios({
57 57 method: 'get',
58   - url:`/api/device/query/${this.deviceId}/sync_status/`,
  58 + url:`./api/device/query/${this.deviceId}/sync_status/`,
59 59 }).then((res) => {
60 60 if (res.data.code === 0) {
61 61 if (!this.syncFlag) {
... ...
web_src/src/components/dialog/addUser.vue
... ... @@ -100,7 +100,7 @@ export default {
100 100 onSubmit: function () {
101 101 this.$axios({
102 102 method: 'post',
103   - url: "/api/user/add",
  103 + url: "./api/user/add",
104 104 params: {
105 105 username: this.username,
106 106 password: this.password,
... ... @@ -139,7 +139,7 @@ export default {
139 139  
140 140 this.$axios({
141 141 method: 'get',
142   - url: "/api/role/all"
  142 + url: "./api/role/all"
143 143 }).then((res) => {
144 144 this.loading = true;
145 145 if (res.data.code === 0) {
... ...
web_src/src/components/dialog/catalogEdit.vue
... ... @@ -116,7 +116,7 @@ export default {
116 116 console.log(this.form);
117 117 this.$axios({
118 118 method:"post",
119   - url:`/api/platform/catalog/${!this.isEdit? "add":"edit"}`,
  119 + url:`./api/platform/catalog/${!this.isEdit? "add":"edit"}`,
120 120 data: this.form
121 121 }).then((res)=> {
122 122 if (res.data.code === 0) {
... ...
web_src/src/components/dialog/changePassword.vue
... ... @@ -90,7 +90,7 @@ export default {
90 90 onSubmit: function () {
91 91 this.$axios({
92 92 method: 'post',
93   - url:"/api/user/changePassword",
  93 + url:"./api/user/changePassword",
94 94 params: {
95 95 oldPassword: crypto.createHash('md5').update(this.oldPassword, "utf8").digest('hex'),
96 96 password: this.newPassword
... ...
web_src/src/components/dialog/changePasswordForAdmin.vue
... ... @@ -85,7 +85,7 @@ export default {
85 85 onSubmit: function () {
86 86 this.$axios({
87 87 method: 'post',
88   - url:"/api/user/changePasswordForAdmin",
  88 + url:"./api/user/changePasswordForAdmin",
89 89 params: {
90 90 password: this.newPassword,
91 91 userId: this.form.id,
... ...
web_src/src/components/dialog/changePushKey.vue
... ... @@ -65,7 +65,7 @@ export default {
65 65 onSubmit: function () {
66 66 this.$axios({
67 67 method: 'post',
68   - url:"/api/user/changePushKey",
  68 + url:"./api/user/changePushKey",
69 69 params: {
70 70 pushKey: this.newPushKey,
71 71 userId: this.form.id,
... ...
web_src/src/components/dialog/channelMapInfobox.vue
... ... @@ -44,7 +44,7 @@ export default {
44 44 let that = this;
45 45 this.$axios({
46 46 method: 'get',
47   - url: '/api/play/start/' + deviceId + '/' + channelId
  47 + url: './api/play/start/' + deviceId + '/' + channelId
48 48 }).then(function (res) {
49 49 that.isLoging = false;
50 50 if (res.data.code === 0) {
... ...
web_src/src/components/dialog/chooseChannel.vue
... ... @@ -98,7 +98,7 @@ export default {
98 98  
99 99 this.$axios({
100 100 method:"post",
101   - url:"/api/platform/update_channel_for_gb",
  101 + url:"./api/platform/update_channel_for_gb",
102 102 data:{
103 103 platformId: that.platformId,
104 104 channelReduces: that.chooseData
... ...
web_src/src/components/dialog/chooseChannelForCatalog.vue
... ... @@ -82,7 +82,7 @@ export default {
82 82 let that = this;
83 83 this.$axios({
84 84 method:"get",
85   - url:`/api/platform/catalog`,
  85 + url:`./api/platform/catalog`,
86 86 params: {
87 87 platformId: that.platformId,
88 88 parentId: parentId
... ... @@ -134,7 +134,7 @@ export default {
134 134 removeCatalog: function (id, node){
135 135 this.$axios({
136 136 method:"delete",
137   - url:`/api/platform/catalog/del`,
  137 + url:`./api/platform/catalog/del`,
138 138 params: {
139 139 id: id,
140 140 platformId: this.platformId,
... ... @@ -156,7 +156,7 @@ export default {
156 156 setDefaultCatalog: function (id){
157 157 this.$axios({
158 158 method:"post",
159   - url:`/api/platform/catalog/default/update`,
  159 + url:`./api/platform/catalog/default/update`,
160 160 params: {
161 161 platformId: this.platformId,
162 162 catalogId: id,
... ... @@ -201,7 +201,7 @@ export default {
201 201 onClick: () => {
202 202 this.$axios({
203 203 method:"delete",
204   - url:"/api/platform/catalog/relation/del",
  204 + url:"./api/platform/catalog/relation/del",
205 205 data: data
206 206 }).then((res)=>{
207 207 console.log("移除成功")
... ...
web_src/src/components/dialog/chooseChannelForGb.vue
... ... @@ -121,7 +121,7 @@ export default {
121 121 this.getCatalogFromUser((catalogId)=> {
122 122 this.$axios({
123 123 method:"post",
124   - url:"/api/platform/update_channel_for_gb",
  124 + url:"./api/platform/update_channel_for_gb",
125 125 data:{
126 126 platformId: this.platformId,
127 127 all: all,
... ... @@ -149,7 +149,7 @@ export default {
149 149  
150 150 this.$axios({
151 151 method:"delete",
152   - url:"/api/platform/del_channel_for_gb",
  152 + url:"./api/platform/del_channel_for_gb",
153 153 data:{
154 154 platformId: this.platformId,
155 155 all: all,
... ... @@ -248,7 +248,7 @@ export default {
248 248  
249 249 this.$axios({
250 250 method:"get",
251   - url:`/api/platform/channel_list`,
  251 + url:`./api/platform/channel_list`,
252 252 params: {
253 253 page: that.currentPage,
254 254 count: that.count,
... ... @@ -290,7 +290,7 @@ export default {
290 290 }).then(() => {
291 291 this.$axios({
292 292 method:"delete",
293   - url:"/api/platform/del_channel_for_gb",
  293 + url:"./api/platform/del_channel_for_gb",
294 294 data:{
295 295 platformId: this.platformId,
296 296 channelReduces: this.multipleSelection
... ... @@ -310,7 +310,7 @@ export default {
310 310  
311 311 this.$axios({
312 312 method: "post",
313   - url: "/api/platform/update_channel_for_gb",
  313 + url: "./api/platform/update_channel_for_gb",
314 314 data: {
315 315 platformId: this.platformId,
316 316 channelReduces: this.multipleSelection,
... ...
web_src/src/components/dialog/chooseChannelForStream.vue
... ... @@ -134,7 +134,7 @@ export default {
134 134 this.getCatalogFromUser((catalogId)=>{
135 135 this.$axios({
136 136 method:"post",
137   - url:"/api/gbStream/add",
  137 + url:"./api/gbStream/add",
138 138 data:{
139 139 platformId: this.platformId,
140 140 catalogId: catalogId,
... ... @@ -163,7 +163,7 @@ export default {
163 163  
164 164 this.$axios({
165 165 method:"delete",
166   - url:"/api/gbStream/del",
  166 + url:"./api/gbStream/del",
167 167 data:{
168 168 platformId: this.platformId,
169 169 all: all,
... ... @@ -186,7 +186,7 @@ export default {
186 186  
187 187 this.$axios({
188 188 method: 'get',
189   - url:`/api/gbStream/list`,
  189 + url:`./api/gbStream/list`,
190 190 params: {
191 191 page: that.currentPage,
192 192 count: that.count,
... ... @@ -222,7 +222,7 @@ export default {
222 222 }).then(() => {
223 223 this.$axios({
224 224 method:"delete",
225   - url:"/api/gbStream/del",
  225 + url:"./api/gbStream/del",
226 226 data:{
227 227 platformId: this.platformId,
228 228 gbStreams: this.multipleSelection,
... ... @@ -242,7 +242,7 @@ export default {
242 242 this.getCatalogFromUser((catalogId)=>{
243 243 this.$axios({
244 244 method:"post",
245   - url:"/api/gbStream/add",
  245 + url:"./api/gbStream/add",
246 246 data:{
247 247 platformId: this.platformId,
248 248 catalogId: catalogId,
... ...
web_src/src/components/dialog/deviceEdit.vue
... ... @@ -134,7 +134,7 @@ export default {
134 134 this.form.mobilePositionSubmissionInterval = this.form.mobilePositionSubmissionInterval||0
135 135 this.$axios({
136 136 method: 'post',
137   - url:`/api/device/query/device/${this.isEdit?'update':'add'}/`,
  137 + url:`./api/device/query/device/${this.isEdit?'update':'add'}/`,
138 138 params: this.form
139 139 }).then((res) => {
140 140 console.log(res.data)
... ...
web_src/src/components/dialog/getCatalog.vue
... ... @@ -89,7 +89,7 @@ export default {
89 89 let that = this;
90 90 this.$axios({
91 91 method:"get",
92   - url:`/api/platform/catalog`,
  92 + url:`./api/platform/catalog`,
93 93 params: {
94 94 platformId: that.platformId,
95 95 parentId: parentId
... ... @@ -111,7 +111,7 @@ export default {
111 111 if (node.level === 0) {
112 112 this.$axios({
113 113 method:"get",
114   - url:`/api/platform/info/` + this.platformId,
  114 + url:`./api/platform/info/` + this.platformId,
115 115 })
116 116 .then((res)=> {
117 117 if (res.data.code === 0) {
... ...
web_src/src/components/dialog/importChannel.vue
... ... @@ -60,7 +60,7 @@ export default {
60 60 console.log(this.form);
61 61 this.$axios({
62 62 method:"post",
63   - url:`/api/platform/catalog/${!this.isEdit? "add":"edit"}`,
  63 + url:`./api/platform/catalog/${!this.isEdit? "add":"edit"}`,
64 64 data: this.form
65 65 })
66 66 .then((res)=> {
... ...
web_src/src/components/dialog/onvifEdit.vue
... ... @@ -81,7 +81,7 @@ export default {
81 81 console.log(this.form);
82 82 this.$axios({
83 83 method: 'get',
84   - url:`api/onvif/rtsp`,
  84 + url:`./api/onvif/rtsp`,
85 85 params: {
86 86 hostname: this.form.hostName,
87 87 timeout: 3000,
... ...
web_src/src/components/dialog/platformEdit.vue
... ... @@ -138,7 +138,7 @@ export default {
138 138 showDialog: false,
139 139 isLoging: false,
140 140 onSubmit_text: "立即创建",
141   - saveUrl: "/api/platform/save",
  141 + saveUrl: "./api/platform/save",
142 142  
143 143 platform: {
144 144 id: null,
... ... @@ -192,7 +192,7 @@ export default {
192 192 this.saveUrl = "/api/platform/add";
193 193 this.$axios({
194 194 method: 'get',
195   - url:`/api/platform/server_config`
  195 + url:`./api/platform/server_config`
196 196 }).then(function (res) {
197 197 console.log(res);
198 198 if (res.data.code === 0) {
... ... @@ -315,7 +315,7 @@ export default {
315 315 var that = this;
316 316 await that.$axios({
317 317 method: 'get',
318   - url:`/api/platform/exit/${deviceGbId}`})
  318 + url:`./api/platform/exit/${deviceGbId}`})
319 319 .then(function (res) {
320 320 if (res.data.code === 0) {
321 321 result = res.data.data;
... ...
web_src/src/components/dialog/pushStreamEdit.vue
... ... @@ -109,7 +109,7 @@ export default {
109 109 if (this.edit) {
110 110 this.$axios({
111 111 method:"post",
112   - url:`/api/push/save_to_gb`,
  112 + url:`./api/push/save_to_gb`,
113 113 data: this.proxyParam
114 114 }).then( (res) => {
115 115 if (res.data.code === 0) {
... ... @@ -129,7 +129,7 @@ export default {
129 129 }else {
130 130 this.$axios({
131 131 method:"post",
132   - url:`/api/push/add`,
  132 + url:`./api/push/add`,
133 133 data: this.proxyParam
134 134 }).then( (res) => {
135 135 if (res.data.code === 0) {
... ... @@ -159,7 +159,7 @@ export default {
159 159 var that = this;
160 160 await that.$axios({
161 161 method:"get",
162   - url:`/api/platform/exit/${deviceGbId}`
  162 + url:`./api/platform/exit/${deviceGbId}`
163 163 }).then(function (res) {
164 164 result = res.data;
165 165 }).catch(function (error) {
... ...
web_src/src/components/dialog/queryTrace.vue
... ... @@ -72,7 +72,7 @@ export default {
72 72 onSubmit: function () {
73 73 console.log("onSubmit");
74 74 this.isLoging = true;
75   - let url = `/api/position/history/${this.channel.deviceId}?start=${this.searchFrom}&end=${this.searchTo}`;
  75 + let url = `./api/position/history/${this.channel.deviceId}?start=${this.searchFrom}&end=${this.searchTo}`;
76 76 if (this.channel.channelId) {
77 77 url+="&channelId=${this.channel.channelId}"
78 78 }
... ...
web_src/src/components/dialog/recordDownload.vue
... ... @@ -71,7 +71,7 @@ export default {
71 71 getProgress: function (callback){
72 72 this.$axios({
73 73 method: 'get',
74   - url: `/api/gb_record/download/progress/${this.deviceId}/${this.channelId}/${this.stream}`
  74 + url: `./api/gb_record/download/progress/${this.deviceId}/${this.channelId}/${this.stream}`
75 75 }).then((res)=> {
76 76 console.log(res)
77 77 if (res.data.code === 0) {
... ... @@ -124,7 +124,7 @@ export default {
124 124 stopDownloadRecord: function (callback) {
125 125 this.$axios({
126 126 method: 'get',
127   - url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.stream
  127 + url: './api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.stream
128 128 }).then((res)=> {
129 129 if (callback) callback(res)
130 130 });
... ... @@ -132,7 +132,7 @@ export default {
132 132 getFileDownload: function (){
133 133 this.$axios({
134 134 method: 'get',
135   - url:`/record_proxy/${this.mediaServerId}/api/record/file/download/task/add`,
  135 + url:`./record_proxy/${this.mediaServerId}/api/record/file/download/task/add`,
136 136 params: {
137 137 app: this.app,
138 138 stream: this.stream,
... ... @@ -164,7 +164,7 @@ export default {
164 164 getProgressForFile: function (callback){
165 165 this.$axios({
166 166 method: 'get',
167   - url:`/record_proxy/${this.mediaServerId}/api/record/file/download/task/list`,
  167 + url:`./record_proxy/${this.mediaServerId}/api/record/file/download/task/list`,
168 168 params: {
169 169 app: this.app,
170 170 stream: this.stream,
... ...
web_src/src/components/live.vue
... ... @@ -135,7 +135,7 @@ export default {
135 135 this.loading = true
136 136 this.$axios({
137 137 method: 'get',
138   - url: '/api/play/start/' + deviceId + '/' + channelId
  138 + url: './api/play/start/' + deviceId + '/' + channelId
139 139 }).then(function (res) {
140 140 if (res.data.code === 0 && res.data.data) {
141 141 let videoUrl;
... ...
web_src/src/components/map.vue
... ... @@ -298,7 +298,7 @@ export default {
298 298 let that = this;
299 299 this.$axios({
300 300 method: 'get',
301   - url: '/api/play/start/' + deviceId + '/' + channelId
  301 + url: './api/play/start/' + deviceId + '/' + channelId
302 302 }).then(function (res) {
303 303 that.isLoging = false;
304 304 if (res.data.code === 0) {
... ...
web_src/src/components/service/DeviceService.js
... ... @@ -9,7 +9,7 @@ class DeviceService{
9 9 getDeviceList(currentPage, count, callback, errorCallback){
10 10 this.$axios({
11 11 method: 'get',
12   - url:`/api/device/query/devices`,
  12 + url:`./api/device/query/devices`,
13 13 params: {
14 14 page: currentPage,
15 15 count: count
... ... @@ -25,7 +25,7 @@ class DeviceService{
25 25 getDevice(deviceId, callback, errorCallback){
26 26 this.$axios({
27 27 method: 'get',
28   - url:`/api/device/query/devices/${deviceId}`,
  28 + url:`./api/device/query/devices/${deviceId}`,
29 29 }).then((res) => {
30 30 if (typeof (callback) == "function") callback(res.data)
31 31 }).catch((error) => {
... ... @@ -82,7 +82,7 @@ class DeviceService{
82 82 getChanel(isCatalog, catalogUnderDevice, deviceId, currentPage, count, callback, errorCallback) {
83 83 this.$axios({
84 84 method: 'get',
85   - url: `/api/device/query/devices/${deviceId}/channels`,
  85 + url: `./api/device/query/devices/${deviceId}/channels`,
86 86 params:{
87 87 page: currentPage,
88 88 count: count,
... ... @@ -121,7 +121,7 @@ class DeviceService{
121 121 getSubChannel(isCatalog, deviceId, channelId, currentPage, count, callback, errorCallback) {
122 122 this.$axios({
123 123 method: 'get',
124   - url: `/api/device/query/sub_channels/${deviceId}/${channelId}/channels`,
  124 + url: `./api/device/query/sub_channels/${deviceId}/${channelId}/channels`,
125 125 params:{
126 126 page: currentPage,
127 127 count: count,
... ... @@ -161,7 +161,7 @@ class DeviceService{
161 161 }
162 162 this.$axios({
163 163 method: 'get',
164   - url: `/api/device/query/tree/${deviceId}`,
  164 + url: `./api/device/query/tree/${deviceId}`,
165 165 params:{
166 166 page: currentPage,
167 167 count: count,
... ...
web_src/src/components/service/MediaServer.js
... ... @@ -9,7 +9,7 @@ class MediaServer{
9 9 getOnlineMediaServerList(callback){
10 10 this.$axios({
11 11 method: 'get',
12   - url:`/api/server/media_server/online/list`,
  12 + url:`./api/server/media_server/online/list`,
13 13 }).then((res) => {
14 14 if (typeof (callback) == "function") callback(res.data)
15 15 }).catch((error) => {
... ... @@ -19,7 +19,7 @@ class MediaServer{
19 19 getMediaServerList(callback){
20 20 this.$axios({
21 21 method: 'get',
22   - url:`/api/server/media_server/list`,
  22 + url:`./api/server/media_server/list`,
23 23 }).then(function (res) {
24 24 if (typeof (callback) == "function") callback(res.data)
25 25 }).catch(function (error) {
... ... @@ -30,7 +30,7 @@ class MediaServer{
30 30 getMediaServer(id, callback){
31 31 this.$axios({
32 32 method: 'get',
33   - url:`/api/server/media_server/one/` + id,
  33 + url:`./api/server/media_server/one/` + id,
34 34 }).then(function (res) {
35 35 if (typeof (callback) == "function") callback(res.data)
36 36 }).catch(function (error) {
... ... @@ -41,7 +41,7 @@ class MediaServer{
41 41 checkServer(param, callback){
42 42 this.$axios({
43 43 method: 'get',
44   - url:`/api/server/media_server/check`,
  44 + url:`./api/server/media_server/check`,
45 45 params: {
46 46 ip: param.ip,
47 47 port: param.httpPort,
... ... @@ -57,7 +57,7 @@ class MediaServer{
57 57 checkRecordServer(param, callback){
58 58 this.$axios({
59 59 method: 'get',
60   - url:`/api/server/media_server/record/check`,
  60 + url:`./api/server/media_server/record/check`,
61 61 params: {
62 62 ip: param.ip,
63 63 port: param.recordAssistPort
... ... @@ -72,7 +72,7 @@ class MediaServer{
72 72 addServer(param, callback){
73 73 this.$axios({
74 74 method: 'post',
75   - url:`/api/server/media_server/save`,
  75 + url:`./api/server/media_server/save`,
76 76 data: param
77 77 }).then(function (res) {
78 78 if (typeof (callback) == "function") callback(res.data)
... ... @@ -84,7 +84,7 @@ class MediaServer{
84 84 delete(id, callback) {
85 85 this.$axios({
86 86 method: 'delete',
87   - url:`/api/server/media_server/delete`,
  87 + url:`./api/server/media_server/delete`,
88 88 params: {
89 89 id: id
90 90 }
... ...