Commit f2c6210539cfc5f423a773b4d897c37a9066f821

Authored by 648540858
2 parents bb49f1f0 b010e63d

Merge branch 'wvp-28181-2.0'

# Conflicts:
#	doc/_content/introduction/deployment.md
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
#	src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java
#	src/main/resources/all-application.yml
#	web_src/src/components/dialog/devicePlayer.vue
Showing 95 changed files with 2224 additions and 1089 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
... ... @@ -51,11 +51,11 @@ public interface ISIPCommanderForPlatform {
51 51 /**
52 52 * 向上级回复DeviceInfo查询信息
53 53 * @param parentPlatform 平台信息
54   - * @param sn
55   - * @param fromTag
  54 + * @param sn SN
  55 + * @param fromTag FROM头的tag信息
56 56 * @return
57 57 */
58   - void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException;
  58 + void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException;
59 59  
60 60 /**
61 61 * 向上级回复DeviceStatus查询信息
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
... ... @@ -32,6 +32,7 @@ import org.springframework.beans.factory.annotation.Autowired;
32 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
1 1 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
2 2  
3 3 import com.alibaba.fastjson2.JSON;
  4 +import com.genersoft.iot.vmp.conf.DynamicTask;
4 5 import com.genersoft.iot.vmp.gb28181.SipLayer;
5 6 import com.genersoft.iot.vmp.gb28181.bean.*;
6 7 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
... ... @@ -61,6 +62,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
61 62 @Autowired
62 63 private SIPSender sipSender;
63 64  
  65 + @Autowired
  66 + private DynamicTask dynamicTask;
  67 +
64 68 @Override
65 69 public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
66 70 register(parentPlatform, null, null, errorEvent, okEvent, false, true);
... ... @@ -109,13 +113,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
109 113 public String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException {
110 114 String characterSet = parentPlatform.getCharacterSet();
111 115 StringBuffer keepaliveXml = new StringBuffer(200);
112   - keepaliveXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
113   - keepaliveXml.append("<Notify>\r\n");
114   - keepaliveXml.append("<CmdType>Keepalive</CmdType>\r\n");
115   - keepaliveXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
116   - keepaliveXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
117   - keepaliveXml.append("<Status>OK</Status>\r\n");
118   - keepaliveXml.append("</Notify>\r\n");
  116 + keepaliveXml.append("<?xml version=\"1.0\" encoding=\"")
  117 + .append(characterSet).append("\"?>\r\n")
  118 + .append("<Notify>\r\n")
  119 + .append("<CmdType>Keepalive</CmdType>\r\n")
  120 + .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
  121 + .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
  122 + .append("<Status>OK</Status>\r\n")
  123 + .append("</Notify>\r\n");
119 124  
120 125 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
121 126  
... ... @@ -133,7 +138,6 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
133 138 * 向上级回复通道信息
134 139 * @param channel 通道信息
135 140 * @param parentPlatform 平台信息
136   - * @return
137 141 */
138 142 @Override
139 143 public void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException {
... ... @@ -160,18 +164,18 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
160 164 if ( parentPlatform ==null) {
161 165 return ;
162 166 }
163   - sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0);
  167 + sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0, true);
164 168 }
165 169 private String getCatalogXml(List<DeviceChannel> channels, String sn, ParentPlatform parentPlatform, int size) {
166 170 String characterSet = parentPlatform.getCharacterSet();
167 171 StringBuffer catalogXml = new StringBuffer(600);
168   - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet +"\"?>\r\n");
169   - catalogXml.append("<Response>\r\n");
170   - catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
171   - catalogXml.append("<SN>" +sn + "</SN>\r\n");
172   - catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
173   - catalogXml.append("<SumNum>" + size + "</SumNum>\r\n");
174   - catalogXml.append("<DeviceList Num=\"" + channels.size() +"\">\r\n");
  172 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet +"\"?>\r\n")
  173 + .append("<Response>\r\n")
  174 + .append("<CmdType>Catalog</CmdType>\r\n")
  175 + .append("<SN>" +sn + "</SN>\r\n")
  176 + .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
  177 + .append("<SumNum>" + size + "</SumNum>\r\n")
  178 + .append("<DeviceList Num=\"" + channels.size() +"\">\r\n");
175 179 if (channels.size() > 0) {
176 180 for (DeviceChannel channel : channels) {
177 181 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
... ... @@ -222,7 +226,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
222 226 return catalogXml.toString();
223 227 }
224 228  
225   - private void sendCatalogResponse(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag, int index) throws SipException, InvalidArgumentException, ParseException {
  229 + private void sendCatalogResponse(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag, int index, boolean sendAfterResponse) throws SipException, InvalidArgumentException, ParseException {
226 230 if (index >= channels.size()) {
227 231 return;
228 232 }
... ... @@ -236,15 +240,49 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
236 240 // callid
237 241 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
238 242  
239   - Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);
240   - sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, eventResult -> {
241   - int indexNext = index + parentPlatform.getCatalogGroup();
242   - try {
243   - sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext);
244   - } catch (SipException | InvalidArgumentException | ParseException e) {
245   - logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
246   - }
247   - });
  243 + SIPRequest request = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);
  244 +
  245 + String timeoutTaskKey = "catalog_task_" + parentPlatform.getServerGBId() + sn;
  246 +
  247 + String callId = request.getCallIdHeader().getCallId();
  248 +
  249 + if (sendAfterResponse) {
  250 + // 默认按照收到200回复后发送下一条, 如果超时收不到回复,就以30毫秒的间隔直接发送。
  251 + dynamicTask.startDelay(timeoutTaskKey, ()->{
  252 + sipSubscribe.removeOkSubscribe(callId);
  253 + int indexNext = index + parentPlatform.getCatalogGroup();
  254 + try {
  255 + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false);
  256 + } catch (SipException | InvalidArgumentException | ParseException e) {
  257 + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
  258 + }
  259 + }, 3000);
  260 + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> {
  261 + logger.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg);
  262 + dynamicTask.stop(timeoutTaskKey);
  263 + }, eventResult -> {
  264 + dynamicTask.stop(timeoutTaskKey);
  265 + int indexNext = index + parentPlatform.getCatalogGroup();
  266 + try {
  267 + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, true);
  268 + } catch (SipException | InvalidArgumentException | ParseException e) {
  269 + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
  270 + }
  271 + });
  272 + }else {
  273 + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> {
  274 + logger.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg);
  275 + dynamicTask.stop(timeoutTaskKey);
  276 + }, null);
  277 + dynamicTask.startDelay(timeoutTaskKey, ()->{
  278 + int indexNext = index + parentPlatform.getCatalogGroup();
  279 + try {
  280 + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false);
  281 + } catch (SipException | InvalidArgumentException | ParseException e) {
  282 + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
  283 + }
  284 + }, 30);
  285 + }
248 286 }
249 287  
250 288 /**
... ... @@ -255,7 +293,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
255 293 * @return
256 294 */
257 295 @Override
258   - public void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException {
  296 + public void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException {
259 297 if (parentPlatform == null) {
260 298 return;
261 299 }
... ... @@ -265,11 +303,11 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
265 303 deviceInfoXml.append("<Response>\r\n");
266 304 deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
267 305 deviceInfoXml.append("<SN>" +sn + "</SN>\r\n");
268   - deviceInfoXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
269   - deviceInfoXml.append("<DeviceName>" + parentPlatform.getName() + "</DeviceName>\r\n");
270   - deviceInfoXml.append("<Manufacturer>wvp</Manufacturer>\r\n");
271   - deviceInfoXml.append("<Model>wvp-28181-2.0</Model>\r\n");
272   - deviceInfoXml.append("<Firmware>2.0.202107</Firmware>\r\n");
  306 + deviceInfoXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  307 + deviceInfoXml.append("<DeviceName>" + device.getName() + "</DeviceName>\r\n");
  308 + deviceInfoXml.append("<Manufacturer>" + device.getManufacturer() + "</Manufacturer>\r\n");
  309 + deviceInfoXml.append("<Model>" + device.getModel() + "</Model>\r\n");
  310 + deviceInfoXml.append("<Firmware>" + device.getFirmware() + "</Firmware>\r\n");
273 311 deviceInfoXml.append("<Result>OK</Result>\r\n");
274 312 deviceInfoXml.append("</Response>\r\n");
275 313  
... ... @@ -294,15 +332,15 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
294 332 String statusStr = (status==1)?"ONLINE":"OFFLINE";
295 333 String characterSet = parentPlatform.getCharacterSet();
296 334 StringBuffer deviceStatusXml = new StringBuffer(600);
297   - deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
298   - deviceStatusXml.append("<Response>\r\n");
299   - deviceStatusXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
300   - deviceStatusXml.append("<SN>" +sn + "</SN>\r\n");
301   - deviceStatusXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
302   - deviceStatusXml.append("<Result>OK</Result>\r\n");
303   - deviceStatusXml.append("<Online>"+statusStr+"</Online>\r\n");
304   - deviceStatusXml.append("<Status>OK</Status>\r\n");
305   - deviceStatusXml.append("</Response>\r\n");
  335 + deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  336 + .append("<Response>\r\n")
  337 + .append("<CmdType>DeviceStatus</CmdType>\r\n")
  338 + .append("<SN>" +sn + "</SN>\r\n")
  339 + .append("<DeviceID>" + channelId + "</DeviceID>\r\n")
  340 + .append("<Result>OK</Result>\r\n")
  341 + .append("<Online>"+statusStr+"</Online>\r\n")
  342 + .append("<Status>OK</Status>\r\n")
  343 + .append("</Response>\r\n");
306 344  
307 345 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
308 346  
... ... @@ -321,18 +359,18 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
321 359  
322 360 String characterSet = parentPlatform.getCharacterSet();
323 361 StringBuffer deviceStatusXml = new StringBuffer(600);
324   - deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
325   - deviceStatusXml.append("<Notify>\r\n");
326   - deviceStatusXml.append("<CmdType>MobilePosition</CmdType>\r\n");
327   - deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
328   - deviceStatusXml.append("<DeviceID>" + gpsMsgInfo.getId() + "</DeviceID>\r\n");
329   - deviceStatusXml.append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n");
330   - deviceStatusXml.append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n");
331   - deviceStatusXml.append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n");
332   - deviceStatusXml.append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n");
333   - deviceStatusXml.append("<Direction>" + gpsMsgInfo.getDirection() + "</Direction>\r\n");
334   - deviceStatusXml.append("<Altitude>" + gpsMsgInfo.getAltitude() + "</Altitude>\r\n");
335   - deviceStatusXml.append("</Notify>\r\n");
  362 + deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  363 + .append("<Notify>\r\n")
  364 + .append("<CmdType>MobilePosition</CmdType>\r\n")
  365 + .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
  366 + .append("<DeviceID>" + gpsMsgInfo.getId() + "</DeviceID>\r\n")
  367 + .append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n")
  368 + .append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n")
  369 + .append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n")
  370 + .append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n")
  371 + .append("<Direction>" + gpsMsgInfo.getDirection() + "</Direction>\r\n")
  372 + .append("<Altitude>" + gpsMsgInfo.getAltitude() + "</Altitude>\r\n")
  373 + .append("</Notify>\r\n");
336 374  
337 375 sendNotify(parentPlatform, deviceStatusXml.toString(), subscribeInfo, eventResult -> {
338 376 logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg);
... ... @@ -349,21 +387,21 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
349 387 deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm));
350 388 String characterSet = parentPlatform.getCharacterSet();
351 389 StringBuffer deviceStatusXml = new StringBuffer(600);
352   - deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
353   - deviceStatusXml.append("<Notify>\r\n");
354   - deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");
355   - deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
356   - deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
357   - deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
358   - deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
359   - deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
360   - deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
361   - deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
362   - deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
363   - deviceStatusXml.append("<info>\r\n");
364   - deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");
365   - deviceStatusXml.append("</info>\r\n");
366   - deviceStatusXml.append("</Notify>\r\n");
  390 + deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  391 + .append("<Notify>\r\n")
  392 + .append("<CmdType>Alarm</CmdType>\r\n")
  393 + .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
  394 + .append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n")
  395 + .append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n")
  396 + .append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n")
  397 + .append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n")
  398 + .append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n")
  399 + .append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n")
  400 + .append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n")
  401 + .append("<info>\r\n")
  402 + .append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n")
  403 + .append("</info>\r\n")
  404 + .append("</Notify>\r\n");
367 405  
368 406 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
369 407  
... ... @@ -422,13 +460,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
422 460 StringBuffer catalogXml = new StringBuffer(600);
423 461  
424 462 String characterSet = parentPlatform.getCharacterSet();
425   - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
426   - catalogXml.append("<Notify>\r\n");
427   - catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
428   - catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
429   - catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
430   - catalogXml.append("<SumNum>1</SumNum>\r\n");
431   - catalogXml.append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
  463 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  464 + .append("<Notify>\r\n")
  465 + .append("<CmdType>Catalog</CmdType>\r\n")
  466 + .append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
  467 + .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
  468 + .append("<SumNum>1</SumNum>\r\n")
  469 + .append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
432 470 if (channels.size() > 0) {
433 471 for (DeviceChannel channel : channels) {
434 472 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
... ... @@ -449,16 +487,16 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
449 487 catalogXml.append("<Parental>" + channel.getParental() + "</Parental>\r\n");
450 488 if (channel.getParental() == 0) {
451 489 // 通道项
452   - catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n");
453   - catalogXml.append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n");
454   - catalogXml.append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n");
455   - catalogXml.append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n");
  490 + catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n")
  491 + .append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n")
  492 + .append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n")
  493 + .append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n");
456 494  
457 495 if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性
458   - catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n");
459   - catalogXml.append("<Owner> " + channel.getOwner()+ "</Owner>\r\n");
460   - catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n");
461   - catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
  496 + catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n")
  497 + .append("<Owner> " + channel.getOwner()+ "</Owner>\r\n")
  498 + .append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n")
  499 + .append("<Address>" + channel.getAddress() + "</Address>\r\n");
462 500 }
463 501 if (!"presence".equals(subscribeInfo.getEventType())) {
464 502 catalogXml.append("<Event>" + type + "</Event>\r\n");
... ... @@ -468,8 +506,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
468 506 catalogXml.append("</Item>\r\n");
469 507 }
470 508 }
471   - catalogXml.append("</DeviceList>\r\n");
472   - catalogXml.append("</Notify>\r\n");
  509 + catalogXml.append("</DeviceList>\r\n")
  510 + .append("</Notify>\r\n");
473 511 return catalogXml.toString();
474 512 }
475 513  
... ... @@ -515,26 +553,26 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
515 553  
516 554 String characterSet = parentPlatform.getCharacterSet();
517 555 StringBuffer catalogXml = new StringBuffer(600);
518   - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
519   - catalogXml.append("<Notify>\r\n");
520   - catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
521   - catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
522   - catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
523   - catalogXml.append("<SumNum>1</SumNum>\r\n");
524   - catalogXml.append("<DeviceList Num=\" " + channels.size() + " \">\r\n");
  556 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  557 + .append("<Notify>\r\n")
  558 + .append("<CmdType>Catalog</CmdType>\r\n")
  559 + .append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
  560 + .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
  561 + .append("<SumNum>1</SumNum>\r\n")
  562 + .append("<DeviceList Num=\" " + channels.size() + " \">\r\n");
525 563 if (channels.size() > 0) {
526 564 for (DeviceChannel channel : channels) {
527 565 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
528 566 channel.setParentId(parentPlatform.getDeviceGBId());
529 567 }
530   - catalogXml.append("<Item>\r\n");
531   - catalogXml.append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n");
532   - catalogXml.append("<Event>" + type + "</Event>\r\n");
533   - catalogXml.append("</Item>\r\n");
  568 + catalogXml.append("<Item>\r\n")
  569 + .append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n")
  570 + .append("<Event>" + type + "</Event>\r\n")
  571 + .append("</Item>\r\n");
534 572 }
535 573 }
536   - catalogXml.append("</DeviceList>\r\n");
537   - catalogXml.append("</Notify>\r\n");
  574 + catalogXml.append("</DeviceList>\r\n")
  575 + .append("</Notify>\r\n");
538 576 return catalogXml.toString();
539 577 }
540 578 @Override
... ... @@ -544,12 +582,12 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
544 582 }
545 583 String characterSet = parentPlatform.getCharacterSet();
546 584 StringBuffer recordXml = new StringBuffer(600);
547   - recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
548   - recordXml.append("<Response>\r\n");
549   - recordXml.append("<CmdType>RecordInfo</CmdType>\r\n");
550   - recordXml.append("<SN>" +recordInfo.getSn() + "</SN>\r\n");
551   - recordXml.append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n");
552   - recordXml.append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
  585 + recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  586 + .append("<Response>\r\n")
  587 + .append("<CmdType>RecordInfo</CmdType>\r\n")
  588 + .append("<SN>" +recordInfo.getSn() + "</SN>\r\n")
  589 + .append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n")
  590 + .append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
553 591 if (recordInfo.getRecordList() == null ) {
554 592 recordXml.append("<RecordList Num=\"0\">\r\n");
555 593 }else {
... ... @@ -558,12 +596,12 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
558 596 for (RecordItem recordItem : recordInfo.getRecordList()) {
559 597 recordXml.append("<Item>\r\n");
560 598 if (deviceChannel != null) {
561   - recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n");
562   - recordXml.append("<Name>" + recordItem.getName() + "</Name>\r\n");
563   - recordXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n");
564   - recordXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n");
565   - recordXml.append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n");
566   - recordXml.append("<Type>" + recordItem.getType() + "</Type>\r\n");
  599 + recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n")
  600 + .append("<Name>" + recordItem.getName() + "</Name>\r\n")
  601 + .append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n")
  602 + .append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n")
  603 + .append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n")
  604 + .append("<Type>" + recordItem.getType() + "</Type>\r\n");
567 605 if (!ObjectUtils.isEmpty(recordItem.getFileSize())) {
568 606 recordXml.append("<FileSize>" + recordItem.getFileSize() + "</FileSize>\r\n");
569 607 }
... ... @@ -576,8 +614,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
576 614 }
577 615 }
578 616  
579   - recordXml.append("</RecordList>\r\n");
580   - recordXml.append("</Response>\r\n");
  617 + recordXml.append("</RecordList>\r\n")
  618 + .append("</Response>\r\n");
581 619  
582 620 // callid
583 621 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
... ... @@ -596,13 +634,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
596 634  
597 635 String characterSet = parentPlatform.getCharacterSet();
598 636 StringBuffer mediaStatusXml = new StringBuffer(200);
599   - mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
600   - mediaStatusXml.append("<Notify>\r\n");
601   - mediaStatusXml.append("<CmdType>MediaStatus</CmdType>\r\n");
602   - mediaStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
603   - mediaStatusXml.append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n");
604   - mediaStatusXml.append("<NotifyType>121</NotifyType>\r\n");
605   - mediaStatusXml.append("</Notify>\r\n");
  637 + mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
  638 + .append("<Notify>\r\n")
  639 + .append("<CmdType>MediaStatus</CmdType>\r\n")
  640 + .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
  641 + .append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n")
  642 + .append("<NotifyType>121</NotifyType>\r\n")
  643 + .append("</Notify>\r\n");
606 644  
607 645 SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, mediaStatusXml.toString(),
608 646 sendRtpItem);
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
... ... @@ -16,7 +16,6 @@ import org.springframework.beans.factory.annotation.Autowired;
16 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,265 +66,243 @@ 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());
131   -
132   - JSONObject ret = new JSONObject();
133   - ret.put("code", 0);
134   - ret.put("msg", "success");
135   -
136   - return ret;
137   - }
138   -
139   - /**
140   - * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
141   - *
142   - */
143   - @ResponseBody
144   - @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
145   - public JSONObject onPlay(@RequestBody OnPlayHookParam param){
146   - if (logger.isDebugEnabled()) {
147   - logger.debug("[ZLM HOOK] 播放鉴权:{}->{}" + param.getMediaServerId(), param);
148   - }
149   - String mediaServerId = param.getMediaServerId();
150   -
151   - taskExecutor.execute(()->{
152   - JSONObject json = (JSONObject) JSON.toJSON(param);
153   - ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);
154   - if (subscribe != null ) {
155   - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
156   - if (mediaInfo != null) {
157   - subscribe.response(mediaInfo, json);
158   - }
159   - }
160   - });
161   - JSONObject ret = new JSONObject();
162   - if (!"rtp".equals(param.getApp())) {
163   - Map<String, String> paramMap = urlParamToMap(param.getParams());
164   - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
165   - if (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(paramMap.get("callId"))) {
166   - ret.put("code", 401);
167   - ret.put("msg", "Unauthorized");
168   - return ret;
169   - }
170   - }
171   -
172   - ret.put("code", 0);
173   - ret.put("msg", "success");
174   - return ret;
175   - }
176   -
177   - /**
178   - * rtsp/rtmp/rtp推流鉴权事件。
179   - *
180   - */
181   - @ResponseBody
182   - @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
183   - public JSONObject onPublish(@RequestBody OnPublishHookParam param) {
184   -
185   - JSONObject json = (JSONObject) JSON.toJSON(param);
186   -
187   - logger.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param);
188   - JSONObject ret = new JSONObject();
189   - String mediaServerId = json.getString("mediaServerId");
190   - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
191   -
192   - if (!"rtp".equals(param.getApp())) {
193   - if (userSetting.getPushAuthority()) {
194   - // 推流鉴权
195   - if (param.getParams() == null) {
196   - logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
197   - ret.put("code", 401);
198   - ret.put("msg", "Unauthorized");
199   - return ret;
200   - }
201   - Map<String, String> paramMap = urlParamToMap(param.getParams());
202   - String sign = paramMap.get("sign");
203   - if (sign == null) {
204   - logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
205   - ret.put("code", 401);
206   - ret.put("msg", "Unauthorized");
207   - return ret;
208   - }
209   - // 推流自定义播放鉴权码
210   - String callId = paramMap.get("callId");
211   - // 鉴权配置
212   - boolean hasAuthority = userService.checkPushAuthority(callId, sign);
213   - if (!hasAuthority) {
214   - logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign);
215   - ret.put("code", 401);
216   - ret.put("msg", "Unauthorized");
217   - return ret;
218   - }
219   - StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
220   - streamAuthorityInfo.setCallId(callId);
221   - streamAuthorityInfo.setSign(sign);
222   - // 鉴权通过
223   - redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
224   - // 通知assist新的callId
225   - if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) {
226   - taskExecutor.execute(()->{
227   - assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);
228   - });
229   - }
230   - }
231   - }else {
232   - zlmMediaListManager.sendStreamEvent(param.getApp(),param.getStream(), param.getMediaServerId());
233   - }
234   -
235   - ret.put("code", 0);
236   - ret.put("msg", "success");
237   -
238   - if (!"rtp".equals(param.getApp())) {
239   - ret.put("enable_audio", true);
240   - }
241   -
242   - taskExecutor.execute(()->{
243   - ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
244   - if (subscribe != null) {
245   - if (mediaInfo != null) {
246   - subscribe.response(mediaInfo, json);
247   - }else {
248   - ret.put("code", 1);
249   - ret.put("msg", "zlm not register");
250   - }
251   - }
252   - });
253   -
254   - if ("rtp".equals(param.getApp())) {
255   - ret.put("enable_mp4", userSetting.getRecordSip());
256   - }else {
257   - ret.put("enable_mp4", userSetting.isRecordPushLive());
258   - }
259   - List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
260   - if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
261   - String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
262   - String channelId = ssrcTransactionForAll.get(0).getChannelId();
263   - DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
264   - if (deviceChannel != null) {
265   - ret.put("enable_audio", deviceChannel.isHasAudio());
266   - }
267   - // 如果是录像下载就设置视频间隔十秒
268   - if (ssrcTransactionForAll.get(0).getType() == VideoStreamSessionManager.SessionType.download) {
269   - ret.put("mp4_max_second", 10);
270   - ret.put("enable_mp4", true);
271   - ret.put("enable_audio", true);
272   - }
273   - }
274   - return ret;
275   - }
276   -
277   - /**
278   - * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
279   - *
280   - */
281   - @ResponseBody
282   - @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
283   - public JSONObject onStreamChanged(@RequestBody OnStreamChangedHookParam param){
284   -
285   - if (param.isRegist()) {
286   - logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
287   - }else {
288   - logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
289   - }
290   -
291   -
292   - JSONObject json = (JSONObject) JSON.toJSON(param);
293   - taskExecutor.execute(()-> {
294   - ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
295   - if (subscribe != null) {
296   - MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
297   - if (mediaInfo != null) {
298   - subscribe.response(mediaInfo, json);
299   - }
300   - }
301   - // 流消失移除redis play
302   - List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
303   - if (param.isRegist()) {
304   - if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
305   - || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
306   - || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
307   -
308   - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
309   - if (streamAuthorityInfo == null) {
310   - streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
311   - } else {
312   - streamAuthorityInfo.setOriginType(param.getOriginType());
313   - streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
314   - }
315   - redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
316   - }
317   - } else {
318   - redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
319   - }
320   - });
  69 + @Autowired
  70 + private IPlayService playService;
  71 +
  72 + @Autowired
  73 + private IVideoManagerStorage storager;
  74 +
  75 + @Autowired
  76 + private IRedisCatchStorage redisCatchStorage;
  77 +
  78 + @Autowired
  79 + private IDeviceService deviceService;
  80 +
  81 + @Autowired
  82 + private IMediaServerService mediaServerService;
  83 +
  84 + @Autowired
  85 + private IStreamProxyService streamProxyService;
  86 +
  87 + @Autowired
  88 + private DeferredResultHolder resultHolder;
  89 +
  90 + @Autowired
  91 + private IMediaService mediaService;
  92 +
  93 + @Autowired
  94 + private EventPublisher eventPublisher;
  95 +
  96 + @Autowired
  97 + private ZLMMediaListManager zlmMediaListManager;
  98 +
  99 + @Autowired
  100 + private ZlmHttpHookSubscribe subscribe;
  101 +
  102 + @Autowired
  103 + private UserSetting userSetting;
  104 +
  105 + @Autowired
  106 + private IUserService userService;
  107 +
  108 + @Autowired
  109 + private VideoStreamSessionManager sessionManager;
  110 +
  111 + @Autowired
  112 + private AssistRESTfulUtils assistRESTfulUtils;
  113 +
  114 + @Qualifier("taskExecutor")
  115 + @Autowired
  116 + private ThreadPoolTaskExecutor taskExecutor;
  117 +
  118 + /**
  119 + * 服务器定时上报时间,上报间隔可配置,默认10s上报一次
  120 + */
  121 + @ResponseBody
  122 + @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
  123 + public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) {
  124 +
  125 + logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId());
  126 +
  127 + taskExecutor.execute(() -> {
  128 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
  129 + JSONObject json = (JSONObject) JSON.toJSON(param);
  130 + if (subscribes != null && subscribes.size() > 0) {
  131 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  132 + subscribe.response(null, json);
  133 + }
  134 + }
  135 + });
  136 + mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData());
  137 +
  138 + return HookResult.SUCCESS();
  139 + }
  140 +
  141 + /**
  142 + * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
  143 + */
  144 + @ResponseBody
  145 + @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
  146 + public HookResult onPlay(@RequestBody OnPlayHookParam param) {
  147 + if (logger.isDebugEnabled()) {
  148 + logger.debug("[ZLM HOOK] 播放鉴权:{}->{}" + param.getMediaServerId(), param);
  149 + }
  150 + String mediaServerId = param.getMediaServerId();
  151 +
  152 + taskExecutor.execute(() -> {
  153 + JSONObject json = (JSONObject) JSON.toJSON(param);
  154 + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);
  155 + if (subscribe != null) {
  156 + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
  157 + if (mediaInfo != null) {
  158 + subscribe.response(mediaInfo, json);
  159 + }
  160 + }
  161 + });
  162 + if (!"rtp".equals(param.getApp())) {
  163 + Map<String, String> paramMap = urlParamToMap(param.getParams());
  164 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
  165 + if (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(paramMap.get("callId"))) {
  166 + return new HookResult(401, "Unauthorized");
  167 + }
  168 + }
  169 +
  170 + return HookResult.SUCCESS();
  171 + }
  172 +
  173 + /**
  174 + * rtsp/rtmp/rtp推流鉴权事件。
  175 + */
  176 + @ResponseBody
  177 + @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
  178 + public HookResultForOnPublish onPublish(@RequestBody OnPublishHookParam param) {
  179 +
  180 + JSONObject json = (JSONObject) JSON.toJSON(param);
  181 +
  182 + logger.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param);
  183 +
  184 + String mediaServerId = json.getString("mediaServerId");
  185 + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
  186 +
  187 + if (!"rtp".equals(param.getApp())) {
  188 + if (userSetting.getPushAuthority()) {
  189 + // 推流鉴权
  190 + if (param.getParams() == null) {
  191 + logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
  192 + return new HookResultForOnPublish(401, "Unauthorized");
  193 + }
  194 + Map<String, String> paramMap = urlParamToMap(param.getParams());
  195 + String sign = paramMap.get("sign");
  196 + if (sign == null) {
  197 + logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
  198 + return new HookResultForOnPublish(401, "Unauthorized");
  199 + }
  200 + // 推流自定义播放鉴权码
  201 + String callId = paramMap.get("callId");
  202 + // 鉴权配置
  203 + boolean hasAuthority = userService.checkPushAuthority(callId, sign);
  204 + if (!hasAuthority) {
  205 + logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign);
  206 + return new HookResultForOnPublish(401, "Unauthorized");
  207 + }
  208 + StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
  209 + streamAuthorityInfo.setCallId(callId);
  210 + streamAuthorityInfo.setSign(sign);
  211 + // 鉴权通过
  212 + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
  213 + // 通知assist新的callId
  214 + if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) {
  215 + taskExecutor.execute(() -> {
  216 + assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);
  217 + });
  218 + }
  219 + }
  220 + } else {
  221 + zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
  222 + }
  223 +
  224 +
  225 + HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
  226 + if (!"rtp".equals(param.getApp())) {
  227 + result.setEnable_audio(true);
  228 + }
  229 +
  230 + taskExecutor.execute(() -> {
  231 + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
  232 + if (subscribe != null) {
  233 + if (mediaInfo != null) {
  234 + subscribe.response(mediaInfo, json);
  235 + } else {
  236 + new HookResultForOnPublish(1, "zlm not register");
  237 + }
  238 + }
  239 + });
  240 +
  241 + if ("rtp".equals(param.getApp())) {
  242 + result.setEnable_mp4(userSetting.getRecordSip());
  243 + } else {
  244 + result.setEnable_mp4(userSetting.isRecordPushLive());
  245 + }
  246 + List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
  247 + if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
  248 + String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
  249 + String channelId = ssrcTransactionForAll.get(0).getChannelId();
  250 + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
  251 + if (deviceChannel != null) {
  252 + result.setEnable_audio(deviceChannel.isHasAudio());
  253 + }
  254 + // 如果是录像下载就设置视频间隔十秒
  255 + if (ssrcTransactionForAll.get(0).getType() == VideoStreamSessionManager.SessionType.download) {
  256 + result.setMp4_max_second(10);
  257 + result.setEnable_audio(true);
  258 + result.setEnable_mp4(true);
  259 + }
  260 + }
  261 + return result;
  262 + }
  263 +
  264 + /**
  265 + * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
  266 + */
  267 + @ResponseBody
  268 + @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
  269 + public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) {
  270 +
  271 + if (param.isRegist()) {
  272 + logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  273 + } else {
  274 + logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  275 + }
  276 +
  277 +
  278 + JSONObject json = (JSONObject) JSON.toJSON(param);
  279 + taskExecutor.execute(() -> {
  280 + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
  281 + if (subscribe != null) {
  282 + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
  283 + if (mediaInfo != null) {
  284 + subscribe.response(mediaInfo, json);
  285 + }
  286 + }
  287 + // 流消失移除redis play
  288 + List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
  289 + if (param.isRegist()) {
  290 + if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
  291 + || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
  292 + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
  293 +
  294 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
  295 + if (streamAuthorityInfo == null) {
  296 + streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
  297 + } else {
  298 + streamAuthorityInfo.setOriginType(param.getOriginType());
  299 + streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
  300 + }
  301 + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
  302 + }
  303 + } else {
  304 + redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
  305 + }
321 306  
322 307 if ("rtsp".equals(param.getSchema())){
323 308 logger.info("流变化:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
... ... @@ -465,72 +450,57 @@ public class ZLMHttpHookListener {
465 450 GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
466 451 if (gbStream != null) {
467 452 // eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
468   - }
469   - zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
470   - }
471   - if (type != null) {
472   - // 发送流变化redis消息
473   - JSONObject jsonObject = new JSONObject();
474   - jsonObject.put("serverId", userSetting.getServerId());
475   - jsonObject.put("app", param.getApp());
476   - jsonObject.put("stream", param.getStream());
477   - jsonObject.put("register", param.isRegist());
478   - jsonObject.put("mediaServerId", param.getMediaServerId());
479   - redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
480   - }
481   - }
482   - }
483   - }
484   - if (!param.isRegist()) {
485   - List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
486   - if (sendRtpItems.size() > 0) {
487   - for (SendRtpItem sendRtpItem : sendRtpItems) {
488   - if (sendRtpItem.getApp().equals(param.getApp())) {
489   - String platformId = sendRtpItem.getPlatformId();
490   - ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
491   - Device device = deviceService.getDevice(platformId);
492   -
493   - try {
494   - if (platform != null) {
495   - commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
496   - }else {
497   - if (sendRtpItem.isOnlyAudio()) {
498   - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
499   - if (audioBroadcastCatch != null) {
500   -// playService.stopAudioBroadcast(device.getDeviceId(), sendRtpItem.getChannelId());
501   - if ("talk".equals(param.getApp())) {
502   -// cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
503   - }else {
504   -// cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
505   - }
506   - }
507   - }
508   -
509   -
510   -
511   - }
512   - } catch (SipException | InvalidArgumentException | ParseException e) {
513   - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
514   - }
515   - }
516   - }
517   - }
518   - }
519   - }
520   -
521   - JSONObject ret = new JSONObject();
522   - ret.put("code", 0);
523   - ret.put("msg", "success");
524   - return ret;
525   - }
526   -
527   - /**
528   - * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。
529   - *
530   - */
531   - @ResponseBody
532   - @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
533   - public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param){
  453 + }
  454 + zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
  455 + }
  456 + if (type != null) {
  457 + // 发送流变化redis消息
  458 + JSONObject jsonObject = new JSONObject();
  459 + jsonObject.put("serverId", userSetting.getServerId());
  460 + jsonObject.put("app", param.getApp());
  461 + jsonObject.put("stream", param.getStream());
  462 + jsonObject.put("register", param.isRegist());
  463 + jsonObject.put("mediaServerId", param.getMediaServerId());
  464 + redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
  465 + }
  466 + }
  467 + }
  468 + }
  469 + if (!param.isRegist()) {
  470 + List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
  471 + if (sendRtpItems.size() > 0) {
  472 + for (SendRtpItem sendRtpItem : sendRtpItems) {
  473 + if (sendRtpItem.getApp().equals(param.getApp())) {
  474 + String platformId = sendRtpItem.getPlatformId();
  475 + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
  476 + Device device = deviceService.getDevice(platformId);
  477 +
  478 + try {
  479 + if (platform != null) {
  480 + commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
  481 + } else {
  482 + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId());
  483 + }
  484 + } catch (SipException | InvalidArgumentException | ParseException |
  485 + SsrcTransactionNotFoundException e) {
  486 + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
  487 + }
  488 + }
  489 + }
  490 + }
  491 + }
  492 + }
  493 + });
  494 +
  495 + return HookResult.SUCCESS();
  496 + }
  497 +
  498 + /**
  499 + * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。
  500 + */
  501 + @ResponseBody
  502 + @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
  503 + public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) {
534 504  
535 505 logger.info("[ZLM HOOK]流无人观看:{]->{}->{}/{}" + param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
536 506 JSONObject ret = new JSONObject();
... ... @@ -571,215 +541,243 @@ public class ZLMHttpHookListener {
571 541 }
572 542 }
573 543  
574   - redisCatchStorage.stopPlay(streamInfoForPlayCatch);
575   - storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
576   - return ret;
577   - }
578   - // 录像回放
579   - StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, param.getStream(), null);
580   - if (streamInfoForPlayBackCatch != null ) {
581   - if (streamInfoForPlayBackCatch.isPause()) {
582   - ret.put("close", false);
583   - }else {
584   - Device device = deviceService.getDevice(streamInfoForPlayBackCatch.getDeviceID());
585   - if (device != null) {
586   - try {
587   - cmder.streamByeCmd(device,streamInfoForPlayBackCatch.getChannelId(),
588   - streamInfoForPlayBackCatch.getStream(), null);
589   - } catch (InvalidArgumentException | ParseException | SipException |
590   - SsrcTransactionNotFoundException e) {
591   - logger.error("[无人观看]回放, 发送BYE失败 {}", e.getMessage());
592   - }
593   - }
594   - redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),
595   - streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
596   - }
597   - return ret;
598   - }
599   - // 录像下载
600   - StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, param.getStream(), null);
601   - // 进行录像下载时无人观看不断流
602   - if (streamInfoForDownload != null) {
603   - ret.put("close", false);
604   - return ret;
605   - }
606   - }else {
607   - // 非国标流 推流/拉流代理
608   - // 拉流代理
609   - StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
610   - if (streamProxyItem != null ) {
611   - if (streamProxyItem.isEnable_remove_none_reader()) {
612   - // 无人观看自动移除
613   - ret.put("close", true);
614   - streamProxyService.del(param.getApp(), param.getStream());
615   - String url = streamProxyItem.getUrl() != null?streamProxyItem.getUrl():streamProxyItem.getSrc_url();
616   - logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url);
617   - }else if (streamProxyItem.isEnable_disable_none_reader()) {
618   - // 无人观看停用
619   - ret.put("close", true);
620   - // 修改数据
621   - streamProxyService.stop(param.getApp(), param.getStream());
622   - }else {
623   - // 无人观看不做处理
624   - ret.put("close", false);
625   - }
626   - return ret;
627   - }
628   - // 推流具有主动性,暂时不做处理
  544 + redisCatchStorage.stopPlay(streamInfoForPlayCatch);
  545 + storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
  546 + return ret;
  547 + }
  548 + // 录像回放
  549 + StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, param.getStream(), null);
  550 + if (streamInfoForPlayBackCatch != null) {
  551 + if (streamInfoForPlayBackCatch.isPause()) {
  552 + ret.put("close", false);
  553 + } else {
  554 + Device device = deviceService.getDevice(streamInfoForPlayBackCatch.getDeviceID());
  555 + if (device != null) {
  556 + try {
  557 + cmder.streamByeCmd(device, streamInfoForPlayBackCatch.getChannelId(),
  558 + streamInfoForPlayBackCatch.getStream(), null);
  559 + } catch (InvalidArgumentException | ParseException | SipException |
  560 + SsrcTransactionNotFoundException e) {
  561 + logger.error("[无人观看]回放, 发送BYE失败 {}", e.getMessage());
  562 + }
  563 + }
  564 + redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),
  565 + streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
  566 + }
  567 + return ret;
  568 + }
  569 + // 录像下载
  570 + StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, param.getStream(), null);
  571 + // 进行录像下载时无人观看不断流
  572 + if (streamInfoForDownload != null) {
  573 + ret.put("close", false);
  574 + return ret;
  575 + }
  576 + } else {
  577 + // 非国标流 推流/拉流代理
  578 + // 拉流代理
  579 + StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
  580 + if (streamProxyItem != null) {
  581 + if (streamProxyItem.isEnable_remove_none_reader()) {
  582 + // 无人观看自动移除
  583 + ret.put("close", true);
  584 + streamProxyService.del(param.getApp(), param.getStream());
  585 + String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrc_url();
  586 + logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url);
  587 + } else if (streamProxyItem.isEnable_disable_none_reader()) {
  588 + // 无人观看停用
  589 + ret.put("close", true);
  590 + // 修改数据
  591 + streamProxyService.stop(param.getApp(), param.getStream());
  592 + } else {
  593 + // 无人观看不做处理
  594 + ret.put("close", false);
  595 + }
  596 + return ret;
  597 + }
  598 + // 推流具有主动性,暂时不做处理
629 599 // StreamPushItem streamPushItem = streamPushService.getPush(app, streamId);
630 600 // if (streamPushItem != null) {
631 601 // // TODO 发送停止
632 602 //
633 603 // }
634   - }
635   - return ret;
636   - }
637   -
638   - /**
639   - * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。
640   - *
641   - */
642   - @ResponseBody
643   - @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
644   - public JSONObject onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param){
645   - logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
646   - taskExecutor.execute(()->{
647   - MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
648   - if (userSetting.isAutoApplyPlay() && mediaInfo != null) {
649   - if ("rtp".equals(param.getApp())) {
650   - if (mediaInfo.isRtpEnable()) {
651   - String[] s = param.getStream().split("_");
652   - if (s.length == 2) {
653   - String deviceId = s[0];
654   - String channelId = s[1];
655   - Device device = redisCatchStorage.getDevice(deviceId);
656   - if (device != null) {
657   - playService.play(mediaInfo,deviceId, channelId, null, null, null);
658   - }
659   - }
660   - }
661   - }else {
662   - // 拉流代理
663   - StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
664   - if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) {
665   - streamProxyService.start(param.getApp(), param.getStream());
666   - }
667   - }
668   - }
669   - });
670   -
671   -
672   - JSONObject ret = new JSONObject();
673   - ret.put("code", 0);
674   - ret.put("msg", "success");
675   - return ret;
676   - }
677   -
678   - /**
679   - * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。
680   - *
681   - */
682   - @ResponseBody
683   - @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
684   - public JSONObject onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject){
685   -
686   - jsonObject.put("ip", request.getRemoteAddr());
687   - ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
688   - zlmServerConfig.setIp(request.getRemoteAddr());
689   - logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
690   - taskExecutor.execute(()->{
691   - List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
692   - if (subscribes != null && subscribes.size() > 0) {
693   - for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
694   - subscribe.response(null, jsonObject);
695   - }
696   - }
697   - mediaServerService.zlmServerOnline(zlmServerConfig);
698   - });
699   -
700   - JSONObject ret = new JSONObject();
701   - ret.put("code", 0);
702   - ret.put("msg", "success");
703   - return ret;
704   - }
705   -
706   - /**
707   - * 发送rtp(startSendRtp)被动关闭时回调
708   - */
709   - @ResponseBody
710   - @PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8")
711   - public JSONObject onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param){
712   -
713   - logger.info("[ZLM HOOK] rtp发送关闭:{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream());
714   -
715   - JSONObject ret = new JSONObject();
716   - ret.put("code", 0);
717   - ret.put("msg", "success");
718   -
719   - // 查找对应的上级推流,发送停止
720   - if (!"rtp".equals(param.getApp())) {
721   - return ret;
722   - }
723   - taskExecutor.execute(()->{
724   - List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
725   - if (sendRtpItems.size() > 0) {
726   - for (SendRtpItem sendRtpItem : sendRtpItems) {
727   - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
728   - try {
729   - commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
730   - } catch (SipException | InvalidArgumentException | ParseException e) {
731   - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
732   - }
733   - redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
734   - sendRtpItem.getCallId(), sendRtpItem.getStreamId());
735   - }
736   - }
737   - });
738   -
739   -
740   - return ret;
741   - }
742   -
743   - /**
744   - * rtpServer收流超时
745   - */
746   - @ResponseBody
747   - @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
748   - public JSONObject onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param){
749   - logger.info("[ZLM HOOK] rtpServer rtp超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
750   -
751   - JSONObject ret = new JSONObject();
752   - ret.put("code", 0);
753   - ret.put("msg", "success");
754   -
755   - taskExecutor.execute(()->{
756   - JSONObject json = (JSONObject) JSON.toJSON(param);
757   - List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
758   - if (subscribes != null && subscribes.size() > 0) {
759   - for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
760   - subscribe.response(null, json);
761   - }
762   - }
763   - });
764   -
765   - return ret;
766   - }
767   -
768   - private Map<String, String> urlParamToMap(String params) {
769   - HashMap<String, String> map = new HashMap<>();
770   - if (ObjectUtils.isEmpty(params)) {
771   - return map;
772   - }
773   - String[] paramsArray = params.split("&");
774   - if (paramsArray.length == 0) {
775   - return map;
776   - }
777   - for (String param : paramsArray) {
778   - String[] paramArray = param.split("=");
779   - if (paramArray.length == 2){
780   - map.put(paramArray[0], paramArray[1]);
781   - }
782   - }
783   - return map;
784   - }
  604 + }
  605 + return ret;
  606 + }
  607 +
  608 + /**
  609 + * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。
  610 + */
  611 + @ResponseBody
  612 + @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
  613 + public DeferredResult<HookResult> onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) {
  614 + logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  615 +
  616 + DeferredResult<HookResult> defaultResult = new DeferredResult<>();
  617 +
  618 + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
  619 + if (!userSetting.isAutoApplyPlay() || mediaInfo == null) {
  620 + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
  621 + return defaultResult;
  622 + }
  623 +
  624 + if ("rtp".equals(param.getApp())) {
  625 + String[] s = param.getStream().split("_");
  626 + if (!mediaInfo.isRtpEnable() || s.length != 2) {
  627 + defaultResult.setResult(HookResult.SUCCESS());
  628 + return defaultResult;
  629 + }
  630 + String deviceId = s[0];
  631 + String channelId = s[1];
  632 + Device device = redisCatchStorage.getDevice(deviceId);
  633 + if (device == null) {
  634 + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
  635 + return defaultResult;
  636 + }
  637 + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
  638 + if (deviceChannel == null) {
  639 + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
  640 + return defaultResult;
  641 + }
  642 + logger.info("[ZLM HOOK] 流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  643 + RequestMessage msg = new RequestMessage();
  644 + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
  645 + boolean exist = resultHolder.exist(key, null);
  646 + msg.setKey(key);
  647 + String uuid = UUID.randomUUID().toString();
  648 + msg.setId(uuid);
  649 + DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
  650 + DeferredResultEx<HookResult> deferredResultEx = new DeferredResultEx<>(result);
  651 +
  652 + result.onTimeout(() -> {
  653 + logger.info("点播接口等待超时");
  654 + // 释放rtpserver
  655 + msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
  656 + resultHolder.invokeResult(msg);
  657 + });
  658 + // TODO 在点播未成功的情况下在此调用接口点播会导致返回的流地址ip错误
  659 + deferredResultEx.setFilter(result1 -> {
  660 + WVPResult<StreamInfo> wvpResult1 = (WVPResult<StreamInfo>) result1;
  661 + HookResult resultForEnd = new HookResult();
  662 + resultForEnd.setCode(wvpResult1.getCode());
  663 + resultForEnd.setMsg(wvpResult1.getMsg());
  664 + return resultForEnd;
  665 + });
  666 +
  667 + // 录像查询以channelId作为deviceId查询
  668 + resultHolder.put(key, uuid, deferredResultEx);
  669 +
  670 + if (!exist) {
  671 + playService.play(mediaInfo, deviceId, channelId, null, eventResult -> {
  672 + msg.setData(new HookResult(eventResult.statusCode, eventResult.msg));
  673 + resultHolder.invokeResult(msg);
  674 + }, null);
  675 + }
  676 + return result;
  677 + } else {
  678 + // 拉流代理
  679 + StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
  680 + if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) {
  681 + streamProxyService.start(param.getApp(), param.getStream());
  682 + }
  683 + DeferredResult<HookResult> result = new DeferredResult<>();
  684 + result.setResult(HookResult.SUCCESS());
  685 + return result;
  686 + }
  687 + }
  688 +
  689 + /**
  690 + * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。
  691 + */
  692 + @ResponseBody
  693 + @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
  694 + public HookResult onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject) {
  695 +
  696 + jsonObject.put("ip", request.getRemoteAddr());
  697 + ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
  698 + zlmServerConfig.setIp(request.getRemoteAddr());
  699 + logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
  700 + taskExecutor.execute(() -> {
  701 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
  702 + if (subscribes != null && subscribes.size() > 0) {
  703 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  704 + subscribe.response(null, jsonObject);
  705 + }
  706 + }
  707 + mediaServerService.zlmServerOnline(zlmServerConfig);
  708 + });
  709 +
  710 + return HookResult.SUCCESS();
  711 + }
  712 +
  713 + /**
  714 + * 发送rtp(startSendRtp)被动关闭时回调
  715 + */
  716 + @ResponseBody
  717 + @PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8")
  718 + public HookResult onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param) {
  719 +
  720 + logger.info("[ZLM HOOK] rtp发送关闭:{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream());
  721 +
  722 + // 查找对应的上级推流,发送停止
  723 + if (!"rtp".equals(param.getApp())) {
  724 + return HookResult.SUCCESS();
  725 + }
  726 + taskExecutor.execute(() -> {
  727 + List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
  728 + if (sendRtpItems.size() > 0) {
  729 + for (SendRtpItem sendRtpItem : sendRtpItems) {
  730 + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
  731 + try {
  732 + commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
  733 + } catch (SipException | InvalidArgumentException | ParseException e) {
  734 + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
  735 + }
  736 + redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
  737 + sendRtpItem.getCallId(), sendRtpItem.getStreamId());
  738 + }
  739 + }
  740 + });
  741 +
  742 + return HookResult.SUCCESS();
  743 + }
  744 +
  745 + /**
  746 + * rtpServer收流超时
  747 + */
  748 + @ResponseBody
  749 + @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
  750 + public HookResult onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param) {
  751 + logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
  752 +
  753 + taskExecutor.execute(() -> {
  754 + JSONObject json = (JSONObject) JSON.toJSON(param);
  755 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
  756 + if (subscribes != null && subscribes.size() > 0) {
  757 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  758 + subscribe.response(null, json);
  759 + }
  760 + }
  761 + });
  762 +
  763 + return HookResult.SUCCESS();
  764 + }
  765 +
  766 + private Map<String, String> urlParamToMap(String params) {
  767 + HashMap<String, String> map = new HashMap<>();
  768 + if (ObjectUtils.isEmpty(params)) {
  769 + return map;
  770 + }
  771 + String[] paramsArray = params.split("&");
  772 + if (paramsArray.length == 0) {
  773 + return map;
  774 + }
  775 + for (String param : paramsArray) {
  776 + String[] paramArray = param.split("=");
  777 + if (paramArray.length == 2) {
  778 + map.put(paramArray[0], paramArray[1]);
  779 + }
  780 + }
  781 + return map;
  782 + }
785 783 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
... ... @@ -36,7 +36,7 @@ public class ZLMRESTfulUtils {
36 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 }
... ...