Commit 2e7c9a7341670d78f5a1ce061ac225b02d6f606f

Authored by 648540858
2 parents fc8bb378 b079039f

Merge branch 'wvp-28181-2.0' into main-dev

# Conflicts:
#	pom.xml
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java
#	src/main/resources/all-application.yml
#	src/main/resources/application-dev.yml
Showing 70 changed files with 2282 additions and 1000 deletions

Too many changes to show.

To preserve performance only 70 of 144 files are displayed.

libs/jdbc-x86/bcprov-jdk15on-1.70.jar 0 → 100644
No preview for this file type
libs/jdbc-x86/kingbase8-8.6.0.jar 0 → 100644
No preview for this file type
libs/jdbc-x86/kingbase8-8.6.0.jre6.jar 0 → 100644
No preview for this file type
libs/jdbc-x86/kingbase8-8.6.0.jre7.jar 0 → 100644
No preview for this file type
libs/jdbc-x86/postgresql-42.2.9.jar 0 → 100644
No preview for this file type
libs/jdbc-x86/postgresql-42.2.9.jre6.jar 0 → 100644
No preview for this file type
libs/jdbc-x86/postgresql-42.2.9.jre7.jar 0 → 100644
No preview for this file type
1 <?xml version="1.0"?> 1 <?xml version="1.0"?>
2 -<project  
3 - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"  
4 - xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  
5 - <modelVersion>4.0.0</modelVersion>  
6 - <parent>  
7 - <groupId>org.springframework.boot</groupId>  
8 - <artifactId>spring-boot-starter-parent</artifactId>  
9 - <version>2.7.9</version>  
10 - </parent>  
11 -  
12 - <groupId>com.genersoft</groupId>  
13 - <artifactId>wvp-pro</artifactId>  
14 - <version>2.6.9</version>  
15 - <name>web video platform</name>  
16 - <description>国标28181视频平台</description>  
17 - <packaging>${project.packaging}</packaging>  
18 -  
19 - <repositories>  
20 - <repository>  
21 - <id>nexus-aliyun</id>  
22 - <name>Nexus aliyun</name>  
23 - <url>https://maven.aliyun.com/repository/public</url>  
24 - <layout>default</layout>  
25 - <snapshots>  
26 - <enabled>false</enabled>  
27 - </snapshots>  
28 - <releases>  
29 - <enabled>true</enabled>  
30 - </releases>  
31 - </repository>  
32 - </repositories>  
33 - <pluginRepositories>  
34 - <pluginRepository>  
35 - <id>nexus-aliyun</id>  
36 - <name>Nexus aliyun</name>  
37 - <url>https://maven.aliyun.com/repository/public</url>  
38 - <snapshots>  
39 - <enabled>false</enabled>  
40 - </snapshots>  
41 - <releases>  
42 - <enabled>true</enabled>  
43 - </releases>  
44 - </pluginRepository>  
45 - </pluginRepositories>  
46 -  
47 - <properties>  
48 - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
49 - <maven.build.timestamp.format>MMddHHmm</maven.build.timestamp.format>  
50 - <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>  
51 -  
52 - <!-- 依赖版本 -->  
53 - <snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>  
54 - <asciidoctor.input.directory>${project.basedir}/docs/asciidoc</asciidoctor.input.directory>  
55 - <generated.asciidoc.directory>${project.build.directory}/asciidoc</generated.asciidoc.directory>  
56 - <asciidoctor.html.output.directory>${project.build.directory}/asciidoc/html</asciidoctor.html.output.directory>  
57 - <asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>  
58 - </properties>  
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 -  
96 - <dependencies>  
97 - <dependency>  
98 - <groupId>org.springframework.boot</groupId>  
99 - <artifactId>spring-boot-starter-data-redis</artifactId>  
100 - </dependency>  
101 - <dependency>  
102 - <groupId>org.springframework.boot</groupId>  
103 - <artifactId>spring-boot-starter-web</artifactId>  
104 - </dependency>  
105 - <dependency>  
106 - <groupId>org.springframework.boot</groupId>  
107 - <artifactId>spring-boot-configuration-processor</artifactId>  
108 - <optional>true</optional>  
109 - </dependency>  
110 - <dependency>  
111 - <groupId>org.mybatis.spring.boot</groupId>  
112 - <artifactId>mybatis-spring-boot-starter</artifactId>  
113 - <version>2.2.2</version>  
114 - <exclusions>  
115 - <exclusion>  
116 - <groupId>com.zaxxer</groupId>  
117 - <artifactId>HikariCP</artifactId>  
118 - </exclusion>  
119 - </exclusions>  
120 - </dependency>  
121 - <dependency>  
122 - <groupId>org.springframework.boot</groupId>  
123 - <artifactId>spring-boot-starter-security</artifactId>  
124 - </dependency>  
125 -  
126 - <dependency>  
127 - <groupId>org.springframework.boot</groupId>  
128 - <artifactId>spring-boot-starter-jdbc</artifactId>  
129 - </dependency>  
130 -  
131 - <!-- mysql数据库 -->  
132 - <dependency>  
133 - <groupId>mysql</groupId>  
134 - <artifactId>mysql-connector-java</artifactId>  
135 - <version>8.0.30</version>  
136 - </dependency>  
137 -  
138 - <!--postgresql-->  
139 - <dependency>  
140 - <groupId>org.postgresql</groupId>  
141 - <artifactId>postgresql</artifactId>  
142 - <version>42.5.1</version>  
143 - </dependency> 2 +<project
  3 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
  4 + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  5 + <modelVersion>4.0.0</modelVersion>
  6 + <parent>
  7 + <groupId>org.springframework.boot</groupId>
  8 + <artifactId>spring-boot-starter-parent</artifactId>
  9 + <version>2.7.17</version>
  10 + </parent>
  11 +
  12 + <groupId>com.genersoft</groupId>
  13 + <artifactId>wvp-pro</artifactId>
  14 + <version>2.7.0</version>
  15 + <name>web video platform</name>
  16 + <description>国标28181视频平台</description>
  17 + <packaging>${project.packaging}</packaging>
  18 +
  19 + <repositories>
  20 + <repository>
  21 + <id>nexus-aliyun</id>
  22 + <name>Nexus aliyun</name>
  23 + <url>https://maven.aliyun.com/repository/public</url>
  24 + <layout>default</layout>
  25 + <snapshots>
  26 + <enabled>false</enabled>
  27 + </snapshots>
  28 + <releases>
  29 + <enabled>true</enabled>
  30 + </releases>
  31 + </repository>
  32 + </repositories>
  33 +
  34 + <pluginRepositories>
  35 + <pluginRepository>
  36 + <id>nexus-aliyun</id>
  37 + <name>Nexus aliyun</name>
  38 + <url>https://maven.aliyun.com/repository/public</url>
  39 + <snapshots>
  40 + <enabled>false</enabled>
  41 + </snapshots>
  42 + <releases>
  43 + <enabled>true</enabled>
  44 + </releases>
  45 + </pluginRepository>
  46 + </pluginRepositories>
  47 +
  48 + <properties>
  49 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  50 + <maven.build.timestamp.format>MMddHHmm</maven.build.timestamp.format>
  51 + <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
  52 +
  53 + <!-- 依赖版本 -->
  54 + <snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
  55 + <asciidoctor.input.directory>${project.basedir}/docs/asciidoc</asciidoctor.input.directory>
  56 + <generated.asciidoc.directory>${project.build.directory}/asciidoc</generated.asciidoc.directory>
  57 + <asciidoctor.html.output.directory>${project.build.directory}/asciidoc/html</asciidoctor.html.output.directory>
  58 + <asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>
  59 + </properties>
  60 +
  61 + <profiles>
  62 + <profile>
  63 + <id>jar</id>
  64 + <activation>
  65 + <activeByDefault>true</activeByDefault>
  66 + </activation>
  67 + <properties>
  68 + <project.packaging>jar</project.packaging>
  69 + </properties>
  70 + </profile>
  71 + <profile>
  72 + <id>war</id>
  73 + <properties>
  74 + <project.packaging>war</project.packaging>
  75 + </properties>
  76 + <dependencies>
  77 + <dependency>
  78 + <groupId>org.springframework.boot</groupId>
  79 + <artifactId>spring-boot-starter-web</artifactId>
  80 + <exclusions>
  81 + <exclusion>
  82 + <groupId>org.springframework.boot</groupId>
  83 + <artifactId>spring-boot-starter-jetty</artifactId>
  84 + </exclusion>
  85 + </exclusions>
  86 + </dependency>
  87 + <dependency>
  88 + <groupId>javax.servlet</groupId>
  89 + <artifactId>javax.servlet-api</artifactId>
  90 + <version>3.1.0</version>
  91 + <scope>provided</scope>
  92 + </dependency>
  93 + </dependencies>
  94 + </profile>
  95 + </profiles>
  96 +
  97 + <dependencies>
  98 + <dependency>
  99 + <groupId>org.springframework.boot</groupId>
  100 + <artifactId>spring-boot-starter-data-redis</artifactId>
  101 + </dependency>
  102 + <dependency>
  103 + <groupId>org.springframework.boot</groupId>
  104 + <artifactId>spring-boot-starter-web</artifactId>
  105 + </dependency>
  106 + <dependency>
  107 + <groupId>org.springframework.boot</groupId>
  108 + <artifactId>spring-boot-configuration-processor</artifactId>
  109 + <optional>true</optional>
  110 + </dependency>
  111 + <dependency>
  112 + <groupId>org.mybatis.spring.boot</groupId>
  113 + <artifactId>mybatis-spring-boot-starter</artifactId>
  114 + <version>2.2.2</version>
  115 + <exclusions>
  116 + <exclusion>
  117 + <groupId>com.zaxxer</groupId>
  118 + <artifactId>HikariCP</artifactId>
  119 + </exclusion>
  120 + </exclusions>
  121 + </dependency>
  122 + <dependency>
  123 + <groupId>org.springframework.boot</groupId>
  124 + <artifactId>spring-boot-starter-security</artifactId>
  125 + </dependency>
  126 +
  127 + <dependency>
  128 + <groupId>org.springframework.boot</groupId>
  129 + <artifactId>spring-boot-starter-jdbc</artifactId>
  130 + </dependency>
  131 +
  132 + <!-- mysql数据库 -->
  133 + <dependency>
  134 + <groupId>com.mysql</groupId>
  135 + <artifactId>mysql-connector-j</artifactId>
  136 + <version>8.2.0</version>
  137 + </dependency>
  138 +
  139 + <!--postgresql-->
  140 + <dependency>
  141 + <groupId>org.postgresql</groupId>
  142 + <artifactId>postgresql</artifactId>
  143 + <version>42.5.1</version>
  144 + </dependency>
144 145
145 <!-- kingbase人大金仓 --> 146 <!-- kingbase人大金仓 -->
146 <!-- 手动下载驱动后安装 --> 147 <!-- 手动下载驱动后安装 -->
@@ -153,14 +154,41 @@ @@ -153,14 +154,41 @@
153 <scope>system</scope> 154 <scope>system</scope>
154 <systemPath>${basedir}/libs/jdbc-aarch/kingbase8-8.6.0.jar</systemPath> 155 <systemPath>${basedir}/libs/jdbc-aarch/kingbase8-8.6.0.jar</systemPath>
155 </dependency> 156 </dependency>
  157 + <dependency>
  158 + <groupId>com.kingbase</groupId>
  159 + <artifactId>kingbase8</artifactId>
  160 + <version>8.6.0</version>
  161 + <scope>system</scope>
  162 + <systemPath>${basedir}/libs/jdbc-x86/kingbase8-8.6.0.jar</systemPath>
  163 + </dependency>
156 164
157 - <!--Mybatis分页插件 --> 165 + <!--Mybatis分页插件 -->
  166 + <dependency>
  167 + <groupId>com.github.pagehelper</groupId>
  168 + <artifactId>pagehelper-spring-boot-starter</artifactId>
  169 + <version>1.4.6</version>
  170 + </dependency>
  171 +
  172 + <!--在线文档 -->
  173 + <!--在线文档 -->
  174 + <dependency>
  175 + <groupId>org.springdoc</groupId>
  176 + <artifactId>springdoc-openapi-ui</artifactId>
  177 + <version>1.6.10</version>
  178 + </dependency>
  179 + <dependency>
  180 + <groupId>org.springdoc</groupId>
  181 + <artifactId>springdoc-openapi-security</artifactId>
  182 + <version>1.6.10</version>
  183 + </dependency>
  184 + <!-- https://mvnrepository.com/artifact/com.baomidou/dynamic-datasource-spring-boot-starter -->
158 <dependency> 185 <dependency>
159 - <groupId>com.github.pagehelper</groupId>  
160 - <artifactId>pagehelper-spring-boot-starter</artifactId>  
161 - <version>1.4.6</version> 186 + <groupId>com.baomidou</groupId>
  187 + <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  188 + <version>3.6.1</version>
162 </dependency> 189 </dependency>
163 190
  191 +
164 <!--在线文档 --> 192 <!--在线文档 -->
165 <dependency> 193 <dependency>
166 <groupId>org.springdoc</groupId> 194 <groupId>org.springdoc</groupId>
@@ -168,199 +196,203 @@ @@ -168,199 +196,203 @@
168 <version>1.6.10</version> 196 <version>1.6.10</version>
169 </dependency> 197 </dependency>
170 198
171 - <dependency>  
172 - <groupId>com.github.xiaoymin</groupId>  
173 - <artifactId>knife4j-springdoc-ui</artifactId>  
174 - <version>3.0.3</version>  
175 - </dependency>  
176 -  
177 - <!--参数校验 -->  
178 - <dependency>  
179 - <groupId>javax.validation</groupId>  
180 - <artifactId>validation-api</artifactId>  
181 - </dependency>  
182 -  
183 - <!-- 日志相关 -->  
184 - <dependency>  
185 - <groupId>org.springframework.boot</groupId>  
186 - <artifactId>spring-boot-starter-aop</artifactId>  
187 - </dependency>  
188 -  
189 - <!-- sip协议栈 -->  
190 - <dependency>  
191 - <groupId>javax.sip</groupId>  
192 - <artifactId>jain-sip-ri</artifactId>  
193 - <version>1.3.0-91</version>  
194 - </dependency>  
195 -  
196 - <!-- 取代log4j -->  
197 - <dependency>  
198 - <groupId>org.slf4j</groupId>  
199 - <artifactId>log4j-over-slf4j</artifactId>  
200 - <version>1.7.36</version>  
201 - </dependency>  
202 -  
203 - <!-- xml解析库 -->  
204 - <dependency>  
205 - <groupId>org.dom4j</groupId>  
206 - <artifactId>dom4j</artifactId>  
207 - <version>2.1.3</version>  
208 - </dependency>  
209 -  
210 - <dependency>  
211 - <groupId>com.google.guava</groupId>  
212 - <artifactId>guava</artifactId>  
213 - <version>20.0</version>  
214 - </dependency>  
215 -  
216 - <!-- json解析库fastjson2 -->  
217 - <dependency>  
218 - <groupId>com.alibaba.fastjson2</groupId>  
219 - <artifactId>fastjson2</artifactId>  
220 - <version>2.0.17</version>  
221 - </dependency>  
222 - <dependency>  
223 - <groupId>com.alibaba.fastjson2</groupId>  
224 - <artifactId>fastjson2-extension</artifactId>  
225 - <version>2.0.17</version>  
226 - </dependency>  
227 -  
228 - <!-- okhttp -->  
229 - <dependency>  
230 - <groupId>com.squareup.okhttp3</groupId>  
231 - <artifactId>okhttp</artifactId>  
232 - <version>4.10.0</version>  
233 - </dependency>  
234 -  
235 - <!-- okhttp 调试日志 -->  
236 - <dependency>  
237 - <groupId>com.squareup.okhttp3</groupId>  
238 - <artifactId>logging-interceptor</artifactId>  
239 - <version>4.10.0</version>  
240 - </dependency>  
241 -  
242 - <!-- okhttp-digest -->  
243 - <dependency>  
244 - <groupId>io.github.rburgst</groupId>  
245 - <artifactId>okhttp-digest</artifactId>  
246 - <version>2.7</version>  
247 - </dependency>  
248 -  
249 - <!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 -->  
250 -<!-- <dependency>-->  
251 -<!-- <groupId>net.sf.kxml</groupId>-->  
252 -<!-- <artifactId>kxml2</artifactId>-->  
253 -<!-- <version>2.3.0</version>-->  
254 -<!-- </dependency>-->  
255 -  
256 - <!-- jwt实现 -->  
257 - <dependency>  
258 - <groupId>org.bitbucket.b_c</groupId>  
259 - <artifactId>jose4j</artifactId>  
260 - <version>0.9.3</version>  
261 - </dependency>  
262 -  
263 - <!--反向代理-->  
264 - <dependency>  
265 - <groupId>org.mitre.dsmiley.httpproxy</groupId>  
266 - <artifactId>smiley-http-proxy-servlet</artifactId>  
267 - <version>1.12.1</version>  
268 - </dependency>  
269 -  
270 - <!--excel解析库-->  
271 - <dependency>  
272 - <groupId>com.alibaba</groupId>  
273 - <artifactId>easyexcel</artifactId>  
274 - <version>3.1.1</version>  
275 - </dependency>  
276 -  
277 - <!-- 获取系统信息 -->  
278 - <dependency>  
279 - <groupId>com.github.oshi</groupId>  
280 - <artifactId>oshi-core</artifactId>  
281 - <version>6.2.2</version>  
282 - </dependency>  
283 -  
284 - <dependency>  
285 - <groupId>org.springframework.session</groupId>  
286 - <artifactId>spring-session-core</artifactId>  
287 - </dependency>  
288 -  
289 -<!-- &lt;!&ndash; 检测文件编码 &ndash;&gt;-->  
290 -<!-- &lt;!&ndash; https://mvnrepository.com/artifact/cpdetector/cpdetector &ndash;&gt;-->  
291 -<!-- <dependency>-->  
292 -<!-- <groupId>cpdetector</groupId>-->  
293 -<!-- <artifactId>cpdetector</artifactId>-->  
294 -<!-- <version>1.0.8</version>-->  
295 -<!-- </dependency>-->  
296 -  
297 - <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->  
298 - <dependency>  
299 - <groupId>com.google.guava</groupId>  
300 - <artifactId>guava</artifactId>  
301 - <version>31.1-jre</version>  
302 - </dependency>  
303 -  
304 -  
305 - <dependency>  
306 - <groupId>org.springframework.boot</groupId>  
307 - <artifactId>spring-boot-starter-test</artifactId>  
308 -<!-- <scope>test</scope>-->  
309 - </dependency>  
310 -  
311 - </dependencies>  
312 -  
313 -  
314 - <build>  
315 - <finalName>${project.artifactId}-${project.version}-${maven.build.timestamp}</finalName>  
316 - <plugins>  
317 - <plugin>  
318 - <groupId>org.springframework.boot</groupId>  
319 - <artifactId>spring-boot-maven-plugin</artifactId>  
320 - <version>2.7.2</version>  
321 - <configuration>  
322 - <includeSystemScope>true</includeSystemScope>  
323 - </configuration>  
324 - </plugin>  
325 - <plugin>  
326 - <groupId>org.apache.maven.plugins</groupId>  
327 - <artifactId>maven-compiler-plugin</artifactId>  
328 - <version>3.8.1</version>  
329 - <configuration>  
330 - <source>1.8</source>  
331 - <target>1.8</target>  
332 - </configuration>  
333 - </plugin>  
334 -  
335 - <plugin>  
336 - <groupId>pl.project13.maven</groupId>  
337 - <artifactId>git-commit-id-plugin</artifactId>  
338 - <version>3.0.1</version>  
339 - <configuration>  
340 - <offline>true</offline>  
341 - <failOnNoGitDirectory>false</failOnNoGitDirectory>  
342 - <dateFormat>yyyyMMdd</dateFormat>  
343 - </configuration>  
344 - </plugin>  
345 - <plugin>  
346 - <groupId>org.apache.maven.plugins</groupId>  
347 - <artifactId>maven-surefire-plugin</artifactId>  
348 - <version>2.22.2</version>  
349 - <configuration>  
350 - <skipTests>true</skipTests>  
351 - </configuration>  
352 - </plugin>  
353 - </plugins>  
354 - <resources>  
355 - <resource>  
356 - <directory>src/main/resources</directory>  
357 - </resource>  
358 - <resource>  
359 - <directory>src/main/java</directory>  
360 - <includes>  
361 - <include>**/*.xml</include>  
362 - </includes>  
363 - </resource>  
364 - </resources>  
365 - </build> 199 + <dependency>
  200 + <groupId>com.github.xiaoymin</groupId>
  201 + <artifactId>knife4j-springdoc-ui</artifactId>
  202 + <version>3.0.3</version>
  203 + </dependency>
  204 +
  205 + <!--参数校验 -->
  206 + <dependency>
  207 + <groupId>javax.validation</groupId>
  208 + <artifactId>validation-api</artifactId>
  209 + </dependency>
  210 +
  211 + <!-- 日志相关 -->
  212 + <dependency>
  213 + <groupId>org.springframework.boot</groupId>
  214 + <artifactId>spring-boot-starter-aop</artifactId>
  215 + </dependency>
  216 +
  217 + <!-- sip协议栈 -->
  218 + <dependency>
  219 + <groupId>javax.sip</groupId>
  220 + <artifactId>jain-sip-ri</artifactId>
  221 + <version>1.3.0-91</version>
  222 + </dependency>
  223 +
  224 + <!-- 取代log4j -->
  225 + <dependency>
  226 + <groupId>org.slf4j</groupId>
  227 + <artifactId>log4j-over-slf4j</artifactId>
  228 + <version>1.7.36</version>
  229 + </dependency>
  230 +
  231 + <!-- xml解析库 -->
  232 + <dependency>
  233 + <groupId>org.dom4j</groupId>
  234 + <artifactId>dom4j</artifactId>
  235 + <version>2.1.3</version>
  236 + </dependency>
  237 +
  238 + <!-- json解析库fastjson2 -->
  239 + <dependency>
  240 + <groupId>com.alibaba.fastjson2</groupId>
  241 + <artifactId>fastjson2</artifactId>
  242 + <version>2.0.17</version>
  243 + </dependency>
  244 + <dependency>
  245 + <groupId>com.alibaba.fastjson2</groupId>
  246 + <artifactId>fastjson2-extension</artifactId>
  247 + <version>2.0.17</version>
  248 + </dependency>
  249 +
  250 + <!-- okhttp -->
  251 + <dependency>
  252 + <groupId>com.squareup.okhttp3</groupId>
  253 + <artifactId>okhttp</artifactId>
  254 + <version>4.10.0</version>
  255 + </dependency>
  256 +
  257 + <!-- okhttp 调试日志 -->
  258 + <dependency>
  259 + <groupId>com.squareup.okhttp3</groupId>
  260 + <artifactId>logging-interceptor</artifactId>
  261 + <version>4.10.0</version>
  262 + </dependency>
  263 +
  264 + <!-- okhttp-digest -->
  265 + <dependency>
  266 + <groupId>io.github.rburgst</groupId>
  267 + <artifactId>okhttp-digest</artifactId>
  268 + <version>2.7</version>
  269 + </dependency>
  270 +
  271 + <!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 -->
  272 + <!-- <dependency>-->
  273 + <!-- <groupId>net.sf.kxml</groupId>-->
  274 + <!-- <artifactId>kxml2</artifactId>-->
  275 + <!-- <version>2.3.0</version>-->
  276 + <!-- </dependency>-->
  277 +
  278 + <!-- jwt实现 -->
  279 + <dependency>
  280 + <groupId>org.bitbucket.b_c</groupId>
  281 + <artifactId>jose4j</artifactId>
  282 + <version>0.9.3</version>
  283 + </dependency>
  284 +
  285 + <!--反向代理-->
  286 + <dependency>
  287 + <groupId>org.mitre.dsmiley.httpproxy</groupId>
  288 + <artifactId>smiley-http-proxy-servlet</artifactId>
  289 + <version>1.12.1</version>
  290 + </dependency>
  291 +
  292 + <!--excel解析库-->
  293 + <dependency>
  294 + <groupId>com.alibaba</groupId>
  295 + <artifactId>easyexcel</artifactId>
  296 + <version>3.3.2</version>
  297 + <exclusions>
  298 + <exclusion>
  299 + <groupId>org.apache.commons</groupId>
  300 + <artifactId>commons-compress</artifactId>
  301 + </exclusion>
  302 + </exclusions>
  303 + </dependency>
  304 + <dependency>
  305 + <groupId>org.apache.commons</groupId>
  306 + <artifactId>commons-compress</artifactId>
  307 + <version>1.24.0</version>
  308 + </dependency>
  309 +
  310 + <!-- 获取系统信息 -->
  311 + <dependency>
  312 + <groupId>com.github.oshi</groupId>
  313 + <artifactId>oshi-core</artifactId>
  314 + <version>6.2.2</version>
  315 + </dependency>
  316 +
  317 + <dependency>
  318 + <groupId>org.springframework.session</groupId>
  319 + <artifactId>spring-session-core</artifactId>
  320 + </dependency>
  321 +
  322 + <!-- 检测文件编码 -->
  323 + <!-- https://mvnrepository.com/artifact/cpdetector/cpdetector -->
  324 + <!--<dependency>-->
  325 + <!-- <groupId>cpdetector</groupId>-->
  326 + <!-- <artifactId>cpdetector</artifactId>-->
  327 + <!-- <version>1.0.8</version>-->
  328 + <!--</dependency>-->
  329 +
  330 + <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
  331 + <dependency>
  332 + <groupId>com.google.guava</groupId>
  333 + <artifactId>guava</artifactId>
  334 + <version>32.1.3-jre</version>
  335 + </dependency>
  336 +
  337 + <dependency>
  338 + <groupId>org.springframework.boot</groupId>
  339 + <artifactId>spring-boot-starter-test</artifactId>
  340 + <scope>test</scope>
  341 + </dependency>
  342 + </dependencies>
  343 +
  344 + <build>
  345 + <finalName>${project.artifactId}-${project.version}-${maven.build.timestamp}</finalName>
  346 + <plugins>
  347 + <plugin>
  348 + <groupId>org.springframework.boot</groupId>
  349 + <artifactId>spring-boot-maven-plugin</artifactId>
  350 + <version>2.7.2</version>
  351 + <configuration>
  352 + <includeSystemScope>true</includeSystemScope>
  353 + </configuration>
  354 + </plugin>
  355 +
  356 + <plugin>
  357 + <groupId>org.apache.maven.plugins</groupId>
  358 + <artifactId>maven-compiler-plugin</artifactId>
  359 + <version>3.8.1</version>
  360 + <configuration>
  361 + <source>1.8</source>
  362 + <target>1.8</target>
  363 + </configuration>
  364 + </plugin>
  365 +
  366 + <plugin>
  367 + <groupId>pl.project13.maven</groupId>
  368 + <artifactId>git-commit-id-plugin</artifactId>
  369 + <version>3.0.1</version>
  370 + <configuration>
  371 + <offline>true</offline>
  372 + <failOnNoGitDirectory>false</failOnNoGitDirectory>
  373 + <dateFormat>yyyyMMdd</dateFormat>
  374 + </configuration>
  375 + </plugin>
  376 +
  377 + <plugin>
  378 + <groupId>org.apache.maven.plugins</groupId>
  379 + <artifactId>maven-surefire-plugin</artifactId>
  380 + <version>2.22.2</version>
  381 + <configuration>
  382 + <skipTests>true</skipTests>
  383 + </configuration>
  384 + </plugin>
  385 + </plugins>
  386 + <resources>
  387 + <resource>
  388 + <directory>src/main/resources</directory>
  389 + </resource>
  390 + <resource>
  391 + <directory>src/main/java</directory>
  392 + <includes>
  393 + <include>**/*.xml</include>
  394 + </includes>
  395 + </resource>
  396 + </resources>
  397 + </build>
366 </project> 398 </project>
sql/2.6.9更新.sql deleted 100644 → 0
1 -alter table wvp_device_channel  
2 - change stream_id stream_id varying(255)  
3 -  
4 -alter table wvp_platform  
5 - add auto_push_channel bool default false  
6 -  
7 -alter table wvp_stream_proxy  
8 - add stream_key character varying(255)  
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
1 package com.genersoft.iot.vmp.common; 1 package com.genersoft.iot.vmp.common;
2 2
  3 +import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
3 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
4 5
5 import java.io.Serializable; 6 import java.io.Serializable;
@@ -76,6 +77,8 @@ public class StreamInfo implements Serializable, Cloneable{ @@ -76,6 +77,8 @@ public class StreamInfo implements Serializable, Cloneable{
76 private String endTime; 77 private String endTime;
77 @Schema(description = "进度(录像下载使用)") 78 @Schema(description = "进度(录像下载使用)")
78 private double progress; 79 private double progress;
  80 + @Schema(description = "文件下载地址(录像下载使用)")
  81 + private DownloadFileInfo downLoadFilePath;
79 82
80 @Schema(description = "是否暂停(录像回放使用)") 83 @Schema(description = "是否暂停(录像回放使用)")
81 private boolean pause; 84 private boolean pause;
@@ -605,5 +608,11 @@ public class StreamInfo implements Serializable, Cloneable{ @@ -605,5 +608,11 @@ public class StreamInfo implements Serializable, Cloneable{
605 this.subStream = subStream; 608 this.subStream = subStream;
606 } 609 }
607 610
  611 + public DownloadFileInfo getDownLoadFilePath() {
  612 + return downLoadFilePath;
  613 + }
608 614
  615 + public void setDownLoadFilePath(DownloadFileInfo downLoadFilePath) {
  616 + this.downLoadFilePath = downLoadFilePath;
  617 + }
609 } 618 }
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
@@ -53,7 +53,7 @@ public class VideoManagerConstants { @@ -53,7 +53,7 @@ public class VideoManagerConstants {
53 53
54 public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_MEDIA_TRANSACTION_"; 54 public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_MEDIA_TRANSACTION_";
55 55
56 - public static final String MEDIA_STREAM_AUTHORITY = "MEDIA_STREAM_AUTHORITY_"; 56 + public static final String MEDIA_STREAM_AUTHORITY = "VMP_MEDIA_STREAM_AUTHORITY_";
57 57
58 public static final String SIP_CSEQ_PREFIX = "VMP_SIP_CSEQ_"; 58 public static final String SIP_CSEQ_PREFIX = "VMP_SIP_CSEQ_";
59 59
@@ -71,6 +71,7 @@ public class VideoManagerConstants { @@ -71,6 +71,7 @@ public class VideoManagerConstants {
71 public static final String BROADCAST_WAITE_INVITE = "task_broadcast_waite_invite_"; 71 public static final String BROADCAST_WAITE_INVITE = "task_broadcast_waite_invite_";
72 72
73 public static final String REGISTER_EXPIRE_TASK_KEY_PREFIX = "VMP_device_register_expire_"; 73 public static final String REGISTER_EXPIRE_TASK_KEY_PREFIX = "VMP_device_register_expire_";
  74 + public static final String PUSH_STREAM_LIST = "VMP_PUSH_STREAM_LIST_";
74 75
75 76
76 77
src/main/java/com/genersoft/iot/vmp/conf/CloudRecordTimer.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf;
  2 +
  3 +
  4 +import com.alibaba.fastjson2.JSONObject;
  5 +import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
  6 +import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
  7 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  8 +import com.genersoft.iot.vmp.service.IMediaServerService;
  9 +import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
  10 +import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
  11 +import com.genersoft.iot.vmp.vmanager.cloudRecord.CloudRecordController;
  12 +import org.slf4j.Logger;
  13 +import org.slf4j.LoggerFactory;
  14 +import org.springframework.beans.factory.annotation.Autowired;
  15 +import org.springframework.scheduling.annotation.Scheduled;
  16 +import org.springframework.stereotype.Component;
  17 +
  18 +import java.io.File;
  19 +import java.util.ArrayList;
  20 +import java.util.Calendar;
  21 +import java.util.Date;
  22 +import java.util.List;
  23 +
  24 +/**
  25 + * 录像文件定时删除
  26 + */
  27 +@Component
  28 +public class CloudRecordTimer {
  29 +
  30 + private final static Logger logger = LoggerFactory.getLogger(CloudRecordTimer.class);
  31 +
  32 + @Autowired
  33 + private IMediaServerService mediaServerService;
  34 +
  35 + @Autowired
  36 + private CloudRecordServiceMapper cloudRecordServiceMapper;
  37 +
  38 + @Autowired
  39 + private ZLMRESTfulUtils zlmresTfulUtils;
  40 +
  41 + /**
  42 + * 定时查询待删除的录像文件
  43 + */
  44 +// @Scheduled(fixedRate = 10000) //每五秒执行一次,方便测试
  45 + @Scheduled(cron = "0 0 0 * * ?") //每天的0点执行
  46 + public void execute(){
  47 + logger.info("[录像文件定时清理] 开始清理过期录像文件");
  48 + // 获取配置了assist的流媒体节点
  49 + List<MediaServerItem> mediaServerItemList = mediaServerService.getAllOnline();
  50 + if (mediaServerItemList.isEmpty()) {
  51 + return;
  52 + }
  53 + long result = 0;
  54 + for (MediaServerItem mediaServerItem : mediaServerItemList) {
  55 +
  56 + Calendar lastCalendar = Calendar.getInstance();
  57 + if (mediaServerItem.getRecordDay() > 0) {
  58 + lastCalendar.setTime(new Date());
  59 + // 获取保存的最后截至日[期,因为每个节点都有一个日期,也就是支持每个节点设置不同的保存日期,
  60 + lastCalendar.add(Calendar.DAY_OF_MONTH, -mediaServerItem.getRecordDay());
  61 + Long lastDate = lastCalendar.getTimeInMillis();
  62 +
  63 + // 获取到截至日期之前的录像文件列表,文件列表满足未被收藏和保持的。这两个字段目前共能一致,
  64 + // 为我自己业务系统相关的代码,大家使用的时候直接使用收藏(collect)这一个类型即可
  65 + List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.queryRecordListForDelete(lastDate, mediaServerItem.getId());
  66 + if (cloudRecordItemList.isEmpty()) {
  67 + continue;
  68 + }
  69 + // TODO 后续可以删除空了的过期日期文件夹
  70 + for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
  71 + String date = new File(cloudRecordItem.getFilePath()).getParentFile().getName();
  72 + JSONObject jsonObject = zlmresTfulUtils.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(),
  73 + cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
  74 + if (jsonObject.getInteger("code") != 0) {
  75 + logger.warn("[录像文件定时清理] 删除磁盘文件错误: {}:{}", cloudRecordItem.getFilePath(), jsonObject);
  76 + }
  77 + }
  78 + result += cloudRecordServiceMapper.deleteList(cloudRecordItemList);
  79 + }
  80 + }
  81 + logger.info("[录像文件定时清理] 共清理{}个过期录像文件", result);
  82 + }
  83 +}
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
@@ -81,6 +81,12 @@ public class MediaConfig{ @@ -81,6 +81,12 @@ public class MediaConfig{
81 @Value("${media.record-assist-port:0}") 81 @Value("${media.record-assist-port:0}")
82 private Integer recordAssistPort = 0; 82 private Integer recordAssistPort = 0;
83 83
  84 + @Value("${media.record-day:7}")
  85 + private Integer recordDay;
  86 +
  87 + @Value("${media.record-path:}")
  88 + private String recordPath;
  89 +
84 public String getId() { 90 public String getId() {
85 return id; 91 return id;
86 } 92 }
@@ -212,13 +218,32 @@ public class MediaConfig{ @@ -212,13 +218,32 @@ public class MediaConfig{
212 mediaServerItem.setSendRtpPortRange(rtpSendPortRange); 218 mediaServerItem.setSendRtpPortRange(rtpSendPortRange);
213 mediaServerItem.setRecordAssistPort(recordAssistPort); 219 mediaServerItem.setRecordAssistPort(recordAssistPort);
214 mediaServerItem.setHookAliveInterval(30.00f); 220 mediaServerItem.setHookAliveInterval(30.00f);
215 - 221 + mediaServerItem.setRecordDay(recordDay);
  222 + if (recordPath != null) {
  223 + mediaServerItem.setRecordPath(recordPath);
  224 + }
216 mediaServerItem.setCreateTime(DateUtil.getNow()); 225 mediaServerItem.setCreateTime(DateUtil.getNow());
217 mediaServerItem.setUpdateTime(DateUtil.getNow()); 226 mediaServerItem.setUpdateTime(DateUtil.getNow());
218 227
219 return mediaServerItem; 228 return mediaServerItem;
220 } 229 }
221 230
  231 + public Integer getRecordDay() {
  232 + return recordDay;
  233 + }
  234 +
  235 + public void setRecordDay(Integer recordDay) {
  236 + this.recordDay = recordDay;
  237 + }
  238 +
  239 + public String getRecordPath() {
  240 + return recordPath;
  241 + }
  242 +
  243 + public void setRecordPath(String recordPath) {
  244 + this.recordPath = recordPath;
  245 + }
  246 +
222 public String getRtpSendPortRange() { 247 public String getRtpSendPortRange() {
223 return rtpSendPortRange; 248 return rtpSendPortRange;
224 } 249 }
src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java
1 package com.genersoft.iot.vmp.conf; 1 package com.genersoft.iot.vmp.conf;
2 2
  3 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
  4 +import io.swagger.v3.oas.models.Components;
3 import io.swagger.v3.oas.models.OpenAPI; 5 import io.swagger.v3.oas.models.OpenAPI;
4 import io.swagger.v3.oas.models.info.Contact; 6 import io.swagger.v3.oas.models.info.Contact;
5 import io.swagger.v3.oas.models.info.Info; 7 import io.swagger.v3.oas.models.info.Info;
6 import io.swagger.v3.oas.models.info.License; 8 import io.swagger.v3.oas.models.info.License;
  9 +import io.swagger.v3.oas.models.security.SecurityScheme;
7 import org.springframework.core.annotation.Order; 10 import org.springframework.core.annotation.Order;
8 import org.springdoc.core.GroupedOpenApi; 11 import org.springdoc.core.GroupedOpenApi;
9 import org.springframework.beans.factory.annotation.Value; 12 import org.springframework.beans.factory.annotation.Value;
@@ -26,10 +29,14 @@ public class SpringDocConfig { @@ -26,10 +29,14 @@ public class SpringDocConfig {
26 contact.setName("pan"); 29 contact.setName("pan");
27 contact.setEmail("648540858@qq.com"); 30 contact.setEmail("648540858@qq.com");
28 return new OpenAPI() 31 return new OpenAPI()
  32 + .components(new Components()
  33 + .addSecuritySchemes(JwtUtils.HEADER, new SecurityScheme()
  34 + .type(SecurityScheme.Type.HTTP)
  35 + .bearerFormat("JWT")))
29 .info(new Info().title("WVP-PRO 接口文档") 36 .info(new Info().title("WVP-PRO 接口文档")
30 .contact(contact) 37 .contact(contact)
31 .description("开箱即用的28181协议视频平台") 38 .description("开箱即用的28181协议视频平台")
32 - .version("v2.0") 39 + .version("v3.1.0")
33 .license(new License().name("Apache 2.0").url("http://springdoc.org"))); 40 .license(new License().name("Apache 2.0").url("http://springdoc.org")));
34 } 41 }
35 42
src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java
@@ -39,4 +39,6 @@ public class SystemInfoTimerTask { @@ -39,4 +39,6 @@ public class SystemInfoTimerTask {
39 } 39 }
40 40
41 } 41 }
  42 +
  43 +
42 } 44 }
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
@@ -23,7 +23,7 @@ public class UserSetting { @@ -23,7 +23,7 @@ public class UserSetting {
23 23
24 private Integer playTimeout = 18000; 24 private Integer playTimeout = 18000;
25 25
26 - private int platformPlayTimeout = 60000; 26 + private int platformPlayTimeout = 20000;
27 27
28 private Boolean interfaceAuthentication = Boolean.TRUE; 28 private Boolean interfaceAuthentication = Boolean.TRUE;
29 29
@@ -51,13 +51,11 @@ public class UserSetting { @@ -51,13 +51,11 @@ public class UserSetting {
51 51
52 private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE; 52 private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE;
53 53
54 - private Boolean deviceStatusNotify = Boolean.FALSE; 54 + private Boolean deviceStatusNotify = Boolean.TRUE;
55 private Boolean useCustomSsrcForParentInvite = Boolean.TRUE; 55 private Boolean useCustomSsrcForParentInvite = Boolean.TRUE;
56 56
57 private String serverId = "000000"; 57 private String serverId = "000000";
58 58
59 - private String recordPath = null;  
60 -  
61 private String thirdPartyGBIdReg = "[\\s\\S]*"; 59 private String thirdPartyGBIdReg = "[\\s\\S]*";
62 60
63 private String broadcastForPlatform = "UDP"; 61 private String broadcastForPlatform = "UDP";
@@ -262,14 +260,6 @@ public class UserSetting { @@ -262,14 +260,6 @@ public class UserSetting {
262 this.refuseChannelStatusChannelFormNotify = refuseChannelStatusChannelFormNotify; 260 this.refuseChannelStatusChannelFormNotify = refuseChannelStatusChannelFormNotify;
263 } 261 }
264 262
265 - public String getRecordPath() {  
266 - return recordPath;  
267 - }  
268 -  
269 - public void setRecordPath(String recordPath) {  
270 - this.recordPath = recordPath;  
271 - }  
272 -  
273 public int getMaxNotifyCountQueue() { 263 public int getMaxNotifyCountQueue() {
274 return maxNotifyCountQueue; 264 return maxNotifyCountQueue;
275 } 265 }
src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java
@@ -78,6 +78,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { @@ -78,6 +78,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
78 78
79 // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录 79 // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
80 User user = new User(); 80 User user = new User();
  81 + user.setId(jwtUser.getUserId());
81 user.setUsername(jwtUser.getUserName()); 82 user.setUsername(jwtUser.getUserName());
82 user.setPassword(jwtUser.getPassword()); 83 user.setPassword(jwtUser.getPassword());
83 Role role = new Role(); 84 Role role = new Role();
src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
@@ -28,7 +28,7 @@ public class JwtUtils implements InitializingBean { @@ -28,7 +28,7 @@ public class JwtUtils implements InitializingBean {
28 28
29 private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); 29 private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
30 30
31 - private static final String HEADER = "access-token"; 31 + public static final String HEADER = "access-token";
32 32
33 private static final String AUDIENCE = "Audience"; 33 private static final String AUDIENCE = "Audience";
34 34
@@ -144,6 +144,7 @@ public class JwtUtils implements InitializingBean { @@ -144,6 +144,7 @@ public class JwtUtils implements InitializingBean {
144 jwtUser.setUserName(username); 144 jwtUser.setUserName(username);
145 jwtUser.setPassword(user.getPassword()); 145 jwtUser.setPassword(user.getPassword());
146 jwtUser.setRoleId(user.getRole().getId()); 146 jwtUser.setRoleId(user.getRole().getId());
  147 + jwtUser.setUserId(user.getId());
147 148
148 return jwtUser; 149 return jwtUser;
149 } catch (InvalidJwtException e) { 150 } catch (InvalidJwtException e) {
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
1 package com.genersoft.iot.vmp.conf.security; 1 package com.genersoft.iot.vmp.conf.security;
2 2
3 import com.genersoft.iot.vmp.conf.UserSetting; 3 import com.genersoft.iot.vmp.conf.UserSetting;
4 -import org.springframework.core.annotation.Order;  
5 import org.slf4j.Logger; 4 import org.slf4j.Logger;
6 import org.slf4j.LoggerFactory; 5 import org.slf4j.LoggerFactory;
7 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.beans.factory.annotation.Autowired;
8 import org.springframework.context.annotation.Bean; 7 import org.springframework.context.annotation.Bean;
9 import org.springframework.context.annotation.Configuration; 8 import org.springframework.context.annotation.Configuration;
  9 +import org.springframework.core.annotation.Order;
10 import org.springframework.security.authentication.AuthenticationManager; 10 import org.springframework.security.authentication.AuthenticationManager;
11 import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 11 import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
12 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 12 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -25,9 +25,11 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @@ -25,9 +25,11 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
25 25
26 import java.util.ArrayList; 26 import java.util.ArrayList;
27 import java.util.Arrays; 27 import java.util.Arrays;
  28 +import java.util.Collections;
28 29
29 /** 30 /**
30 * 配置Spring Security 31 * 配置Spring Security
  32 + *
31 * @author lin 33 * @author lin
32 */ 34 */
33 @Configuration 35 @Configuration
@@ -67,6 +69,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -67,6 +69,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
67 matchers.add("/"); 69 matchers.add("/");
68 matchers.add("/#/**"); 70 matchers.add("/#/**");
69 matchers.add("/static/**"); 71 matchers.add("/static/**");
  72 + matchers.add("/swagger-ui.html");
  73 + matchers.add("/swagger-ui/");
70 matchers.add("/index.html"); 74 matchers.add("/index.html");
71 matchers.add("/doc.html"); 75 matchers.add("/doc.html");
72 matchers.add("/webjars/**"); 76 matchers.add("/webjars/**");
@@ -75,7 +79,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -75,7 +79,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
75 matchers.add("/js/**"); 79 matchers.add("/js/**");
76 matchers.add("/api/device/query/snap/**"); 80 matchers.add("/api/device/query/snap/**");
77 matchers.add("/record_proxy/*/**"); 81 matchers.add("/record_proxy/*/**");
78 - matchers.addAll(userSetting.getInterfaceAuthenticationExcludes()); 82 + matchers.add("/api/emit");
  83 + matchers.add("/favicon.ico");
79 // 可以直接访问的静态数据 84 // 可以直接访问的静态数据
80 web.ignoring().antMatchers(matchers.toArray(new String[0])); 85 web.ignoring().antMatchers(matchers.toArray(new String[0]));
81 } 86 }
@@ -83,6 +88,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -83,6 +88,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
83 88
84 /** 89 /**
85 * 配置认证方式 90 * 配置认证方式
  91 + *
86 * @param auth 92 * @param auth
87 * @throws Exception 93 * @throws Exception
88 */ 94 */
@@ -111,7 +117,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -111,7 +117,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
111 .authorizeRequests() 117 .authorizeRequests()
112 .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() 118 .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
113 .antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll() 119 .antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll()
114 - .antMatchers("/api/user/login","/index/hook/**","/zlm_Proxy/FhTuMYqB2HeCuNOb/record/t/1/2023-03-25/16:35:07-16:35:16-9353.mp4").permitAll() 120 + .antMatchers("/api/user/login", "/index/hook/**", "/swagger-ui/**", "/doc.html").permitAll()
115 .anyRequest().authenticated() 121 .anyRequest().authenticated()
116 // 异常处理器 122 // 异常处理器
117 .and() 123 .and()
@@ -124,18 +130,24 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -124,18 +130,24 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
124 130
125 } 131 }
126 132
127 - CorsConfigurationSource configurationSource(){ 133 + CorsConfigurationSource configurationSource() {
128 // 配置跨域 134 // 配置跨域
129 CorsConfiguration corsConfiguration = new CorsConfiguration(); 135 CorsConfiguration corsConfiguration = new CorsConfiguration();
130 corsConfiguration.setAllowedHeaders(Arrays.asList("*")); 136 corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
131 corsConfiguration.setAllowedMethods(Arrays.asList("*")); 137 corsConfiguration.setAllowedMethods(Arrays.asList("*"));
132 corsConfiguration.setMaxAge(3600L); 138 corsConfiguration.setMaxAge(3600L);
133 - corsConfiguration.setAllowCredentials(true);  
134 - corsConfiguration.setAllowedOrigins(userSetting.getAllowedOrigins()); 139 + if (userSetting.getAllowedOrigins() != null && !userSetting.getAllowedOrigins().isEmpty()) {
  140 + corsConfiguration.setAllowCredentials(true);
  141 + corsConfiguration.setAllowedOrigins(userSetting.getAllowedOrigins());
  142 + }else {
  143 + corsConfiguration.setAllowCredentials(false);
  144 + corsConfiguration.setAllowedOrigins(Collections.singletonList(CorsConfiguration.ALL));
  145 + }
  146 +
135 corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader())); 147 corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader()));
136 148
137 UrlBasedCorsConfigurationSource url = new UrlBasedCorsConfigurationSource(); 149 UrlBasedCorsConfigurationSource url = new UrlBasedCorsConfigurationSource();
138 - url.registerCorsConfiguration("/**",corsConfiguration); 150 + url.registerCorsConfiguration("/**", corsConfiguration);
139 return url; 151 return url;
140 } 152 }
141 153
src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java
@@ -21,6 +21,7 @@ public class JwtUser { @@ -21,6 +21,7 @@ public class JwtUser {
21 EXCEPTION 21 EXCEPTION
22 } 22 }
23 23
  24 + private int userId;
24 private String userName; 25 private String userName;
25 26
26 private String password; 27 private String password;
@@ -29,6 +30,14 @@ public class JwtUser { @@ -29,6 +30,14 @@ public class JwtUser {
29 30
30 private TokenStatus status; 31 private TokenStatus status;
31 32
  33 + public int getUserId() {
  34 + return userId;
  35 + }
  36 +
  37 + public void setUserId(int userId) {
  38 + this.userId = userId;
  39 + }
  40 +
32 public String getUserName() { 41 public String getUserName() {
33 return userName; 42 return userName;
34 } 43 }
src/main/java/com/genersoft/iot/vmp/gb28181/conf/StackLoggerImpl.java
1 package com.genersoft.iot.vmp.gb28181.conf; 1 package com.genersoft.iot.vmp.gb28181.conf;
2 2
3 import gov.nist.core.StackLogger; 3 import gov.nist.core.StackLogger;
4 -import org.slf4j.Logger;  
5 import org.slf4j.LoggerFactory; 4 import org.slf4j.LoggerFactory;
  5 +import org.slf4j.spi.LocationAwareLogger;
6 import org.springframework.stereotype.Component; 6 import org.springframework.stereotype.Component;
7 7
8 import java.util.Properties; 8 import java.util.Properties;
@@ -10,100 +10,132 @@ import java.util.Properties; @@ -10,100 +10,132 @@ import java.util.Properties;
10 @Component 10 @Component
11 public class StackLoggerImpl implements StackLogger { 11 public class StackLoggerImpl implements StackLogger {
12 12
13 - private final static Logger logger = LoggerFactory.getLogger(StackLoggerImpl.class); 13 + /**
  14 + * 完全限定类名(Fully Qualified Class Name),用于定位日志位置
  15 + */
  16 + private static final String FQCN = StackLoggerImpl.class.getName();
  17 +
  18 + /**
  19 + * 获取栈中类信息(以便底层日志记录系统能够提取正确的位置信息(方法名、行号))
  20 + * @return LocationAwareLogger
  21 + */
  22 + private static LocationAwareLogger getLocationAwareLogger() {
  23 + return (LocationAwareLogger) LoggerFactory.getLogger(new Throwable().getStackTrace()[4].getClassName());
  24 + }
  25 +
  26 +
  27 + /**
  28 + * 封装打印日志的位置信息
  29 + * @param level 日志级别
  30 + * @param message 日志事件的消息
  31 + */
  32 + private static void log(int level, String message) {
  33 + LocationAwareLogger locationAwareLogger = getLocationAwareLogger();
  34 + locationAwareLogger.log(null, FQCN, level, message, null, null);
  35 + }
  36 +
  37 + /**
  38 + * 封装打印日志的位置信息
  39 + * @param level 日志级别
  40 + * @param message 日志事件的消息
  41 + */
  42 + private static void log(int level, String message, Throwable throwable) {
  43 + LocationAwareLogger locationAwareLogger = getLocationAwareLogger();
  44 + locationAwareLogger.log(null, FQCN, level, message, null, throwable);
  45 + }
  46 +
  47 + @Override
  48 + public void logStackTrace() {
  49 +
  50 + }
  51 +
  52 + @Override
  53 + public void logStackTrace(int traceLevel) {
  54 + System.out.println("traceLevel: " + traceLevel);
  55 + }
  56 +
  57 + @Override
  58 + public int getLineCount() {
  59 + return 0;
  60 + }
  61 +
  62 + @Override
  63 + public void logException(Throwable ex) {
  64 +
  65 + }
  66 +
  67 + @Override
  68 + public void logDebug(String message) {
  69 + log(LocationAwareLogger.INFO_INT, message);
  70 + }
  71 +
  72 + @Override
  73 + public void logDebug(String message, Exception ex) {
  74 + log(LocationAwareLogger.INFO_INT, message, ex);
  75 + }
  76 +
  77 + @Override
  78 + public void logTrace(String message) {
  79 + log(LocationAwareLogger.INFO_INT, message);
  80 + }
  81 +
  82 + @Override
  83 + public void logFatalError(String message) {
  84 + log(LocationAwareLogger.INFO_INT, message);
  85 + }
  86 +
  87 + @Override
  88 + public void logError(String message) {
  89 + log(LocationAwareLogger.INFO_INT, message);
  90 + }
  91 +
  92 + @Override
  93 + public boolean isLoggingEnabled() {
  94 + return true;
  95 + }
  96 +
  97 + @Override
  98 + public boolean isLoggingEnabled(int logLevel) {
  99 + return true;
  100 + }
  101 +
  102 + @Override
  103 + public void logError(String message, Exception ex) {
  104 + log(LocationAwareLogger.INFO_INT, message, ex);
  105 + }
  106 +
  107 + @Override
  108 + public void logWarning(String message) {
  109 + log(LocationAwareLogger.INFO_INT, message);
  110 + }
  111 +
  112 + @Override
  113 + public void logInfo(String message) {
  114 + log(LocationAwareLogger.INFO_INT, message);
  115 + }
  116 +
  117 + @Override
  118 + public void disableLogging() {
  119 +
  120 + }
  121 +
  122 + @Override
  123 + public void enableLogging() {
  124 +
  125 + }
  126 +
  127 + @Override
  128 + public void setBuildTimeStamp(String buildTimeStamp) {
  129 +
  130 + }
  131 +
  132 + @Override
  133 + public void setStackProperties(Properties stackProperties) {
14 134
15 - @Override  
16 - public void logStackTrace() { 135 + }
17 136
18 - }  
19 -  
20 - @Override  
21 - public void logStackTrace(int traceLevel) {  
22 - System.out.println("traceLevel: " + traceLevel);  
23 - }  
24 -  
25 - @Override  
26 - public int getLineCount() {  
27 - return 0;  
28 - }  
29 -  
30 - @Override  
31 - public void logException(Throwable ex) {  
32 -  
33 - }  
34 -  
35 - @Override  
36 - public void logDebug(String message) {  
37 -// logger.debug(message);  
38 - }  
39 -  
40 - @Override  
41 - public void logDebug(String message, Exception ex) {  
42 -// logger.debug(message);  
43 - }  
44 -  
45 - @Override  
46 - public void logTrace(String message) {  
47 - logger.trace(message);  
48 - }  
49 -  
50 - @Override  
51 - public void logFatalError(String message) {  
52 -// logger.error(message);  
53 - }  
54 -  
55 - @Override  
56 - public void logError(String message) {  
57 -// logger.error(message);  
58 - }  
59 -  
60 - @Override  
61 - public boolean isLoggingEnabled() {  
62 - return true;  
63 - }  
64 -  
65 - @Override  
66 - public boolean isLoggingEnabled(int logLevel) {  
67 - return true;  
68 - }  
69 -  
70 - @Override  
71 - public void logError(String message, Exception ex) {  
72 -// logger.error(message);  
73 - }  
74 -  
75 - @Override  
76 - public void logWarning(String message) {  
77 - logger.warn(message);  
78 - }  
79 -  
80 - @Override  
81 - public void logInfo(String message) {  
82 - logger.info(message);  
83 - }  
84 -  
85 - @Override  
86 - public void disableLogging() {  
87 -  
88 - }  
89 -  
90 - @Override  
91 - public void enableLogging() {  
92 -  
93 - }  
94 -  
95 - @Override  
96 - public void setBuildTimeStamp(String buildTimeStamp) {  
97 -  
98 - }  
99 -  
100 - @Override  
101 - public void setStackProperties(Properties stackProperties) {  
102 -  
103 - }  
104 -  
105 - @Override  
106 - public String getLoggerName() {  
107 - return null;  
108 - } 137 + @Override
  138 + public String getLoggerName() {
  139 + return null;
  140 + }
109 } 141 }
src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java
1 package com.genersoft.iot.vmp.gb28181.event.alarm; 1 package com.genersoft.iot.vmp.gb28181.event.alarm;
2 2
  3 +import org.jetbrains.annotations.NotNull;
  4 +import org.slf4j.Logger;
  5 +import org.slf4j.LoggerFactory;
3 import org.springframework.context.ApplicationListener; 6 import org.springframework.context.ApplicationListener;
4 import org.springframework.stereotype.Component; 7 import org.springframework.stereotype.Component;
5 -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;  
6 -import java.io.IOException;  
7 -import java.util.Hashtable; 8 +
  9 +import java.io.PrintWriter;
8 import java.util.Iterator; 10 import java.util.Iterator;
9 import java.util.Map; 11 import java.util.Map;
10 -  
11 -import org.slf4j.Logger;  
12 -import org.slf4j.LoggerFactory; 12 +import java.util.concurrent.ConcurrentHashMap;
13 13
14 /** 14 /**
15 - * @description: 报警事件监听  
16 - * @author: lawrencehj  
17 - * @data: 2021-01-20 15 + * 报警事件监听器.
  16 + *
  17 + * @author lawrencehj
  18 + * @author <a href="mailto:xiaoQQya@126.com">xiaoQQya</a>
  19 + * @since 2021/01/20
18 */ 20 */
19 -  
20 @Component 21 @Component
21 public class AlarmEventListener implements ApplicationListener<AlarmEvent> { 22 public class AlarmEventListener implements ApplicationListener<AlarmEvent> {
22 23
23 - private final static Logger logger = LoggerFactory.getLogger(AlarmEventListener.class); 24 + private static final Logger logger = LoggerFactory.getLogger(AlarmEventListener.class);
24 25
25 - private static Map<String, SseEmitter> sseEmitters = new Hashtable<>(); 26 + private static final Map<String, PrintWriter> SSE_CACHE = new ConcurrentHashMap<>();
26 27
27 - public void addSseEmitters(String browserId, SseEmitter sseEmitter) {  
28 - sseEmitters.put(browserId, sseEmitter); 28 + public void addSseEmitter(String browserId, PrintWriter writer) {
  29 + SSE_CACHE.put(browserId, writer);
  30 + logger.info("SSE 在线数量: {}", SSE_CACHE.size());
  31 + }
  32 +
  33 + public void removeSseEmitter(String browserId, PrintWriter writer) {
  34 + SSE_CACHE.remove(browserId, writer);
  35 + logger.info("SSE 在线数量: {}", SSE_CACHE.size());
29 } 36 }
30 37
31 @Override 38 @Override
32 - public void onApplicationEvent(AlarmEvent event) { 39 + public void onApplicationEvent(@NotNull AlarmEvent event) {
33 if (logger.isDebugEnabled()) { 40 if (logger.isDebugEnabled()) {
34 - logger.debug("设备报警事件触发,deviceId:" + event.getAlarmInfo().getDeviceId() + ", "  
35 - + event.getAlarmInfo().getAlarmDescription()); 41 + logger.debug("设备报警事件触发, deviceId: {}, {}", event.getAlarmInfo().getDeviceId(), event.getAlarmInfo().getAlarmDescription());
36 } 42 }
37 - String msg = "<strong>设备编码:</strong> <i>" + event.getAlarmInfo().getDeviceId() + "</i>"  
38 - + "<br><strong>报警描述:</strong> <i>" + event.getAlarmInfo().getAlarmDescription() + "</i>"  
39 - + "<br><strong>报警时间:</strong> <i>" + event.getAlarmInfo().getAlarmTime() + "</i>"  
40 - + "<br><strong>报警位置:</strong> <i>" + event.getAlarmInfo().getLongitude() + "</i>"  
41 - + ", <i>" + event.getAlarmInfo().getLatitude() + "</i>";  
42 -  
43 - for (Iterator<Map.Entry<String, SseEmitter>> it = sseEmitters.entrySet().iterator(); it.hasNext();) {  
44 - Map.Entry<String, SseEmitter> emitter = it.next();  
45 - logger.info("推送到SSE连接,浏览器ID: " + emitter.getKey()); 43 +
  44 + String msg = "<strong>设备编号:</strong> <i>" + event.getAlarmInfo().getDeviceId() + "</i>"
  45 + + "<br><strong>通道编号:</strong> <i>" + event.getAlarmInfo().getChannelId() + "</i>"
  46 + + "<br><strong>报警描述:</strong> <i>" + event.getAlarmInfo().getAlarmDescription() + "</i>"
  47 + + "<br><strong>报警时间:</strong> <i>" + event.getAlarmInfo().getAlarmTime() + "</i>";
  48 +
  49 + for (Iterator<Map.Entry<String, PrintWriter>> it = SSE_CACHE.entrySet().iterator(); it.hasNext(); ) {
  50 + Map.Entry<String, PrintWriter> response = it.next();
  51 + logger.info("推送到 SSE 连接, 浏览器 ID: {}", response.getKey());
46 try { 52 try {
47 - emitter.getValue().send(msg);  
48 - } catch (IOException | IllegalStateException e) {  
49 - if (logger.isDebugEnabled()) {  
50 - logger.debug("SSE连接已关闭"); 53 + PrintWriter writer = response.getValue();
  54 +
  55 + if (writer.checkError()) {
  56 + it.remove();
  57 + continue;
51 } 58 }
52 - // 移除已关闭的连接 59 +
  60 + String sseMsg = "event:message\n" +
  61 + "data:" + msg + "\n" +
  62 + "\n";
  63 + writer.write(sseMsg);
  64 + writer.flush();
  65 + } catch (Exception e) {
53 it.remove(); 66 it.remove();
54 } 67 }
55 } 68 }
src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java
@@ -35,7 +35,7 @@ public class RecordEndEventListener implements ApplicationListener&lt;RecordEndEven @@ -35,7 +35,7 @@ public class RecordEndEventListener implements ApplicationListener&lt;RecordEndEven
35 int sumNum = event.getRecordInfo().getSumNum(); 35 int sumNum = event.getRecordInfo().getSumNum();
36 logger.info("录像查询完成事件触发,deviceId:{}, channelId: {}, 录像数量{}/{}条", event.getRecordInfo().getDeviceId(), 36 logger.info("录像查询完成事件触发,deviceId:{}, channelId: {}, 录像数量{}/{}条", event.getRecordInfo().getDeviceId(),
37 event.getRecordInfo().getChannelId(), count,sumNum); 37 event.getRecordInfo().getChannelId(), count,sumNum);
38 - if (handlerMap.size() > 0) { 38 + if (!handlerMap.isEmpty()) {
39 RecordEndEventHandler handler = handlerMap.get(deviceId + channelId); 39 RecordEndEventHandler handler = handlerMap.get(deviceId + channelId);
40 if (handler !=null){ 40 if (handler !=null){
41 handler.handler(event.getRecordInfo()); 41 handler.handler(event.getRecordInfo());
@@ -43,6 +43,9 @@ public class RecordEndEventListener implements ApplicationListener&lt;RecordEndEven @@ -43,6 +43,9 @@ public class RecordEndEventListener implements ApplicationListener&lt;RecordEndEven
43 handlerMap.remove(deviceId + channelId); 43 handlerMap.remove(deviceId + channelId);
44 } 44 }
45 } 45 }
  46 + }else {
  47 + logger.info("录像查询完成事件触发, 但是订阅为空,取消发送,deviceId:{}, channelId: {}",
  48 + event.getRecordInfo().getDeviceId(), event.getRecordInfo().getChannelId());
46 } 49 }
47 } 50 }
48 51
@@ -53,6 +56,7 @@ public class RecordEndEventListener implements ApplicationListener&lt;RecordEndEven @@ -53,6 +56,7 @@ public class RecordEndEventListener implements ApplicationListener&lt;RecordEndEven
53 * @param recordEndEventHandler 56 * @param recordEndEventHandler
54 */ 57 */
55 public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) { 58 public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) {
  59 + logger.info("录像查询事件添加监听,deviceId:{}, channelId: {}", device, channelId);
56 handlerMap.put(device + channelId, recordEndEventHandler); 60 handlerMap.put(device + channelId, recordEndEventHandler);
57 } 61 }
58 /** 62 /**
@@ -61,6 +65,7 @@ public class RecordEndEventListener implements ApplicationListener&lt;RecordEndEven @@ -61,6 +65,7 @@ public class RecordEndEventListener implements ApplicationListener&lt;RecordEndEven
61 * @param channelId 65 * @param channelId
62 */ 66 */
63 public void delEndEventHandler(String device, String channelId) { 67 public void delEndEventHandler(String device, String channelId) {
  68 + logger.info("录像查询事件移除监听,deviceId:{}, channelId: {}", device, channelId);
64 handlerMap.remove(device + channelId); 69 handlerMap.remove(device + channelId);
65 } 70 }
66 71
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
@@ -148,13 +148,13 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; { @@ -148,13 +148,13 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; {
148 if (event.getDeviceChannels() != null) { 148 if (event.getDeviceChannels() != null) {
149 deviceChannelList.addAll(event.getDeviceChannels()); 149 deviceChannelList.addAll(event.getDeviceChannels());
150 } 150 }
151 - if (event.getGbStreams() != null && event.getGbStreams().size() > 0){ 151 + if (event.getGbStreams() != null && !event.getGbStreams().isEmpty()){
152 for (GbStream gbStream : event.getGbStreams()) { 152 for (GbStream gbStream : event.getGbStreams()) {
153 deviceChannelList.add( 153 deviceChannelList.add(
154 gbStreamService.getDeviceChannelListByStreamWithStatus(gbStream, gbStream.getCatalogId(), parentPlatform)); 154 gbStreamService.getDeviceChannelListByStreamWithStatus(gbStream, gbStream.getCatalogId(), parentPlatform));
155 } 155 }
156 } 156 }
157 - if (deviceChannelList.size() > 0) { 157 + if (!deviceChannelList.isEmpty()) {
158 logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size()); 158 logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size());
159 try { 159 try {
160 sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), parentPlatform, deviceChannelList, subscribe, null); 160 sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), parentPlatform, deviceChannelList, subscribe, null);
@@ -163,10 +163,10 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; { @@ -163,10 +163,10 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; {
163 logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); 163 logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
164 } 164 }
165 } 165 }
166 - }else if (parentPlatformMap.keySet().size() > 0) { 166 + }else if (!parentPlatformMap.keySet().isEmpty()) {
167 for (String gbId : parentPlatformMap.keySet()) { 167 for (String gbId : parentPlatformMap.keySet()) {
168 List<ParentPlatform> parentPlatforms = parentPlatformMap.get(gbId); 168 List<ParentPlatform> parentPlatforms = parentPlatformMap.get(gbId);
169 - if (parentPlatforms != null && parentPlatforms.size() > 0) { 169 + if (parentPlatforms != null && !parentPlatforms.isEmpty()) {
170 for (ParentPlatform platform : parentPlatforms) { 170 for (ParentPlatform platform : parentPlatforms) {
171 SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId()); 171 SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId());
172 if (subscribeInfo == null) { 172 if (subscribeInfo == null) {
src/main/java/com/genersoft/iot/vmp/gb28181/session/SSRCFactory.java
@@ -38,7 +38,8 @@ public class SSRCFactory { @@ -38,7 +38,8 @@ public class SSRCFactory {
38 38
39 39
40 public void initMediaServerSSRC(String mediaServerId, Set<String> usedSet) { 40 public void initMediaServerSSRC(String mediaServerId, Set<String> usedSet) {
41 - String ssrcPrefix = sipConfig.getDomain().substring(3, 8); 41 + String sipDomain = sipConfig.getDomain();
  42 + String ssrcPrefix = sipDomain.length() >= 8 ? sipDomain.substring(3, 8) : sipDomain;
42 String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId; 43 String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId;
43 List<String> ssrcList = new ArrayList<>(); 44 List<String> ssrcList = new ArrayList<>();
44 for (int i = 1; i < MAX_STREAM_COUNT; i++) { 45 for (int i = 1; i < MAX_STREAM_COUNT; i++) {
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
@@ -75,6 +75,33 @@ public class VideoStreamSessionManager { @@ -75,6 +75,33 @@ public class VideoStreamSessionManager {
75 return (SsrcTransaction)redisTemplate.opsForValue().get(scanResult.get(0)); 75 return (SsrcTransaction)redisTemplate.opsForValue().get(scanResult.get(0));
76 } 76 }
77 77
  78 + public SsrcTransaction getSsrcTransactionByCallId(String callId){
  79 +
  80 + if (ObjectUtils.isEmpty(callId)) {
  81 + return null;
  82 + }
  83 + String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_*_*_" + callId+ "_*";
  84 + List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
  85 + if (!scanResult.isEmpty()) {
  86 + return (SsrcTransaction)redisTemplate.opsForValue().get(scanResult.get(0));
  87 + }else {
  88 + key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_*_*_play_*";
  89 + scanResult = RedisUtil.scan(redisTemplate, key);
  90 + if (scanResult.isEmpty()) {
  91 + return null;
  92 + }
  93 + for (Object keyObj : scanResult) {
  94 + SsrcTransaction ssrcTransaction = (SsrcTransaction)redisTemplate.opsForValue().get(keyObj);
  95 + if (ssrcTransaction.getSipTransactionInfo() != null &&
  96 + ssrcTransaction.getSipTransactionInfo().getCallId().equals(callId)) {
  97 + return ssrcTransaction;
  98 + }
  99 + }
  100 + return null;
  101 + }
  102 +
  103 + }
  104 +
78 public List<SsrcTransaction> getSsrcTransactionForAll(String deviceId, String channelId, String callId, String stream){ 105 public List<SsrcTransaction> getSsrcTransactionForAll(String deviceId, String channelId, String callId, String stream){
79 if (ObjectUtils.isEmpty(deviceId)) { 106 if (ObjectUtils.isEmpty(deviceId)) {
80 deviceId ="*"; 107 deviceId ="*";
@@ -117,8 +144,19 @@ public class VideoStreamSessionManager { @@ -117,8 +144,19 @@ public class VideoStreamSessionManager {
117 } 144 }
118 145
119 public void remove(String deviceId, String channelId, String stream) { 146 public void remove(String deviceId, String channelId, String stream) {
120 - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);  
121 - if (ssrcTransaction == null) { 147 + List<SsrcTransaction> ssrcTransactionList = getSsrcTransactionForAll(deviceId, channelId, null, stream);
  148 + if (ssrcTransactionList == null || ssrcTransactionList.isEmpty()) {
  149 + return;
  150 + }
  151 + for (SsrcTransaction ssrcTransaction : ssrcTransactionList) {
  152 + redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_"
  153 + + deviceId + "_" + channelId + "_" + ssrcTransaction.getCallId() + "_" + ssrcTransaction.getStream());
  154 + }
  155 + }
  156 +
  157 + public void removeByCallId(String deviceId, String channelId, String callId) {
  158 + SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null);
  159 + if (ssrcTransaction == null ) {
122 return; 160 return;
123 } 161 }
124 redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_" 162 redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_"
src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java
@@ -129,4 +129,6 @@ public class SipRunner implements CommandLineRunner { @@ -129,4 +129,6 @@ public class SipRunner implements CommandLineRunner {
129 } 129 }
130 } 130 }
131 } 131 }
  132 +
  133 +
132 } 134 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java
@@ -66,17 +66,17 @@ public class SIPSender { @@ -66,17 +66,17 @@ public class SIPSender {
66 // 添加错误订阅 66 // 添加错误订阅
67 if (errorEvent != null) { 67 if (errorEvent != null) {
68 sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> { 68 sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
69 - errorEvent.response(eventResult);  
70 sipSubscribe.removeErrorSubscribe(eventResult.callId); 69 sipSubscribe.removeErrorSubscribe(eventResult.callId);
71 sipSubscribe.removeOkSubscribe(eventResult.callId); 70 sipSubscribe.removeOkSubscribe(eventResult.callId);
  71 + errorEvent.response(eventResult);
72 })); 72 }));
73 } 73 }
74 // 添加订阅 74 // 添加订阅
75 if (okEvent != null) { 75 if (okEvent != null) {
76 sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> { 76 sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
77 - okEvent.response(eventResult);  
78 sipSubscribe.removeOkSubscribe(eventResult.callId); 77 sipSubscribe.removeOkSubscribe(eventResult.callId);
79 sipSubscribe.removeErrorSubscribe(eventResult.callId); 78 sipSubscribe.removeErrorSubscribe(eventResult.callId);
  79 + okEvent.response(eventResult);
80 }); 80 });
81 } 81 }
82 if ("TCP".equals(transport)) { 82 if ("TCP".equals(transport)) {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
@@ -164,6 +164,7 @@ public class SIPRequestHeaderProvider { @@ -164,6 +164,7 @@ public class SIPRequestHeaderProvider {
164 Request request = null; 164 Request request = null;
165 //请求行 165 //请求行
166 SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); 166 SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  167 +// SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
167 // via 168 // via
168 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); 169 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
169 ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag()); 170 ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
@@ -174,6 +175,7 @@ public class SIPRequestHeaderProvider { @@ -174,6 +175,7 @@ public class SIPRequestHeaderProvider {
174 FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag()); 175 FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
175 //to 176 //to
176 SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress()); 177 SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress());
  178 +// SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(),device.getHostAddress());
177 Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); 179 Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
178 ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag()); 180 ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag());
179 181
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -40,6 +40,7 @@ import javax.sip.SipFactory; @@ -40,6 +40,7 @@ import javax.sip.SipFactory;
40 import javax.sip.header.CallIdHeader; 40 import javax.sip.header.CallIdHeader;
41 import javax.sip.message.Request; 41 import javax.sip.message.Request;
42 import java.text.ParseException; 42 import java.text.ParseException;
  43 +import java.util.List;
43 44
44 /** 45 /**
45 * @description:设备能力接口,用于定义设备的控制、查询能力 46 * @description:设备能力接口,用于定义设备的控制、查询能力
@@ -374,7 +375,8 @@ public class SIPCommander implements ISIPCommander { @@ -374,7 +375,8 @@ public class SIPCommander implements ISIPCommander {
374 }), e -> { 375 }), e -> {
375 ResponseEvent responseEvent = (ResponseEvent) e.event; 376 ResponseEvent responseEvent = (ResponseEvent) e.event;
376 SIPResponse response = (SIPResponse) responseEvent.getResponse(); 377 SIPResponse response = (SIPResponse) responseEvent.getResponse();
377 - streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, 378 + String callId = response.getCallIdHeader().getCallId();
  379 + streamSession.put(device.getDeviceId(), channelId, callId, stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response,
378 InviteSessionType.PLAY); 380 InviteSessionType.PLAY);
379 okEvent.response(e); 381 okEvent.response(e);
380 }); 382 });
@@ -676,22 +678,26 @@ public class SIPCommander implements ISIPCommander { @@ -676,22 +678,26 @@ public class SIPCommander implements ISIPCommander {
676 */ 678 */
677 @Override 679 @Override
678 public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { 680 public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
679 - SsrcTransaction ssrcTransaction;  
680 - if (callId != null) {  
681 - ssrcTransaction = streamSession.getSsrcTransaction(null, null, callId, null);  
682 - }else {  
683 - ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, null, stream); 681 + if (device == null) {
  682 + logger.warn("[发送BYE] device为null");
  683 + return;
684 } 684 }
685 - if (ssrcTransaction == null) { 685 + List<SsrcTransaction> ssrcTransactionList = streamSession.getSsrcTransactionForAll(device.getDeviceId(), channelId, callId, stream);
  686 + if (ssrcTransactionList == null || ssrcTransactionList.isEmpty()) {
  687 + logger.info("[发送BYE] 未找到事务信息,设备: device: {}, channel: {}", device.getDeviceId(), channelId);
686 throw new SsrcTransactionNotFoundException(device.getDeviceId(), channelId, callId, stream); 688 throw new SsrcTransactionNotFoundException(device.getDeviceId(), channelId, callId, stream);
687 } 689 }
688 690
689 - mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());  
690 - mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());  
691 - streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); 691 + for (SsrcTransaction ssrcTransaction : ssrcTransactionList) {
  692 + logger.info("[发送BYE] 设备: device: {}, channel: {}, callId: {}", device.getDeviceId(), channelId, ssrcTransaction.getCallId());
  693 + mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
692 694
693 - Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo());  
694 - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent); 695 + mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
  696 + streamSession.removeByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getCallId());
  697 + Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo());
  698 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent);
  699 +
  700 + }
695 } 701 }
696 702
697 @Override 703 @Override
@@ -1000,7 +1006,7 @@ public class SIPCommander implements ISIPCommander { @@ -1000,7 +1006,7 @@ public class SIPCommander implements ISIPCommander {
1000 catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n"); 1006 catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
1001 catalogXml.append("</Query>\r\n"); 1007 catalogXml.append("</Query>\r\n");
1002 1008
1003 - 1009 +
1004 1010
1005 Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); 1011 Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
1006 1012
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -579,7 +579,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -579,7 +579,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
579 579
580 @Override 580 @Override
581 public void sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException { 581 public void sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException {
582 - if (parentPlatform == null || deviceChannels == null || deviceChannels.size() == 0 || subscribeInfo == null) { 582 + if (parentPlatform == null || deviceChannels == null || deviceChannels.isEmpty() || subscribeInfo == null) {
583 return; 583 return;
584 } 584 }
585 if (index == null) { 585 if (index == null) {
@@ -597,6 +597,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -597,6 +597,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
597 Integer finalIndex = index; 597 Integer finalIndex = index;
598 String catalogXmlContent = getCatalogXmlContentForCatalogAddOrUpdate(parentPlatform, channels, 598 String catalogXmlContent = getCatalogXmlContentForCatalogAddOrUpdate(parentPlatform, channels,
599 deviceChannels.size(), type, subscribeInfo); 599 deviceChannels.size(), type, subscribeInfo);
  600 + logger.info("[发送NOTIFY通知]类型: {},发送数量: {}", type, channels.size());
600 sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> { 601 sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> {
601 logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg); 602 logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg);
602 }, (eventResult -> { 603 }, (eventResult -> {
@@ -620,7 +621,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -620,7 +621,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
620 621
621 SIPRequest notifyRequest = headerProviderPlatformProvider.createNotifyRequest(parentPlatform, catalogXmlContent, subscribeInfo); 622 SIPRequest notifyRequest = headerProviderPlatformProvider.createNotifyRequest(parentPlatform, catalogXmlContent, subscribeInfo);
622 623
623 - sipSender.transmitRequest(parentPlatform.getDeviceIp(), notifyRequest); 624 + sipSender.transmitRequest(parentPlatform.getDeviceIp(), notifyRequest, errorEvent, okEvent);
624 } 625 }
625 626
626 private String getCatalogXmlContentForCatalogAddOrUpdate(ParentPlatform parentPlatform, List<DeviceChannel> channels, int sumNum, String type, SubscribeInfo subscribeInfo) { 627 private String getCatalogXmlContentForCatalogAddOrUpdate(ParentPlatform parentPlatform, List<DeviceChannel> channels, int sumNum, String type, SubscribeInfo subscribeInfo) {
@@ -632,9 +633,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -632,9 +633,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
632 .append("<CmdType>Catalog</CmdType>\r\n") 633 .append("<CmdType>Catalog</CmdType>\r\n")
633 .append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n") 634 .append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
634 .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n") 635 .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
635 - .append("<SumNum>1</SumNum>\r\n") 636 + .append("<SumNum>"+ sumNum +"</SumNum>\r\n")
636 .append("<DeviceList Num=\"" + channels.size() + "\">\r\n"); 637 .append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
637 - if (channels.size() > 0) { 638 + if (!channels.isEmpty()) {
638 for (DeviceChannel channel : channels) { 639 for (DeviceChannel channel : channels) {
639 if (parentPlatform.getServerGBId().equals(channel.getParentId())) { 640 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
640 channel.setParentId(parentPlatform.getDeviceGBId()); 641 channel.setParentId(parentPlatform.getDeviceGBId());
@@ -701,6 +702,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -701,6 +702,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
701 }else { 702 }else {
702 channels = deviceChannels.subList(index, deviceChannels.size()); 703 channels = deviceChannels.subList(index, deviceChannels.size());
703 } 704 }
  705 + logger.info("[发送NOTIFY通知]类型: {},发送数量: {}", type, channels.size());
704 Integer finalIndex = index; 706 Integer finalIndex = index;
705 String catalogXmlContent = getCatalogXmlContentForCatalogOther(parentPlatform, channels, type); 707 String catalogXmlContent = getCatalogXmlContentForCatalogOther(parentPlatform, channels, type);
706 sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> { 708 sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> {
@@ -747,13 +749,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -747,13 +749,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
747 if ( parentPlatform ==null) { 749 if ( parentPlatform ==null) {
748 return ; 750 return ;
749 } 751 }
  752 + logger.info("[国标级联] 发送录像数据通道: {}", recordInfo.getChannelId());
750 String characterSet = parentPlatform.getCharacterSet(); 753 String characterSet = parentPlatform.getCharacterSet();
751 StringBuffer recordXml = new StringBuffer(600); 754 StringBuffer recordXml = new StringBuffer(600);
752 recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n") 755 recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
753 .append("<Response>\r\n") 756 .append("<Response>\r\n")
754 .append("<CmdType>RecordInfo</CmdType>\r\n") 757 .append("<CmdType>RecordInfo</CmdType>\r\n")
755 .append("<SN>" +recordInfo.getSn() + "</SN>\r\n") 758 .append("<SN>" +recordInfo.getSn() + "</SN>\r\n")
756 - .append("<DeviceID>" + recordInfo.getChannelId() + "</DeviceID>\r\n") 759 + .append("<DeviceID>" + deviceChannel.getChannelId() + "</DeviceID>\r\n")
757 .append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n"); 760 .append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
758 if (recordInfo.getRecordList() == null ) { 761 if (recordInfo.getRecordList() == null ) {
759 recordXml.append("<RecordList Num=\"0\">\r\n"); 762 recordXml.append("<RecordList Num=\"0\">\r\n");
@@ -763,7 +766,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -763,7 +766,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
763 for (RecordItem recordItem : recordInfo.getRecordList()) { 766 for (RecordItem recordItem : recordInfo.getRecordList()) {
764 recordXml.append("<Item>\r\n"); 767 recordXml.append("<Item>\r\n");
765 if (deviceChannel != null) { 768 if (deviceChannel != null) {
766 - recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n") 769 + recordXml.append("<DeviceID>" + deviceChannel.getChannelId() + "</DeviceID>\r\n")
767 .append("<Name>" + recordItem.getName() + "</Name>\r\n") 770 .append("<Name>" + recordItem.getName() + "</Name>\r\n")
768 .append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n") 771 .append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n")
769 .append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n") 772 .append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n")
@@ -783,12 +786,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -783,12 +786,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
783 786
784 recordXml.append("</RecordList>\r\n") 787 recordXml.append("</RecordList>\r\n")
785 .append("</Response>\r\n"); 788 .append("</Response>\r\n");
786 - 789 + logger.info("[国标级联] 发送录像数据通道:{}, 内容: {}", recordInfo.getChannelId(), recordXml);
787 // callid 790 // callid
788 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); 791 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
789 792
790 Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, recordXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader); 793 Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, recordXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
791 - sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); 794 + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, eventResult -> {
  795 + logger.info("[国标级联] 发送录像数据通道:{}, 发送成功", recordInfo.getChannelId());
  796 + });
792 797
793 } 798 }
794 799
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
@@ -128,6 +128,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @@ -128,6 +128,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
128 redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), 128 redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(),
129 callIdHeader.getCallId(), null); 129 callIdHeader.getCallId(), null);
130 zlmServerFactory.stopSendRtpStream(mediaInfo, param); 130 zlmServerFactory.stopSendRtpStream(mediaInfo, param);
  131 + if (userSetting.getUseCustomSsrcForParentInvite()) {
  132 + mediaServerService.releaseSsrc(mediaInfo.getId(), sendRtpItem.getSsrc());
  133 + }
131 if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) { 134 if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
132 ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId()); 135 ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId());
133 if (platform != null) { 136 if (platform != null) {
@@ -167,14 +170,12 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @@ -167,14 +170,12 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
167 } 170 }
168 } 171 }
169 172
170 - // 发流端发送的停止  
171 - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);  
172 - if (ssrcTransaction == null ) {  
173 - logger.info("[收到bye] 但是无法获取推流信息和发流信息,忽略此请求");  
174 - logger.info(request.toString());  
175 - return;  
176 - }  
177 - 173 + // 可能是设备发送的停止
  174 + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransactionByCallId(callIdHeader.getCallId());
  175 + if (ssrcTransaction == null) {
  176 + return;
  177 + }
  178 + logger.info("[收到bye] 来自设备:{}, 通道已停止推流: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
178 179
179 ParentPlatform platform = platformService.queryPlatformByServerGBId(ssrcTransaction.getDeviceId()); 180 ParentPlatform platform = platformService.queryPlatformByServerGBId(ssrcTransaction.getDeviceId());
180 if (platform != null ) { 181 if (platform != null ) {
@@ -216,7 +217,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @@ -216,7 +217,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
216 if (mediaServerItem != null) { 217 if (mediaServerItem != null) {
217 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc()); 218 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc());
218 } 219 }
219 - streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.getStream()); 220 + streamSession.removeByCallId(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.getCallId());
220 if (ssrcTransaction.getType() == InviteSessionType.BROADCAST) { 221 if (ssrcTransaction.getType() == InviteSessionType.BROADCAST) {
221 // 查找来源的对讲设备,发送停止 222 // 查找来源的对讲设备,发送停止
222 Device sourceDevice = storager.queryVideoDeviceByPlatformIdAndChannelId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); 223 Device sourceDevice = storager.queryVideoDeviceByPlatformIdAndChannelId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -152,7 +152,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -152,7 +152,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
152 String requesterId = SipUtils.getUserIdFromFromHeader(request); 152 String requesterId = SipUtils.getUserIdFromFromHeader(request);
153 CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); 153 CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
154 if (requesterId == null || channelId == null) { 154 if (requesterId == null || channelId == null) {
155 - logger.info("无法从FromHeader的Address中获取到平台id,返回400"); 155 + logger.info("无法从请求中获取到平台id,返回400");
156 // 参数不全, 发400,请求错误 156 // 参数不全, 发400,请求错误
157 try { 157 try {
158 responseAck(request, Response.BAD_REQUEST); 158 responseAck(request, Response.BAD_REQUEST);
@@ -162,6 +162,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -162,6 +162,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
162 return; 162 return;
163 } 163 }
164 164
  165 + logger.info("[INVITE] requesterId: {}, callId: {}, 来自:{}:{}",
  166 + requesterId, callIdHeader.getCallId(), request.getRemoteAddress(), request.getRemotePort());
165 167
166 // 查询请求是否来自上级平台\设备 168 // 查询请求是否来自上级平台\设备
167 ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId); 169 ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
@@ -409,7 +411,16 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -409,7 +411,16 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
409 // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口 411 // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口
410 localPort = new Random().nextInt(65535) + 1; 412 localPort = new Random().nextInt(65535) + 1;
411 } 413 }
412 - content.append("m=video " + localPort + " RTP/AVP 96\r\n"); 414 + if (sendRtpItem.isTcp()) {
  415 + content.append("m=video " + localPort + " TCP/RTP/AVP 96\r\n");
  416 + if (!sendRtpItem.isTcpActive()) {
  417 + content.append("a=setup:active\r\n");
  418 + } else {
  419 + content.append("a=setup:passive\r\n");
  420 + }
  421 + }else {
  422 + content.append("m=video " + localPort + " RTP/AVP 96\r\n");
  423 + }
413 content.append("a=sendonly\r\n"); 424 content.append("a=sendonly\r\n");
414 content.append("a=rtpmap:96 PS/90000\r\n"); 425 content.append("a=rtpmap:96 PS/90000\r\n");
415 content.append("y=" + sendRtpItem.getSsrc() + "\r\n"); 426 content.append("y=" + sendRtpItem.getSsrc() + "\r\n");
@@ -524,7 +535,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -524,7 +535,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
524 } 535 }
525 }); 536 });
526 } else { 537 } else {
527 - 538 + sendRtpItem.setPlayType(InviteStreamType.PLAY);
  539 + String streamId = String.format("%s_%s", device.getDeviceId(), channelId);
  540 + sendRtpItem.setStreamId(streamId);
  541 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
528 SSRCInfo ssrcInfo = playService.play(mediaServerItem, device.getDeviceId(), channelId, ssrc, ((code, msg, data) -> { 542 SSRCInfo ssrcInfo = playService.play(mediaServerItem, device.getDeviceId(), channelId, ssrc, ((code, msg, data) -> {
529 if (code == InviteErrorCode.SUCCESS.getCode()) { 543 if (code == InviteErrorCode.SUCCESS.getCode()) {
530 hookEvent.run(code, msg, data); 544 hookEvent.run(code, msg, data);
@@ -536,9 +550,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -536,9 +550,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
536 errorEvent.run(code, msg, data); 550 errorEvent.run(code, msg, data);
537 } 551 }
538 })); 552 }));
539 - sendRtpItem.setPlayType(InviteStreamType.PLAY);  
540 - String streamId = String.format("%s_%s", device.getDeviceId(), channelId);  
541 - sendRtpItem.setStream(streamId);  
542 sendRtpItem.setSsrc(ssrcInfo.getSsrc()); 553 sendRtpItem.setSsrc(ssrcInfo.getSsrc());
543 redisCatchStorage.updateSendRTPSever(sendRtpItem); 554 redisCatchStorage.updateSendRTPSever(sendRtpItem);
544 555
@@ -721,8 +732,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -721,8 +732,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
721 zlmHttpHookSubscribe.removeSubscribe(hookSubscribe); 732 zlmHttpHookSubscribe.removeSubscribe(hookSubscribe);
722 dynamicTask.stop(callIdHeader.getCallId()); 733 dynamicTask.stop(callIdHeader.getCallId());
723 } 734 }
724 -  
725 -  
726 } else if ("push".equals(gbStream.getStreamType())) { 735 } else if ("push".equals(gbStream.getStreamType())) {
727 if (!platform.isStartOfflinePush()) { 736 if (!platform.isStartOfflinePush()) {
728 // 平台设置中关闭了拉起离线的推流则直接回复 737 // 平台设置中关闭了拉起离线的推流则直接回复
@@ -745,13 +754,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -745,13 +754,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
745 dynamicTask.startDelay(callIdHeader.getCallId(), () -> { 754 dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
746 logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream()); 755 logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream());
747 try { 756 try {
  757 + redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream());
748 mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream()); 758 mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
749 responseAck(request, Response.REQUEST_TIMEOUT); // 超时 759 responseAck(request, Response.REQUEST_TIMEOUT); // 超时
750 - } catch (SipException e) {  
751 - logger.error("未处理的异常 ", e);  
752 - } catch (InvalidArgumentException e) {  
753 - logger.error("未处理的异常 ", e);  
754 - } catch (ParseException e) { 760 + } catch (SipException | InvalidArgumentException | ParseException e) {
755 logger.error("未处理的异常 ", e); 761 logger.error("未处理的异常 ", e);
756 } 762 }
757 }, userSetting.getPlatformPlayTimeout()); 763 }, userSetting.getPlatformPlayTimeout());
@@ -762,6 +768,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -762,6 +768,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
762 // 添加在本机上线的通知 768 // 添加在本机上线的通知
763 mediaListManager.addChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream(), (app, stream, serverId) -> { 769 mediaListManager.addChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream(), (app, stream, serverId) -> {
764 dynamicTask.stop(callIdHeader.getCallId()); 770 dynamicTask.stop(callIdHeader.getCallId());
  771 + redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream());
765 if (serverId.equals(userSetting.getServerId())) { 772 if (serverId.equals(userSetting.getServerId())) {
766 SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId, 773 SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
767 app, stream, channelId, mediaTransmissionTCP, platform.isRtcp()); 774 app, stream, channelId, mediaTransmissionTCP, platform.isRtcp());
@@ -826,7 +833,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -826,7 +833,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
826 // 发送redis消息 833 // 发送redis消息
827 redisGbPlayMsgListener.sendMsg(streamPushItem.getServerId(), streamPushItem.getMediaServerId(), 834 redisGbPlayMsgListener.sendMsg(streamPushItem.getServerId(), streamPushItem.getMediaServerId(),
828 streamPushItem.getApp(), streamPushItem.getStream(), addressStr, port, ssrc, requesterId, 835 streamPushItem.getApp(), streamPushItem.getStream(), addressStr, port, ssrc, requesterId,
829 - channelId, mediaTransmissionTCP, platform.isRtcp(), null, responseSendItemMsg -> { 836 + channelId, mediaTransmissionTCP, platform.isRtcp(),platform.getName(), responseSendItemMsg -> {
830 SendRtpItem sendRtpItem = responseSendItemMsg.getSendRtpItem(); 837 SendRtpItem sendRtpItem = responseSendItemMsg.getSendRtpItem();
831 if (sendRtpItem == null || responseSendItemMsg.getMediaServerItem() == null) { 838 if (sendRtpItem == null || responseSendItemMsg.getMediaServerItem() == null) {
832 logger.warn("服务器端口资源不足"); 839 logger.warn("服务器端口资源不足");
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java
@@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.utils.SipUtils; @@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
13 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; 13 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
14 import com.genersoft.iot.vmp.service.IDeviceChannelService; 14 import com.genersoft.iot.vmp.service.IDeviceChannelService;
15 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 15 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  16 +import com.genersoft.iot.vmp.utils.DateUtil;
16 import org.dom4j.DocumentException; 17 import org.dom4j.DocumentException;
17 import org.dom4j.Element; 18 import org.dom4j.Element;
18 import org.slf4j.Logger; 19 import org.slf4j.Logger;
@@ -185,6 +186,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent @@ -185,6 +186,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
185 // 判断此通道是否存在 186 // 判断此通道是否存在
186 DeviceChannel deviceChannel = deviceChannelService.getOne(deviceId, channel.getChannelId()); 187 DeviceChannel deviceChannel = deviceChannelService.getOne(deviceId, channel.getChannelId());
187 if (deviceChannel != null) { 188 if (deviceChannel != null) {
  189 + logger.info("[增加通道] 已存在,不发送通知只更新,设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
188 channel.setId(deviceChannel.getId()); 190 channel.setId(deviceChannel.getId());
189 updateChannelMap.put(channel.getChannelId(), channel); 191 updateChannelMap.put(channel.getChannelId(), channel);
190 if (updateChannelMap.keySet().size() > 300) { 192 if (updateChannelMap.keySet().size() > 300) {
@@ -222,6 +224,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent @@ -222,6 +224,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
222 DeviceChannel deviceChannelForUpdate = deviceChannelService.getOne(deviceId, channel.getChannelId()); 224 DeviceChannel deviceChannelForUpdate = deviceChannelService.getOne(deviceId, channel.getChannelId());
223 if (deviceChannelForUpdate != null) { 225 if (deviceChannelForUpdate != null) {
224 channel.setId(deviceChannelForUpdate.getId()); 226 channel.setId(deviceChannelForUpdate.getId());
  227 + channel.setUpdateTime(DateUtil.getNow());
225 updateChannelMap.put(channel.getChannelId(), channel); 228 updateChannelMap.put(channel.getChannelId(), channel);
226 if (updateChannelMap.keySet().size() > 300) { 229 if (updateChannelMap.keySet().size() > 300) {
227 executeSaveForUpdate(); 230 executeSaveForUpdate();
@@ -244,11 +247,11 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent @@ -244,11 +247,11 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
244 // 转发变化信息 247 // 转发变化信息
245 eventPublisher.catalogEventPublish(null, channel, event); 248 eventPublisher.catalogEventPublish(null, channel, event);
246 249
247 - if (updateChannelMap.keySet().size() > 0  
248 - || addChannelMap.keySet().size() > 0  
249 - || updateChannelOnlineList.size() > 0  
250 - || updateChannelOfflineList.size() > 0  
251 - || deleteChannelList.size() > 0) { 250 + if (!updateChannelMap.keySet().isEmpty()
  251 + || !addChannelMap.keySet().isEmpty()
  252 + || !updateChannelOnlineList.isEmpty()
  253 + || !updateChannelOfflineList.isEmpty()
  254 + || !deleteChannelList.isEmpty()) {
252 255
253 if (!dynamicTask.contains(talkKey)) { 256 if (!dynamicTask.contains(talkKey)) {
254 dynamicTask.startDelay(talkKey, this::executeSave, 1000); 257 dynamicTask.startDelay(talkKey, this::executeSave, 1000);
@@ -262,16 +265,36 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent @@ -262,16 +265,36 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
262 } 265 }
263 266
264 private void executeSave(){ 267 private void executeSave(){
265 - executeSaveForAdd();  
266 - executeSaveForUpdate();  
267 - executeSaveForDelete();  
268 - executeSaveForOnline();  
269 - executeSaveForOffline(); 268 + try {
  269 + executeSaveForAdd();
  270 + } catch (Exception e) {
  271 + logger.error("[存储收到的增加通道] 异常: ", e );
  272 + }
  273 + try {
  274 + executeSaveForUpdate();
  275 + } catch (Exception e) {
  276 + logger.error("[存储收到的更新通道] 异常: ", e );
  277 + }
  278 + try {
  279 + executeSaveForDelete();
  280 + } catch (Exception e) {
  281 + logger.error("[存储收到的删除通道] 异常: ", e );
  282 + }
  283 + try {
  284 + executeSaveForOnline();
  285 + } catch (Exception e) {
  286 + logger.error("[存储收到的通道上线] 异常: ", e );
  287 + }
  288 + try {
  289 + executeSaveForOffline();
  290 + } catch (Exception e) {
  291 + logger.error("[存储收到的通道离线] 异常: ", e );
  292 + }
270 dynamicTask.stop(talkKey); 293 dynamicTask.stop(talkKey);
271 } 294 }
272 295
273 private void executeSaveForUpdate(){ 296 private void executeSaveForUpdate(){
274 - if (updateChannelMap.values().size() > 0) { 297 + if (!updateChannelMap.values().isEmpty()) {
275 ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(updateChannelMap.values()); 298 ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(updateChannelMap.values());
276 updateChannelMap.clear(); 299 updateChannelMap.clear();
277 deviceChannelService.batchUpdateChannel(deviceChannels); 300 deviceChannelService.batchUpdateChannel(deviceChannels);
@@ -280,7 +303,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent @@ -280,7 +303,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
280 } 303 }
281 304
282 private void executeSaveForAdd(){ 305 private void executeSaveForAdd(){
283 - if (addChannelMap.values().size() > 0) { 306 + if (!addChannelMap.values().isEmpty()) {
284 ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(addChannelMap.values()); 307 ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(addChannelMap.values());
285 addChannelMap.clear(); 308 addChannelMap.clear();
286 deviceChannelService.batchAddChannel(deviceChannels); 309 deviceChannelService.batchAddChannel(deviceChannels);
@@ -288,21 +311,21 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent @@ -288,21 +311,21 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
288 } 311 }
289 312
290 private void executeSaveForDelete(){ 313 private void executeSaveForDelete(){
291 - if (deleteChannelList.size() > 0) { 314 + if (!deleteChannelList.isEmpty()) {
292 deviceChannelService.deleteChannels(deleteChannelList); 315 deviceChannelService.deleteChannels(deleteChannelList);
293 deleteChannelList.clear(); 316 deleteChannelList.clear();
294 } 317 }
295 } 318 }
296 319
297 private void executeSaveForOnline(){ 320 private void executeSaveForOnline(){
298 - if (updateChannelOnlineList.size() > 0) { 321 + if (!updateChannelOnlineList.isEmpty()) {
299 deviceChannelService.channelsOnline(updateChannelOnlineList); 322 deviceChannelService.channelsOnline(updateChannelOnlineList);
300 updateChannelOnlineList.clear(); 323 updateChannelOnlineList.clear();
301 } 324 }
302 } 325 }
303 326
304 private void executeSaveForOffline(){ 327 private void executeSaveForOffline(){
305 - if (updateChannelOfflineList.size() > 0) { 328 + if (!updateChannelOfflineList.isEmpty()) {
306 deviceChannelService.channelsOffline(updateChannelOfflineList); 329 deviceChannelService.channelsOffline(updateChannelOfflineList);
307 updateChannelOfflineList.clear(); 330 updateChannelOfflineList.clear();
308 } 331 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
@@ -106,23 +106,27 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen @@ -106,23 +106,27 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
106 String title = registerFlag ? "[注册请求]": "[注销请求]"; 106 String title = registerFlag ? "[注册请求]": "[注销请求]";
107 logger.info(title + "设备:{}, 开始处理: {}", deviceId, requestAddress); 107 logger.info(title + "设备:{}, 开始处理: {}", deviceId, requestAddress);
108 if (device != null && 108 if (device != null &&
109 - device.getSipTransactionInfo() != null &&  
110 - request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) { 109 + device.getSipTransactionInfo() != null &&
  110 + request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
111 logger.info(title + "设备:{}, 注册续订: {}",device.getDeviceId(), device.getDeviceId()); 111 logger.info(title + "设备:{}, 注册续订: {}",device.getDeviceId(), device.getDeviceId());
112 - device.setExpires(request.getExpires().getExpires());  
113 - device.setIp(remoteAddressInfo.getIp());  
114 - device.setPort(remoteAddressInfo.getPort());  
115 - device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));  
116 - device.setLocalIp(request.getLocalAddress().getHostAddress());  
117 - Response registerOkResponse = getRegisterOkResponse(request);  
118 - // 判断TCP还是UDP  
119 - ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);  
120 - String transport = reqViaHeader.getTransport();  
121 - device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");  
122 - sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse);  
123 - device.setRegisterTime(DateUtil.getNow());  
124 - SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse);  
125 - deviceService.online(device, sipTransactionInfo); 112 + if (registerFlag) {
  113 + device.setExpires(request.getExpires().getExpires());
  114 + device.setIp(remoteAddressInfo.getIp());
  115 + device.setPort(remoteAddressInfo.getPort());
  116 + device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
  117 + device.setLocalIp(request.getLocalAddress().getHostAddress());
  118 + Response registerOkResponse = getRegisterOkResponse(request);
  119 + // 判断TCP还是UDP
  120 + ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
  121 + String transport = reqViaHeader.getTransport();
  122 + device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
  123 + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse);
  124 + device.setRegisterTime(DateUtil.getNow());
  125 + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse);
  126 + deviceService.online(device, sipTransactionInfo);
  127 + }else {
  128 + deviceService.offline(deviceId, "主动注销");
  129 + }
126 return; 130 return;
127 } 131 }
128 String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword(); 132 String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword();
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
@@ -61,7 +61,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp @@ -61,7 +61,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
61 return; 61 return;
62 } 62 }
63 SIPRequest request = (SIPRequest) evt.getRequest(); 63 SIPRequest request = (SIPRequest) evt.getRequest();
64 - logger.info("[收到心跳] device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); 64 + logger.info("[收到心跳] device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId());
65 65
66 // 回复200 OK 66 // 回复200 OK
67 try { 67 try {
@@ -76,10 +76,15 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp @@ -76,10 +76,15 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
76 76
77 RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress()); 77 RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress());
78 if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) { 78 if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) {
79 - logger.info("[心跳] 设备{}地址变化, 远程地址为: {}:{}", device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort()); 79 + logger.info("[收到心跳] 设备{}地址变化, 远程地址为: {}:{}", device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort());
80 device.setPort(remoteAddressInfo.getPort()); 80 device.setPort(remoteAddressInfo.getPort());
81 device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); 81 device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
82 device.setIp(remoteAddressInfo.getIp()); 82 device.setIp(remoteAddressInfo.getIp());
  83 + // 设备地址变化会引起目录订阅任务失效,需要重新添加
  84 + if (device.getSubscribeCycleForCatalog() > 0) {
  85 + deviceService.removeCatalogSubscribe(device);
  86 + deviceService.addCatalogSubscribe(device);
  87 + }
83 } 88 }
84 if (device.getKeepaliveTime() == null) { 89 if (device.getKeepaliveTime() == null) {
85 device.setKeepaliveIntervalTime(60); 90 device.setKeepaliveIntervalTime(60);
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java
@@ -102,6 +102,7 @@ public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent imp @@ -102,6 +102,7 @@ public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent imp
102 // 接收录像数据 102 // 接收录像数据
103 recordEndEventListener.addEndEventHandler(deviceChannel.getDeviceId(), channelId, (recordInfo)->{ 103 recordEndEventListener.addEndEventHandler(deviceChannel.getDeviceId(), channelId, (recordInfo)->{
104 try { 104 try {
  105 + logger.info("[国标级联] 录像查询收到数据, 通道: {},准备转发===", channelId);
105 cmderFroPlatform.recordInfo(deviceChannel, parentPlatform, request.getFromTag(), recordInfo); 106 cmderFroPlatform.recordInfo(deviceChannel, parentPlatform, request.getFromTag(), recordInfo);
106 } catch (SipException | InvalidArgumentException | ParseException e) { 107 } catch (SipException | InvalidArgumentException | ParseException e) {
107 logger.error("[命令发送失败] 国标级联 回复录像数据: {}", e.getMessage()); 108 logger.error("[命令发送失败] 国标级联 回复录像数据: {}", e.getMessage());
src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
@@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device; @@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
8 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; 8 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
9 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; 9 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
10 import com.genersoft.iot.vmp.utils.DateUtil; 10 import com.genersoft.iot.vmp.utils.DateUtil;
  11 +import org.apache.commons.lang3.StringUtils;
11 import org.apache.commons.lang3.math.NumberUtils; 12 import org.apache.commons.lang3.math.NumberUtils;
12 import org.dom4j.Attribute; 13 import org.dom4j.Attribute;
13 import org.dom4j.Document; 14 import org.dom4j.Document;
@@ -214,8 +215,11 @@ public class XmlUtil { @@ -214,8 +215,11 @@ public class XmlUtil {
214 return deviceChannel; 215 return deviceChannel;
215 } 216 }
216 Element nameElement = itemDevice.element("Name"); 217 Element nameElement = itemDevice.element("Name");
217 - if (nameElement != null) { 218 + // 当通道名称为空时,设置通道名称为通道编码,避免级联时因通道名称为空导致上级接收通道失败
  219 + if (nameElement != null && StringUtils.isNotBlank(nameElement.getText())) {
218 deviceChannel.setName(nameElement.getText()); 220 deviceChannel.setName(nameElement.getText());
  221 + } else {
  222 + deviceChannel.setName(channelId);
219 } 223 }
220 if(channelId.length() <= 8) { 224 if(channelId.length() <= 8) {
221 deviceChannel.setHasAudio(false); 225 deviceChannel.setHasAudio(false);
src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
@@ -2,40 +2,69 @@ package com.genersoft.iot.vmp.media.zlm; @@ -2,40 +2,69 @@ package com.genersoft.iot.vmp.media.zlm;
2 2
3 import com.alibaba.fastjson2.JSON; 3 import com.alibaba.fastjson2.JSON;
4 import com.alibaba.fastjson2.JSONObject; 4 import com.alibaba.fastjson2.JSONObject;
  5 +import com.genersoft.iot.vmp.conf.exception.ControllerException;
5 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 6 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  7 +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
6 import okhttp3.*; 8 import okhttp3.*;
7 import okhttp3.logging.HttpLoggingInterceptor; 9 import okhttp3.logging.HttpLoggingInterceptor;
8 import org.jetbrains.annotations.NotNull; 10 import org.jetbrains.annotations.NotNull;
9 import org.slf4j.Logger; 11 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory; 12 import org.slf4j.LoggerFactory;
11 import org.springframework.stereotype.Component; 13 import org.springframework.stereotype.Component;
  14 +import org.springframework.util.ObjectUtils;
12 15
13 import java.io.IOException; 16 import java.io.IOException;
14 import java.net.ConnectException; 17 import java.net.ConnectException;
  18 +import java.net.MalformedURLException;
  19 +import java.net.SocketTimeoutException;
  20 +import java.net.URL;
15 import java.util.HashMap; 21 import java.util.HashMap;
  22 +import java.util.List;
16 import java.util.Map; 23 import java.util.Map;
17 import java.util.Objects; 24 import java.util.Objects;
  25 +import java.util.concurrent.TimeUnit;
18 26
19 @Component 27 @Component
20 public class AssistRESTfulUtils { 28 public class AssistRESTfulUtils {
21 29
22 private final static Logger logger = LoggerFactory.getLogger(AssistRESTfulUtils.class); 30 private final static Logger logger = LoggerFactory.getLogger(AssistRESTfulUtils.class);
23 31
  32 +
  33 + private OkHttpClient client;
  34 +
  35 +
24 public interface RequestCallback{ 36 public interface RequestCallback{
25 void run(JSONObject response); 37 void run(JSONObject response);
26 } 38 }
27 39
28 private OkHttpClient getClient(){ 40 private OkHttpClient getClient(){
29 - OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();  
30 - if (logger.isDebugEnabled()) {  
31 - HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {  
32 - logger.debug("http请求参数:" + message);  
33 - });  
34 - logging.setLevel(HttpLoggingInterceptor.Level.BASIC);  
35 - // OkHttp進行添加攔截器loggingInterceptor  
36 - httpClientBuilder.addInterceptor(logging); 41 + return getClient(null);
  42 + }
  43 +
  44 + private OkHttpClient getClient(Integer readTimeOut){
  45 + if (client == null) {
  46 + if (readTimeOut == null) {
  47 + readTimeOut = 10;
  48 + }
  49 + OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
  50 + // 设置连接超时时间
  51 + httpClientBuilder.connectTimeout(8, TimeUnit.SECONDS);
  52 + // 设置读取超时时间
  53 + httpClientBuilder.readTimeout(readTimeOut,TimeUnit.SECONDS);
  54 + // 设置连接池
  55 + httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES));
  56 + if (logger.isDebugEnabled()) {
  57 + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
  58 + logger.debug("http请求参数:" + message);
  59 + });
  60 + logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
  61 + // OkHttp進行添加攔截器loggingInterceptor
  62 + httpClientBuilder.addInterceptor(logging);
  63 + }
  64 + client = httpClientBuilder.build();
37 } 65 }
38 - return httpClientBuilder.build(); 66 + return client;
  67 +
39 } 68 }
40 69
41 70
@@ -49,11 +78,11 @@ public class AssistRESTfulUtils { @@ -49,11 +78,11 @@ public class AssistRESTfulUtils {
49 logger.warn("未启用Assist服务"); 78 logger.warn("未启用Assist服务");
50 return null; 79 return null;
51 } 80 }
52 - StringBuffer stringBuffer = new StringBuffer();  
53 - stringBuffer.append(String.format("http://%s:%s/%s", mediaServerItem.getIp(), mediaServerItem.getRecordAssistPort(), api)); 81 + StringBuilder stringBuffer = new StringBuilder();
  82 + stringBuffer.append(api);
54 JSONObject responseJSON = null; 83 JSONObject responseJSON = null;
55 84
56 - if (param != null && param.keySet().size() > 0) { 85 + if (param != null && !param.keySet().isEmpty()) {
57 stringBuffer.append("?"); 86 stringBuffer.append("?");
58 int index = 1; 87 int index = 1;
59 for (String key : param.keySet()){ 88 for (String key : param.keySet()){
@@ -68,6 +97,7 @@ public class AssistRESTfulUtils { @@ -68,6 +97,7 @@ public class AssistRESTfulUtils {
68 } 97 }
69 98
70 String url = stringBuffer.toString(); 99 String url = stringBuffer.toString();
  100 + logger.info("[访问assist]: {}", url);
71 Request request = new Request.Builder() 101 Request request = new Request.Builder()
72 .get() 102 .get()
73 .url(url) 103 .url(url)
@@ -123,13 +153,93 @@ public class AssistRESTfulUtils { @@ -123,13 +153,93 @@ public class AssistRESTfulUtils {
123 return responseJSON; 153 return responseJSON;
124 } 154 }
125 155
  156 + public JSONObject sendPost(MediaServerItem mediaServerItem, String url,
  157 + JSONObject param, ZLMRESTfulUtils.RequestCallback callback,
  158 + Integer readTimeOut) {
  159 + OkHttpClient client = getClient(readTimeOut);
126 160
127 - public JSONObject fileDuration(MediaServerItem mediaServerItem, String app, String stream, RequestCallback callback){  
128 - Map<String, Object> param = new HashMap<>();  
129 - param.put("app",app);  
130 - param.put("stream",stream);  
131 - param.put("recordIng",true);  
132 - return sendGet(mediaServerItem, "api/record/file/duration",param, callback); 161 + if (mediaServerItem == null) {
  162 + return null;
  163 + }
  164 + logger.info("[访问assist]: {}, 参数: {}", url, param);
  165 + JSONObject responseJSON = new JSONObject();
  166 + //-2自定义流媒体 调用错误码
  167 + responseJSON.put("code",-2);
  168 + responseJSON.put("msg","ASSIST调用失败");
  169 +
  170 + RequestBody requestBodyJson = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), param.toString());
  171 +
  172 + Request request = new Request.Builder()
  173 + .post(requestBodyJson)
  174 + .url(url)
  175 + .addHeader("Content-Type", "application/json")
  176 + .build();
  177 + if (callback == null) {
  178 + try {
  179 + Response response = client.newCall(request).execute();
  180 + if (response.isSuccessful()) {
  181 + ResponseBody responseBody = response.body();
  182 + if (responseBody != null) {
  183 + String responseStr = responseBody.string();
  184 + responseJSON = JSON.parseObject(responseStr);
  185 + }
  186 + }else {
  187 + response.close();
  188 + Objects.requireNonNull(response.body()).close();
  189 + }
  190 + }catch (IOException e) {
  191 + logger.error(String.format("[ %s ]ASSIST请求失败: %s", url, e.getMessage()));
  192 +
  193 + if(e instanceof SocketTimeoutException){
  194 + //读取超时超时异常
  195 + logger.error(String.format("读取ASSIST数据失败: %s, %s", url, e.getMessage()));
  196 + }
  197 + if(e instanceof ConnectException){
  198 + //判断连接异常,我这里是报Failed to connect to 10.7.5.144
  199 + logger.error(String.format("连接ASSIST失败: %s, %s", url, e.getMessage()));
  200 + }
  201 +
  202 + }catch (Exception e){
  203 + logger.error(String.format("访问ASSIST失败: %s, %s", url, e.getMessage()));
  204 + }
  205 + }else {
  206 + client.newCall(request).enqueue(new Callback(){
  207 +
  208 + @Override
  209 + public void onResponse(@NotNull Call call, @NotNull Response response){
  210 + if (response.isSuccessful()) {
  211 + try {
  212 + String responseStr = Objects.requireNonNull(response.body()).string();
  213 + callback.run(JSON.parseObject(responseStr));
  214 + } catch (IOException e) {
  215 + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage()));
  216 + }
  217 +
  218 + }else {
  219 + response.close();
  220 + Objects.requireNonNull(response.body()).close();
  221 + }
  222 + }
  223 +
  224 + @Override
  225 + public void onFailure(@NotNull Call call, @NotNull IOException e) {
  226 + logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
  227 +
  228 + if(e instanceof SocketTimeoutException){
  229 + //读取超时超时异常
  230 + logger.error(String.format("读取ZLM数据失败: %s, %s", call.request().toString(), e.getMessage()));
  231 + }
  232 + if(e instanceof ConnectException){
  233 + //判断连接异常,我这里是报Failed to connect to 10.7.5.144
  234 + logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
  235 + }
  236 + }
  237 + });
  238 + }
  239 +
  240 +
  241 +
  242 + return responseJSON;
133 } 243 }
134 244
135 public JSONObject getInfo(MediaServerItem mediaServerItem, RequestCallback callback){ 245 public JSONObject getInfo(MediaServerItem mediaServerItem, RequestCallback callback){
@@ -137,33 +247,43 @@ public class AssistRESTfulUtils { @@ -137,33 +247,43 @@ public class AssistRESTfulUtils {
137 return sendGet(mediaServerItem, "api/record/info",param, callback); 247 return sendGet(mediaServerItem, "api/record/info",param, callback);
138 } 248 }
139 249
140 - public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){  
141 - Map<String, Object> param = new HashMap<>();  
142 - param.put("app",app);  
143 - param.put("stream",stream);  
144 - param.put("callId",callId);  
145 - return sendGet(mediaServerItem, "api/record/addStreamCallInfo",param, callback);  
146 - } 250 + public JSONObject addTask(MediaServerItem mediaServerItem, String app, String stream, String startTime,
  251 + String endTime, String callId, List<String> filePathList, String remoteHost) {
147 252
148 - public JSONObject getDateList(MediaServerItem mediaServerItem, String app, String stream, int year, int month) {  
149 - Map<String, Object> param = new HashMap<>();  
150 - param.put("app", app);  
151 - param.put("stream", stream);  
152 - param.put("year", year);  
153 - param.put("month", month);  
154 - return sendGet(mediaServerItem, "api/record/date/list", param, null); 253 + JSONObject videoTaskInfoJSON = new JSONObject();
  254 + videoTaskInfoJSON.put("app", app);
  255 + videoTaskInfoJSON.put("stream", stream);
  256 + videoTaskInfoJSON.put("startTime", startTime);
  257 + videoTaskInfoJSON.put("endTime", endTime);
  258 + videoTaskInfoJSON.put("callId", callId);
  259 + videoTaskInfoJSON.put("filePathList", filePathList);
  260 + if (!ObjectUtils.isEmpty(remoteHost)) {
  261 + videoTaskInfoJSON.put("remoteHost", remoteHost);
  262 + }
  263 + String urlStr = String.format("%s/api/record/file/download/task/add", remoteHost);;
  264 + return sendPost(mediaServerItem, urlStr, videoTaskInfoJSON, null, 30);
155 } 265 }
156 266
157 - public JSONObject getFileList(MediaServerItem mediaServerItem, int page, int count, String app, String stream,  
158 - String startTime, String endTime) { 267 + public JSONObject queryTaskList(MediaServerItem mediaServerItem, String app, String stream, String callId,
  268 + String taskId, Boolean isEnd, String scheme) {
159 Map<String, Object> param = new HashMap<>(); 269 Map<String, Object> param = new HashMap<>();
160 - param.put("app", app);  
161 - param.put("stream", stream);  
162 - param.put("page", page);  
163 - param.put("count", count);  
164 - param.put("startTime", startTime);  
165 - param.put("endTime", endTime);  
166 - return sendGet(mediaServerItem, "api/record/file/listWithDate", param, null); 270 + if (!ObjectUtils.isEmpty(app)) {
  271 + param.put("app", app);
  272 + }
  273 + if (!ObjectUtils.isEmpty(stream)) {
  274 + param.put("stream", stream);
  275 + }
  276 + if (!ObjectUtils.isEmpty(callId)) {
  277 + param.put("callId", callId);
  278 + }
  279 + if (!ObjectUtils.isEmpty(taskId)) {
  280 + param.put("taskId", taskId);
  281 + }
  282 + if (!ObjectUtils.isEmpty(isEnd)) {
  283 + param.put("isEnd", isEnd);
  284 + }
  285 + String urlStr = String.format("%s://%s:%s/api/record/file/download/task/list",
  286 + scheme, mediaServerItem.getIp(), mediaServerItem.getRecordAssistPort());;
  287 + return sendGet(mediaServerItem, urlStr, param, null);
167 } 288 }
168 -  
169 } 289 }
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -118,6 +118,9 @@ public class ZLMHttpHookListener { @@ -118,6 +118,9 @@ public class ZLMHttpHookListener {
118 private IUserService userService; 118 private IUserService userService;
119 119
120 @Autowired 120 @Autowired
  121 + private ICloudRecordService cloudRecordService;
  122 +
  123 + @Autowired
121 private VideoStreamSessionManager sessionManager; 124 private VideoStreamSessionManager sessionManager;
122 125
123 @Autowired 126 @Autowired
@@ -238,12 +241,6 @@ public class ZLMHttpHookListener { @@ -238,12 +241,6 @@ public class ZLMHttpHookListener {
238 streamAuthorityInfo.setSign(sign); 241 streamAuthorityInfo.setSign(sign);
239 // 鉴权通过 242 // 鉴权通过
240 redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo); 243 redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
241 - // 通知assist新的callId  
242 - if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) {  
243 - taskExecutor.execute(() -> {  
244 - assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);  
245 - });  
246 - }  
247 } 244 }
248 } else { 245 } else {
249 zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId()); 246 zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
@@ -269,51 +266,57 @@ public class ZLMHttpHookListener { @@ -269,51 +266,57 @@ public class ZLMHttpHookListener {
269 } else { 266 } else {
270 result.setEnable_mp4(userSetting.isRecordPushLive()); 267 result.setEnable_mp4(userSetting.isRecordPushLive());
271 } 268 }
272 - // 替换流地址  
273 - if ("rtp".equals(param.getApp()) && !mediaInfo.isRtpEnable()) {  
274 - String ssrc = String.format("%010d", Long.parseLong(param.getStream(), 16));;  
275 - InviteInfo inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc);  
276 - if (inviteInfo != null) {  
277 - result.setStream_replace(inviteInfo.getStream());  
278 - logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream());  
279 - }  
280 - }  
281 - List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());  
282 - if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {  
283 - String deviceId = ssrcTransactionForAll.get(0).getDeviceId();  
284 - String channelId = ssrcTransactionForAll.get(0).getChannelId();  
285 - DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);  
286 - if (deviceChannel != null) { 269 + // 国标流
  270 + if ("rtp".equals(param.getApp()) ) {
287 271
288 - result.setEnable_audio(deviceChannel.isHasAudio());  
289 - }  
290 - // 如果是录像下载就设置视频间隔十秒  
291 - if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {  
292 - result.setMp4_max_second(10);  
293 - result.setEnable_mp4(true);  
294 - }  
295 - // 如果是talk对讲,则默认获取声音  
296 - if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) {  
297 - result.setEnable_audio(true); 272 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
  273 +
  274 + // 单端口模式下修改流 ID
  275 + if (!mediaInfo.isRtpEnable() && inviteInfo == null) {
  276 + String ssrc = String.format("%010d", Long.parseLong(param.getStream(), 16));
  277 + inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc);
  278 + if (inviteInfo != null) {
  279 + result.setStream_replace(inviteInfo.getStream());
  280 + logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream());
  281 + }
298 } 282 }
299 283
300 - }  
301 - if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {  
302 - logger.info("推流时发现尚未设置录像路径,从assist服务中读取");  
303 - JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null);  
304 - if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0) {  
305 - JSONObject dataJson = info.getJSONObject("data");  
306 - if (dataJson != null) {  
307 - String recordPath = dataJson.getString("record");  
308 - userSetting.setRecordPath(recordPath);  
309 - result.setMp4_save_path(recordPath);  
310 - // 修改zlm中的录像路径  
311 - if (mediaInfo.isAutoConfig()) {  
312 - taskExecutor.execute(() -> {  
313 - mediaServerService.setZLMConfig(mediaInfo, false);  
314 - }); 284 + // 设置音频信息及录制信息
  285 + List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
  286 + if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
  287 +
  288 + // 为录制国标模拟一个鉴权信息, 方便后续写入录像文件时使用
  289 + StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
  290 + streamAuthorityInfo.setApp(param.getApp());
  291 + streamAuthorityInfo.setStream(ssrcTransactionForAll.get(0).getStream());
  292 + streamAuthorityInfo.setCallId(ssrcTransactionForAll.get(0).getSipTransactionInfo().getCallId());
  293 +
  294 + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), ssrcTransactionForAll.get(0).getStream(), streamAuthorityInfo);
  295 +
  296 + String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
  297 + String channelId = ssrcTransactionForAll.get(0).getChannelId();
  298 + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
  299 + if (deviceChannel != null) {
  300 + result.setEnable_audio(deviceChannel.isHasAudio());
  301 + }
  302 + // 如果是录像下载就设置视频间隔十秒
  303 + if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
  304 + // 获取录像的总时长,然后设置为这个视频的时长
  305 + InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, param.getStream());
  306 + if (inviteInfoForDownload != null && inviteInfoForDownload.getStreamInfo() != null) {
  307 + String startTime = inviteInfoForDownload.getStreamInfo().getStartTime();
  308 + String endTime = inviteInfoForDownload.getStreamInfo().getEndTime();
  309 + long difference = DateUtil.getDifference(startTime, endTime) / 1000;
  310 + result.setMp4_max_second((int) difference);
  311 + result.setEnable_mp4(true);
  312 + // 设置为2保证得到的mp4的时长是正常的
  313 + result.setModify_stamp(2);
315 } 314 }
316 } 315 }
  316 + // 如果是talk对讲,则默认获取声音
  317 + if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) {
  318 + result.setEnable_audio(true);
  319 + }
317 } 320 }
318 } 321 }
319 if (param.getApp().equalsIgnoreCase("rtp")) { 322 if (param.getApp().equalsIgnoreCase("rtp")) {
@@ -361,13 +364,11 @@ public class ZLMHttpHookListener { @@ -361,13 +364,11 @@ public class ZLMHttpHookListener {
361 364
362 List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks(); 365 List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
363 // TODO 重构此处逻辑 366 // TODO 重构此处逻辑
364 - boolean isPush = false;  
365 if (param.isRegist()) { 367 if (param.isRegist()) {
366 - // 处理流注册的鉴权信息 368 + // 处理流注册的鉴权信息, 流注销这里不再删除鉴权信息,下次来了新的鉴权信息会对就的进行覆盖
367 if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal() 369 if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
368 || param.getOriginType() == OriginType.RTSP_PUSH.ordinal() 370 || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
369 || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) { 371 || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
370 - isPush = true;  
371 StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream()); 372 StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
372 if (streamAuthorityInfo == null) { 373 if (streamAuthorityInfo == null) {
373 streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param); 374 streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
@@ -377,8 +378,6 @@ public class ZLMHttpHookListener { @@ -377,8 +378,6 @@ public class ZLMHttpHookListener {
377 } 378 }
378 redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo); 379 redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
379 } 380 }
380 - } else {  
381 - redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());  
382 } 381 }
383 382
384 if ("rtsp".equals(param.getSchema())) { 383 if ("rtsp".equals(param.getSchema())) {
@@ -460,35 +459,40 @@ public class ZLMHttpHookListener { @@ -460,35 +459,40 @@ public class ZLMHttpHookListener {
460 } else { 459 } else {
461 if (!"rtp".equals(param.getApp())) { 460 if (!"rtp".equals(param.getApp())) {
462 String type = OriginType.values()[param.getOriginType()].getType(); 461 String type = OriginType.values()[param.getOriginType()].getType();
463 - MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());  
464 -  
465 - if (mediaServerItem != null) {  
466 - if (param.isRegist()) {  
467 - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());  
468 - String callId = null;  
469 - if (streamAuthorityInfo != null) {  
470 - callId = streamAuthorityInfo.getCallId();  
471 - }  
472 - StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem,  
473 - param.getApp(), param.getStream(), param.getTracks(), callId);  
474 - param.setStreamInfo(new StreamContent(streamInfoByAppAndStream));  
475 - redisCatchStorage.addStream(mediaServerItem, type, param.getApp(), param.getStream(), param);  
476 - if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()  
477 - || param.getOriginType() == OriginType.RTMP_PUSH.ordinal()  
478 - || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {  
479 - param.setSeverId(userSetting.getServerId());  
480 - zlmMediaListManager.addPush(param);  
481 - }  
482 - } else {  
483 - // 兼容流注销时类型从redis记录获取  
484 - OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(  
485 - param.getApp(), param.getStream(), param.getMediaServerId());  
486 - if (onStreamChangedHookParam != null) {  
487 - type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();  
488 - redisCatchStorage.removeStream(mediaServerItem.getId(), type, param.getApp(), param.getStream()); 462 + if (param.isRegist()) {
  463 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(
  464 + param.getApp(), param.getStream());
  465 + String callId = null;
  466 + if (streamAuthorityInfo != null) {
  467 + callId = streamAuthorityInfo.getCallId();
  468 + }
  469 + StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaInfo,
  470 + param.getApp(), param.getStream(), tracks, callId);
  471 + param.setStreamInfo(new StreamContent(streamInfoByAppAndStream));
  472 + redisCatchStorage.addStream(mediaInfo, type, param.getApp(), param.getStream(), param);
  473 + if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
  474 + || param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
  475 + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
  476 + param.setSeverId(userSetting.getServerId());
  477 + zlmMediaListManager.addPush(param);
  478 +
  479 + // 冗余数据,自己系统中自用
  480 + redisCatchStorage.addPushListItem(param.getApp(), param.getStream(), param);
  481 + }
  482 + } else {
  483 + // 兼容流注销时类型从redis记录获取
  484 + OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
  485 + param.getApp(), param.getStream(), param.getMediaServerId());
  486 + if (onStreamChangedHookParam != null) {
  487 + type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
  488 + redisCatchStorage.removeStream(mediaInfo.getId(), type, param.getApp(), param.getStream());
  489 + if ("PUSH".equalsIgnoreCase(type)) {
  490 + // 冗余数据,自己系统中自用
  491 + redisCatchStorage.removePushListItem(param.getApp(), param.getStream(), param.getMediaServerId());
489 } 492 }
490 - GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());  
491 - if (gbStream != null) { 493 + }
  494 + GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
  495 + if (gbStream != null) {
492 // eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF); 496 // eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
493 } 497 }
494 zlmMediaListManager.removeMedia(param.getApp(), param.getStream()); 498 zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
@@ -513,7 +517,7 @@ public class ZLMHttpHookListener { @@ -513,7 +517,7 @@ public class ZLMHttpHookListener {
513 } 517 }
514 if (!param.isRegist()) { 518 if (!param.isRegist()) {
515 List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream()); 519 List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
516 - if (sendRtpItems.size() > 0) { 520 + if (!sendRtpItems.isEmpty()) {
517 for (SendRtpItem sendRtpItem : sendRtpItems) { 521 for (SendRtpItem sendRtpItem : sendRtpItems) {
518 if (sendRtpItem != null && sendRtpItem.getApp().equals(param.getApp())) { 522 if (sendRtpItem != null && sendRtpItem.getApp().equals(param.getApp())) {
519 String platformId = sendRtpItem.getPlatformId(); 523 String platformId = sendRtpItem.getPlatformId();
@@ -608,11 +612,15 @@ public class ZLMHttpHookListener { @@ -608,11 +612,15 @@ public class ZLMHttpHookListener {
608 if (info != null) { 612 if (info != null) {
609 cmder.streamByeCmd(device, inviteInfo.getChannelId(), 613 cmder.streamByeCmd(device, inviteInfo.getChannelId(),
610 inviteInfo.getStream(), null); 614 inviteInfo.getStream(), null);
  615 + }else {
  616 + logger.info("[无人观看] 未找到设备的点播信息: {}, 流:{}", inviteInfo.getDeviceId(), param.getStream());
611 } 617 }
612 } catch (InvalidArgumentException | ParseException | SipException | 618 } catch (InvalidArgumentException | ParseException | SipException |
613 SsrcTransactionNotFoundException e) { 619 SsrcTransactionNotFoundException e) {
614 logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage()); 620 logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage());
615 } 621 }
  622 + }else {
  623 + logger.info("[无人观看] 未找到设备: {},流:{}", inviteInfo.getDeviceId(), param.getStream());
616 } 624 }
617 625
618 inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), 626 inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
@@ -684,7 +692,7 @@ public class ZLMHttpHookListener { @@ -684,7 +692,7 @@ public class ZLMHttpHookListener {
684 String deviceId = s[0]; 692 String deviceId = s[0];
685 String channelId = s[1]; 693 String channelId = s[1];
686 Device device = redisCatchStorage.getDevice(deviceId); 694 Device device = redisCatchStorage.getDevice(deviceId);
687 - if (device == null) { 695 + if (device == null || !device.isOnLine()) {
688 defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg())); 696 defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
689 return defaultResult; 697 return defaultResult;
690 } 698 }
@@ -848,11 +856,33 @@ public class ZLMHttpHookListener { @@ -848,11 +856,33 @@ public class ZLMHttpHookListener {
848 taskExecutor.execute(() -> { 856 taskExecutor.execute(() -> {
849 JSONObject json = (JSONObject) JSON.toJSON(param); 857 JSONObject json = (JSONObject) JSON.toJSON(param);
850 List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout); 858 List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
851 - if (subscribes != null && subscribes.size() > 0) { 859 + if (subscribes != null && !subscribes.isEmpty()) {
  860 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  861 + subscribe.response(null, param);
  862 + }
  863 + }
  864 + });
  865 +
  866 + return HookResult.SUCCESS();
  867 + }
  868 +
  869 + /**
  870 + * 录像完成事件
  871 + */
  872 + @ResponseBody
  873 + @PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
  874 + public HookResult onRecordMp4(HttpServletRequest request, @RequestBody OnRecordMp4HookParam param) {
  875 + logger.info("[ZLM HOOK] 录像完成事件:{}->{}", param.getMediaServerId(), param.getFile_path());
  876 +
  877 + taskExecutor.execute(() -> {
  878 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_record_mp4);
  879 + if (subscribes != null && !subscribes.isEmpty()) {
852 for (ZlmHttpHookSubscribe.Event subscribe : subscribes) { 880 for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
853 subscribe.response(null, param); 881 subscribe.response(null, param);
854 } 882 }
855 } 883 }
  884 + cloudRecordService.addRecord(param);
  885 +
856 }); 886 });
857 887
858 return HookResult.SUCCESS(); 888 return HookResult.SUCCESS();
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -25,8 +25,6 @@ public class ZLMRESTfulUtils { @@ -25,8 +25,6 @@ public class ZLMRESTfulUtils {
25 25
26 private OkHttpClient client; 26 private OkHttpClient client;
27 27
28 -  
29 -  
30 public interface RequestCallback{ 28 public interface RequestCallback{
31 void run(JSONObject response); 29 void run(JSONObject response);
32 } 30 }
@@ -405,4 +403,14 @@ public class ZLMRESTfulUtils { @@ -405,4 +403,14 @@ public class ZLMRESTfulUtils {
405 param.put("stream_id", streamId); 403 param.put("stream_id", streamId);
406 return sendPost(mediaServerItem, "updateRtpServerSSRC",param, null); 404 return sendPost(mediaServerItem, "updateRtpServerSSRC",param, null);
407 } 405 }
  406 +
  407 + public JSONObject deleteRecordDirectory(MediaServerItem mediaServerItem, String app, String stream, String date, String fileName) {
  408 + Map<String, Object> param = new HashMap<>(1);
  409 + param.put("vhost", "__defaultVhost__");
  410 + param.put("app", app);
  411 + param.put("stream", stream);
  412 + param.put("period", date);
  413 + param.put("name", fileName);
  414 + return sendPost(mediaServerItem, "deleteRecordDirectory",param, null);
  415 + }
408 } 416 }
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java
@@ -57,4 +57,15 @@ public class HookSubscribeFactory { @@ -57,4 +57,15 @@ public class HookSubscribeFactory {
57 return hookSubscribe; 57 return hookSubscribe;
58 } 58 }
59 59
  60 + public static HookSubscribeForRecordMp4 on_record_mp4(String mediaServerId, String app, String stream) {
  61 + HookSubscribeForRecordMp4 hookSubscribe = new HookSubscribeForRecordMp4();
  62 + JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
  63 + subscribeKey.put("app", app);
  64 + subscribeKey.put("stream", stream);
  65 + subscribeKey.put("mediaServerId", mediaServerId);
  66 + hookSubscribe.setContent(subscribeKey);
  67 +
  68 + return hookSubscribe;
  69 + }
  70 +
60 } 71 }
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForRecordMp4.java 0 → 100755
  1 +package com.genersoft.iot.vmp.media.zlm.dto;
  2 +
  3 +import com.alibaba.fastjson2.JSONObject;
  4 +import com.alibaba.fastjson2.annotation.JSONField;
  5 +
  6 +import java.time.Instant;
  7 +
  8 +/**
  9 + * hook订阅-录像完成
  10 + * @author lin
  11 + */
  12 +public class HookSubscribeForRecordMp4 implements IHookSubscribe{
  13 +
  14 + private HookType hookType = HookType.on_record_mp4;
  15 +
  16 + private JSONObject content;
  17 +
  18 + @JSONField(format="yyyy-MM-dd HH:mm:ss")
  19 + private Instant expires;
  20 +
  21 + @Override
  22 + public HookType getHookType() {
  23 + return hookType;
  24 + }
  25 +
  26 + @Override
  27 + public JSONObject getContent() {
  28 + return content;
  29 + }
  30 +
  31 + public void setContent(JSONObject content) {
  32 + this.content = content;
  33 + }
  34 +
  35 + @Override
  36 + public Instant getExpires() {
  37 + return expires;
  38 + }
  39 +
  40 + @Override
  41 + public void setExpires(Instant expires) {
  42 + this.expires = expires;
  43 + }
  44 +}
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
@@ -80,9 +80,11 @@ public class MediaServerItem{ @@ -80,9 +80,11 @@ public class MediaServerItem{
80 @Schema(description = "是否是默认ZLM") 80 @Schema(description = "是否是默认ZLM")
81 private boolean defaultServer; 81 private boolean defaultServer;
82 82
83 - @Schema(description = "当前使用到的端口")  
84 - private int currentPort; 83 + @Schema(description = "录像存储时长")
  84 + private int recordDay;
85 85
  86 + @Schema(description = "录像存储路径")
  87 + private String recordPath;
86 88
87 public MediaServerItem() { 89 public MediaServerItem() {
88 } 90 }
@@ -269,14 +271,6 @@ public class MediaServerItem{ @@ -269,14 +271,6 @@ public class MediaServerItem{
269 this.updateTime = updateTime; 271 this.updateTime = updateTime;
270 } 272 }
271 273
272 - public int getCurrentPort() {  
273 - return currentPort;  
274 - }  
275 -  
276 - public void setCurrentPort(int currentPort) {  
277 - this.currentPort = currentPort;  
278 - }  
279 -  
280 public boolean isStatus() { 274 public boolean isStatus() {
281 return status; 275 return status;
282 } 276 }
@@ -308,4 +302,20 @@ public class MediaServerItem{ @@ -308,4 +302,20 @@ public class MediaServerItem{
308 public void setSendRtpPortRange(String sendRtpPortRange) { 302 public void setSendRtpPortRange(String sendRtpPortRange) {
309 this.sendRtpPortRange = sendRtpPortRange; 303 this.sendRtpPortRange = sendRtpPortRange;
310 } 304 }
  305 +
  306 + public int getRecordDay() {
  307 + return recordDay;
  308 + }
  309 +
  310 + public void setRecordDay(int recordDay) {
  311 + this.recordDay = recordDay;
  312 + }
  313 +
  314 + public String getRecordPath() {
  315 + return recordPath;
  316 + }
  317 +
  318 + public void setRecordPath(String recordPath) {
  319 + this.recordPath = recordPath;
  320 + }
311 } 321 }
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
@@ -7,6 +7,7 @@ public class HookResultForOnPublish extends HookResult{ @@ -7,6 +7,7 @@ public class HookResultForOnPublish extends HookResult{
7 private int mp4_max_second; 7 private int mp4_max_second;
8 private String mp4_save_path; 8 private String mp4_save_path;
9 private String stream_replace; 9 private String stream_replace;
  10 + private Integer modify_stamp;
10 11
11 public HookResultForOnPublish() { 12 public HookResultForOnPublish() {
12 } 13 }
@@ -60,14 +61,23 @@ public class HookResultForOnPublish extends HookResult{ @@ -60,14 +61,23 @@ public class HookResultForOnPublish extends HookResult{
60 this.stream_replace = stream_replace; 61 this.stream_replace = stream_replace;
61 } 62 }
62 63
  64 + public Integer getModify_stamp() {
  65 + return modify_stamp;
  66 + }
  67 +
  68 + public void setModify_stamp(Integer modify_stamp) {
  69 + this.modify_stamp = modify_stamp;
  70 + }
  71 +
63 @Override 72 @Override
64 public String toString() { 73 public String toString() {
65 return "HookResultForOnPublish{" + 74 return "HookResultForOnPublish{" +
66 "enable_audio=" + enable_audio + 75 "enable_audio=" + enable_audio +
67 ", enable_mp4=" + enable_mp4 + 76 ", enable_mp4=" + enable_mp4 +
68 ", mp4_max_second=" + mp4_max_second + 77 ", mp4_max_second=" + mp4_max_second +
69 - ", stream_replace=" + stream_replace +  
70 ", mp4_save_path='" + mp4_save_path + '\'' + 78 ", mp4_save_path='" + mp4_save_path + '\'' +
  79 + ", stream_replace='" + stream_replace + '\'' +
  80 + ", modify_stamp='" + modify_stamp + '\'' +
71 '}'; 81 '}';
72 } 82 }
73 } 83 }
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnRecordMp4HookParam.java 0 → 100755
  1 +package com.genersoft.iot.vmp.media.zlm.dto.hook;
  2 +
  3 +/**
  4 + * zlm hook事件中的on_rtp_server_timeout事件的参数
  5 + * @author lin
  6 + */
  7 +public class OnRecordMp4HookParam extends HookParam{
  8 + private String app;
  9 + private String stream;
  10 + private String file_name;
  11 + private String file_path;
  12 + private long file_size;
  13 + private String folder;
  14 + private String url;
  15 + private String vhost;
  16 + private long start_time;
  17 + private double time_len;
  18 +
  19 + public String getApp() {
  20 + return app;
  21 + }
  22 +
  23 + public void setApp(String app) {
  24 + this.app = app;
  25 + }
  26 +
  27 + public String getStream() {
  28 + return stream;
  29 + }
  30 +
  31 + public void setStream(String stream) {
  32 + this.stream = stream;
  33 + }
  34 +
  35 + public String getFile_name() {
  36 + return file_name;
  37 + }
  38 +
  39 + public void setFile_name(String file_name) {
  40 + this.file_name = file_name;
  41 + }
  42 +
  43 + public String getFile_path() {
  44 + return file_path;
  45 + }
  46 +
  47 + public void setFile_path(String file_path) {
  48 + this.file_path = file_path;
  49 + }
  50 +
  51 + public long getFile_size() {
  52 + return file_size;
  53 + }
  54 +
  55 + public void setFile_size(long file_size) {
  56 + this.file_size = file_size;
  57 + }
  58 +
  59 + public String getFolder() {
  60 + return folder;
  61 + }
  62 +
  63 + public void setFolder(String folder) {
  64 + this.folder = folder;
  65 + }
  66 +
  67 + public String getUrl() {
  68 + return url;
  69 + }
  70 +
  71 + public void setUrl(String url) {
  72 + this.url = url;
  73 + }
  74 +
  75 + public String getVhost() {
  76 + return vhost;
  77 + }
  78 +
  79 + public void setVhost(String vhost) {
  80 + this.vhost = vhost;
  81 + }
  82 +
  83 + public long getStart_time() {
  84 + return start_time;
  85 + }
  86 +
  87 + public void setStart_time(long start_time) {
  88 + this.start_time = start_time;
  89 + }
  90 +
  91 + public double getTime_len() {
  92 + return time_len;
  93 + }
  94 +
  95 + public void setTime_len(double time_len) {
  96 + this.time_len = time_len;
  97 + }
  98 +
  99 + @Override
  100 + public String toString() {
  101 + return "OnRecordMp4HookParam{" +
  102 + "app='" + app + '\'' +
  103 + ", stream='" + stream + '\'' +
  104 + ", file_name='" + file_name + '\'' +
  105 + ", file_path='" + file_path + '\'' +
  106 + ", file_size='" + file_size + '\'' +
  107 + ", folder='" + folder + '\'' +
  108 + ", url='" + url + '\'' +
  109 + ", vhost='" + vhost + '\'' +
  110 + ", start_time=" + start_time +
  111 + ", time_len=" + time_len +
  112 + '}';
  113 + }
  114 +}
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamChangedHookParam.java
@@ -120,17 +120,17 @@ public class OnStreamChangedHookParam extends HookParam{ @@ -120,17 +120,17 @@ public class OnStreamChangedHookParam extends HookParam{
120 /** 120 /**
121 * H264 = 0, H265 = 1, AAC = 2, G711A = 3, G711U = 4 121 * H264 = 0, H265 = 1, AAC = 2, G711A = 3, G711U = 4
122 */ 122 */
123 - private int codecId; 123 + private int codec_id;
124 124
125 /** 125 /**
126 * 编码类型名称 CodecAAC CodecH264 126 * 编码类型名称 CodecAAC CodecH264
127 */ 127 */
128 - private String codecIdName; 128 + private String codec_id_name;
129 129
130 /** 130 /**
131 * Video = 0, Audio = 1 131 * Video = 0, Audio = 1
132 */ 132 */
133 - private int codecType; 133 + private int codec_type;
134 134
135 /** 135 /**
136 * 轨道是否准备就绪 136 * 轨道是否准备就绪
@@ -140,17 +140,17 @@ public class OnStreamChangedHookParam extends HookParam{ @@ -140,17 +140,17 @@ public class OnStreamChangedHookParam extends HookParam{
140 /** 140 /**
141 * 音频采样位数 141 * 音频采样位数
142 */ 142 */
143 - private int sampleBit; 143 + private int sample_bit;
144 144
145 /** 145 /**
146 * 音频采样率 146 * 音频采样率
147 */ 147 */
148 - private int sampleRate; 148 + private int sample_rate;
149 149
150 /** 150 /**
151 * 视频fps 151 * 视频fps
152 */ 152 */
153 - private int fps; 153 + private float fps;
154 154
155 /** 155 /**
156 * 视频高 156 * 视频高
@@ -162,6 +162,31 @@ public class OnStreamChangedHookParam extends HookParam{ @@ -162,6 +162,31 @@ public class OnStreamChangedHookParam extends HookParam{
162 */ 162 */
163 private int width; 163 private int width;
164 164
  165 + /**
  166 + * 帧数
  167 + */
  168 + private int frames;
  169 +
  170 + /**
  171 + * 关键帧数
  172 + */
  173 + private int key_frames;
  174 +
  175 + /**
  176 + * GOP大小
  177 + */
  178 + private int gop_size;
  179 +
  180 + /**
  181 + * GOP间隔时长(ms)
  182 + */
  183 + private int gop_interval_ms;
  184 +
  185 + /**
  186 + * 丢帧率
  187 + */
  188 + private float loss;
  189 +
165 public int getChannels() { 190 public int getChannels() {
166 return channels; 191 return channels;
167 } 192 }
@@ -170,28 +195,28 @@ public class OnStreamChangedHookParam extends HookParam{ @@ -170,28 +195,28 @@ public class OnStreamChangedHookParam extends HookParam{
170 this.channels = channels; 195 this.channels = channels;
171 } 196 }
172 197
173 - public int getCodecId() {  
174 - return codecId; 198 + public int getCodec_id() {
  199 + return codec_id;
175 } 200 }
176 201
177 - public void setCodecId(int codecId) {  
178 - this.codecId = codecId; 202 + public void setCodec_id(int codec_id) {
  203 + this.codec_id = codec_id;
179 } 204 }
180 205
181 - public String getCodecIdName() {  
182 - return codecIdName; 206 + public String getCodec_id_name() {
  207 + return codec_id_name;
183 } 208 }
184 209
185 - public void setCodecIdName(String codecIdName) {  
186 - this.codecIdName = codecIdName; 210 + public void setCodec_id_name(String codec_id_name) {
  211 + this.codec_id_name = codec_id_name;
187 } 212 }
188 213
189 - public int getCodecType() {  
190 - return codecType; 214 + public int getCodec_type() {
  215 + return codec_type;
191 } 216 }
192 217
193 - public void setCodecType(int codecType) {  
194 - this.codecType = codecType; 218 + public void setCodec_type(int codec_type) {
  219 + this.codec_type = codec_type;
195 } 220 }
196 221
197 public boolean isReady() { 222 public boolean isReady() {
@@ -202,27 +227,27 @@ public class OnStreamChangedHookParam extends HookParam{ @@ -202,27 +227,27 @@ public class OnStreamChangedHookParam extends HookParam{
202 this.ready = ready; 227 this.ready = ready;
203 } 228 }
204 229
205 - public int getSampleBit() {  
206 - return sampleBit; 230 + public int getSample_bit() {
  231 + return sample_bit;
207 } 232 }
208 233
209 - public void setSampleBit(int sampleBit) {  
210 - this.sampleBit = sampleBit; 234 + public void setSample_bit(int sample_bit) {
  235 + this.sample_bit = sample_bit;
211 } 236 }
212 237
213 - public int getSampleRate() {  
214 - return sampleRate; 238 + public int getSample_rate() {
  239 + return sample_rate;
215 } 240 }
216 241
217 - public void setSampleRate(int sampleRate) {  
218 - this.sampleRate = sampleRate; 242 + public void setSample_rate(int sample_rate) {
  243 + this.sample_rate = sample_rate;
219 } 244 }
220 245
221 - public int getFps() { 246 + public float getFps() {
222 return fps; 247 return fps;
223 } 248 }
224 249
225 - public void setFps(int fps) { 250 + public void setFps(float fps) {
226 this.fps = fps; 251 this.fps = fps;
227 } 252 }
228 253
@@ -241,6 +266,46 @@ public class OnStreamChangedHookParam extends HookParam{ @@ -241,6 +266,46 @@ public class OnStreamChangedHookParam extends HookParam{
241 public void setWidth(int width) { 266 public void setWidth(int width) {
242 this.width = width; 267 this.width = width;
243 } 268 }
  269 +
  270 + public int getFrames() {
  271 + return frames;
  272 + }
  273 +
  274 + public void setFrames(int frames) {
  275 + this.frames = frames;
  276 + }
  277 +
  278 + public int getKey_frames() {
  279 + return key_frames;
  280 + }
  281 +
  282 + public void setKey_frames(int key_frames) {
  283 + this.key_frames = key_frames;
  284 + }
  285 +
  286 + public int getGop_size() {
  287 + return gop_size;
  288 + }
  289 +
  290 + public void setGop_size(int gop_size) {
  291 + this.gop_size = gop_size;
  292 + }
  293 +
  294 + public int getGop_interval_ms() {
  295 + return gop_interval_ms;
  296 + }
  297 +
  298 + public void setGop_interval_ms(int gop_interval_ms) {
  299 + this.gop_interval_ms = gop_interval_ms;
  300 + }
  301 +
  302 + public float getLoss() {
  303 + return loss;
  304 + }
  305 +
  306 + public void setLoss(float loss) {
  307 + this.loss = loss;
  308 + }
244 } 309 }
245 310
246 public static class OriginSock{ 311 public static class OriginSock{
src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java 0 → 100755
  1 +package com.genersoft.iot.vmp.service;
  2 +
  3 +import com.alibaba.fastjson2.JSONArray;
  4 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  5 +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
  6 +import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
  7 +import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
  8 +import com.github.pagehelper.PageInfo;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * 云端录像管理
  14 + * @author lin
  15 + */
  16 +public interface ICloudRecordService {
  17 +
  18 + /**
  19 + * 分页回去云端录像列表
  20 + */
  21 + PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems);
  22 +
  23 + /**
  24 + * 根据hook消息增加一条记录
  25 + */
  26 + void addRecord(OnRecordMp4HookParam param);
  27 +
  28 + /**
  29 + * 获取所有的日期
  30 + */
  31 + List<String> getDateList(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems);
  32 +
  33 + /**
  34 + * 添加合并任务
  35 + */
  36 + String addTask(String app, String stream, MediaServerItem mediaServerItem, String startTime,
  37 + String endTime, String callId, String remoteHost, boolean filterMediaServer);
  38 +
  39 +
  40 + /**
  41 + * 查询合并任务列表
  42 + */
  43 + JSONArray queryTask(String app, String stream, String callId, String taskId, String mediaServerId, Boolean isEnd, String scheme);
  44 +
  45 + /**
  46 + * 收藏视频,收藏的视频过期不会删除
  47 + */
  48 + int changeCollect(boolean result, String app, String stream, String mediaServerId, String startTime, String endTime, String callId);
  49 +
  50 + /**
  51 + * 添加指定录像收藏
  52 + */
  53 + int changeCollectById(Integer recordId, boolean result);
  54 +
  55 + /**
  56 + * 获取播放地址
  57 + */
  58 + DownloadFileInfo getPlayUrlPath(Integer recordId);
  59 +}
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
@@ -89,21 +89,12 @@ public interface IMediaServerService { @@ -89,21 +89,12 @@ public interface IMediaServerService {
89 89
90 void updateMediaServerKeepalive(String mediaServerId, ServerKeepaliveData data); 90 void updateMediaServerKeepalive(String mediaServerId, ServerKeepaliveData data);
91 91
92 - boolean checkRtpServer(MediaServerItem mediaServerItem, String rtp, String stream);  
93 -  
94 /** 92 /**
95 * 获取负载信息 93 * 获取负载信息
96 * @return 94 * @return
97 */ 95 */
98 MediaServerLoad getLoad(MediaServerItem mediaServerItem); 96 MediaServerLoad getLoad(MediaServerItem mediaServerItem);
99 97
100 - /**  
101 - * 按时间查找录像文件  
102 - */  
103 - List<RecordFile> getRecords(String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems); 98 + List<MediaServerItem> getAllWithAssistPort();
104 99
105 - /**  
106 - * 查找存在录像文件的时间  
107 - */  
108 - List<String> getRecordDates(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems);  
109 } 100 }
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
@@ -33,11 +33,6 @@ public interface IPlayService { @@ -33,11 +33,6 @@ public interface IPlayService {
33 33
34 MediaServerItem getNewMediaServerItem(Device device); 34 MediaServerItem getNewMediaServerItem(Device device);
35 35
36 - /**  
37 - * 获取包含assist服务的节点  
38 - */  
39 - MediaServerItem getNewMediaServerItemHasAssist(Device device);  
40 -  
41 void playBack(String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback); 36 void playBack(String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
42 void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback); 37 void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
43 void zlmServerOffline(String mediaServerId); 38 void zlmServerOffline(String mediaServerId);
@@ -72,5 +67,4 @@ public interface IPlayService { @@ -72,5 +67,4 @@ public interface IPlayService {
72 67
73 void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback); 68 void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback);
74 69
75 -  
76 } 70 }
src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java
@@ -114,4 +114,5 @@ public interface IStreamPushService { @@ -114,4 +114,5 @@ public interface IStreamPushService {
114 * @return 114 * @return
115 */ 115 */
116 ResourceBaseInfo getOverview(); 116 ResourceBaseInfo getOverview();
  117 +
117 } 118 }
src/main/java/com/genersoft/iot/vmp/service/bean/CloudRecordItem.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.bean;
  2 +
  3 +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
  4 +
  5 +/**
  6 + * 云端录像数据
  7 + */
  8 +public class CloudRecordItem {
  9 + /**
  10 + * 主键
  11 + */
  12 + private int id;
  13 +
  14 + /**
  15 + * 应用名
  16 + */
  17 + private String app;
  18 +
  19 + /**
  20 + * 流
  21 + */
  22 + private String stream;
  23 +
  24 + /**
  25 + * 健全ID
  26 + */
  27 + private String callId;
  28 +
  29 + /**
  30 + * 开始时间
  31 + */
  32 + private long startTime;
  33 +
  34 + /**
  35 + * 结束时间
  36 + */
  37 + private long endTime;
  38 +
  39 + /**
  40 + * ZLM Id
  41 + */
  42 + private String mediaServerId;
  43 +
  44 + /**
  45 + * 文件名称
  46 + */
  47 + private String fileName;
  48 +
  49 + /**
  50 + * 文件路径
  51 + */
  52 + private String filePath;
  53 +
  54 + /**
  55 + * 文件夹
  56 + */
  57 + private String folder;
  58 +
  59 + /**
  60 + * 收藏,收藏的文件不移除
  61 + */
  62 + private Boolean collect;
  63 +
  64 + /**
  65 + * 保留,收藏的文件不移除
  66 + */
  67 + private Boolean reserve;
  68 +
  69 + /**
  70 + * 文件大小
  71 + */
  72 + private long fileSize;
  73 +
  74 + /**
  75 + * 文件时长
  76 + */
  77 + private long timeLen;
  78 +
  79 + public static CloudRecordItem getInstance(OnRecordMp4HookParam param) {
  80 + CloudRecordItem cloudRecordItem = new CloudRecordItem();
  81 + cloudRecordItem.setApp(param.getApp());
  82 + cloudRecordItem.setStream(param.getStream());
  83 + cloudRecordItem.setStartTime(param.getStart_time()*1000);
  84 + cloudRecordItem.setFileName(param.getFile_name());
  85 + cloudRecordItem.setFolder(param.getFolder());
  86 + cloudRecordItem.setFileSize(param.getFile_size());
  87 + cloudRecordItem.setFilePath(param.getFile_path());
  88 + cloudRecordItem.setMediaServerId(param.getMediaServerId());
  89 + cloudRecordItem.setTimeLen((long) param.getTime_len() * 1000);
  90 + cloudRecordItem.setEndTime((param.getStart_time() + (long)param.getTime_len()) * 1000);
  91 + return cloudRecordItem;
  92 + }
  93 +
  94 + public int getId() {
  95 + return id;
  96 + }
  97 +
  98 + public void setId(int id) {
  99 + this.id = id;
  100 + }
  101 +
  102 + public String getApp() {
  103 + return app;
  104 + }
  105 +
  106 + public void setApp(String app) {
  107 + this.app = app;
  108 + }
  109 +
  110 + public String getStream() {
  111 + return stream;
  112 + }
  113 +
  114 + public void setStream(String stream) {
  115 + this.stream = stream;
  116 + }
  117 +
  118 + public String getCallId() {
  119 + return callId;
  120 + }
  121 +
  122 + public void setCallId(String callId) {
  123 + this.callId = callId;
  124 + }
  125 +
  126 + public long getStartTime() {
  127 + return startTime;
  128 + }
  129 +
  130 + public void setStartTime(long startTime) {
  131 + this.startTime = startTime;
  132 + }
  133 +
  134 + public long getEndTime() {
  135 + return endTime;
  136 + }
  137 +
  138 + public void setEndTime(long endTime) {
  139 + this.endTime = endTime;
  140 + }
  141 +
  142 + public String getMediaServerId() {
  143 + return mediaServerId;
  144 + }
  145 +
  146 + public void setMediaServerId(String mediaServerId) {
  147 + this.mediaServerId = mediaServerId;
  148 + }
  149 +
  150 + public String getFileName() {
  151 + return fileName;
  152 + }
  153 +
  154 + public void setFileName(String fileName) {
  155 + this.fileName = fileName;
  156 + }
  157 +
  158 + public String getFilePath() {
  159 + return filePath;
  160 + }
  161 +
  162 + public void setFilePath(String filePath) {
  163 + this.filePath = filePath;
  164 + }
  165 +
  166 + public String getFolder() {
  167 + return folder;
  168 + }
  169 +
  170 + public void setFolder(String folder) {
  171 + this.folder = folder;
  172 + }
  173 +
  174 + public long getFileSize() {
  175 + return fileSize;
  176 + }
  177 +
  178 + public void setFileSize(long fileSize) {
  179 + this.fileSize = fileSize;
  180 + }
  181 +
  182 + public long getTimeLen() {
  183 + return timeLen;
  184 + }
  185 +
  186 + public void setTimeLen(long timeLen) {
  187 + this.timeLen = timeLen;
  188 + }
  189 +
  190 + public Boolean getCollect() {
  191 + return collect;
  192 + }
  193 +
  194 + public void setCollect(Boolean collect) {
  195 + this.collect = collect;
  196 + }
  197 +
  198 + public Boolean getReserve() {
  199 + return reserve;
  200 + }
  201 +
  202 + public void setReserve(Boolean reserve) {
  203 + this.reserve = reserve;
  204 + }
  205 +}
src/main/java/com/genersoft/iot/vmp/service/bean/DownloadFileInfo.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.bean;
  2 +
  3 +public class DownloadFileInfo {
  4 +
  5 + private String httpPath;
  6 + private String httpsPath;
  7 + private String httpDomainPath;
  8 + private String httpsDomainPath;
  9 +
  10 + public String getHttpPath() {
  11 + return httpPath;
  12 + }
  13 +
  14 + public void setHttpPath(String httpPath) {
  15 + this.httpPath = httpPath;
  16 + }
  17 +
  18 + public String getHttpsPath() {
  19 + return httpsPath;
  20 + }
  21 +
  22 + public void setHttpsPath(String httpsPath) {
  23 + this.httpsPath = httpsPath;
  24 + }
  25 +
  26 + public String getHttpDomainPath() {
  27 + return httpDomainPath;
  28 + }
  29 +
  30 + public void setHttpDomainPath(String httpDomainPath) {
  31 + this.httpDomainPath = httpDomainPath;
  32 + }
  33 +
  34 + public String getHttpsDomainPath() {
  35 + return httpsDomainPath;
  36 + }
  37 +
  38 + public void setHttpsDomainPath(String httpsDomainPath) {
  39 + this.httpsDomainPath = httpsDomainPath;
  40 + }
  41 +}
src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsg.java
@@ -29,12 +29,12 @@ public class WvpRedisMsg { @@ -29,12 +29,12 @@ public class WvpRedisMsg {
29 * 消息的ID 29 * 消息的ID
30 */ 30 */
31 private String serial; 31 private String serial;
32 - private Object content; 32 + private String content;
33 33
34 private final static String requestTag = "req"; 34 private final static String requestTag = "req";
35 private final static String responseTag = "res"; 35 private final static String responseTag = "res";
36 36
37 - public static WvpRedisMsg getRequestInstance(String fromId, String toId, String cmd, String serial, Object content) { 37 + public static WvpRedisMsg getRequestInstance(String fromId, String toId, String cmd, String serial, String content) {
38 WvpRedisMsg wvpRedisMsg = new WvpRedisMsg(); 38 WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
39 wvpRedisMsg.setType(requestTag); 39 wvpRedisMsg.setType(requestTag);
40 wvpRedisMsg.setFromId(fromId); 40 wvpRedisMsg.setFromId(fromId);
@@ -51,7 +51,7 @@ public class WvpRedisMsg { @@ -51,7 +51,7 @@ public class WvpRedisMsg {
51 return wvpRedisMsg; 51 return wvpRedisMsg;
52 } 52 }
53 53
54 - public static WvpRedisMsg getResponseInstance(String fromId, String toId, String cmd, String serial, Object content) { 54 + public static WvpRedisMsg getResponseInstance(String fromId, String toId, String cmd, String serial, String content) {
55 WvpRedisMsg wvpRedisMsg = new WvpRedisMsg(); 55 WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
56 wvpRedisMsg.setType(responseTag); 56 wvpRedisMsg.setType(responseTag);
57 wvpRedisMsg.setFromId(fromId); 57 wvpRedisMsg.setFromId(fromId);
@@ -106,11 +106,11 @@ public class WvpRedisMsg { @@ -106,11 +106,11 @@ public class WvpRedisMsg {
106 this.cmd = cmd; 106 this.cmd = cmd;
107 } 107 }
108 108
109 - public Object getContent() { 109 + public String getContent() {
110 return content; 110 return content;
111 } 111 }
112 112
113 - public void setContent(Object content) { 113 + public void setContent(String content) {
114 this.content = content; 114 this.content = content;
115 } 115 }
116 } 116 }
src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.impl;
  2 +
  3 +import com.alibaba.fastjson2.JSONArray;
  4 +import com.alibaba.fastjson2.JSONObject;
  5 +import com.baomidou.dynamic.datasource.annotation.DS;
  6 +import com.genersoft.iot.vmp.conf.exception.ControllerException;
  7 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  8 +import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
  9 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  10 +import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
  11 +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
  12 +import com.genersoft.iot.vmp.service.ICloudRecordService;
  13 +import com.genersoft.iot.vmp.service.IMediaServerService;
  14 +import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
  15 +import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
  16 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  17 +import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
  18 +import com.genersoft.iot.vmp.utils.CloudRecordUtils;
  19 +import com.genersoft.iot.vmp.utils.DateUtil;
  20 +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
  21 +import com.github.pagehelper.PageHelper;
  22 +import com.github.pagehelper.PageInfo;
  23 +import org.apache.commons.lang3.ObjectUtils;
  24 +import org.slf4j.Logger;
  25 +import org.slf4j.LoggerFactory;
  26 +import org.springframework.beans.factory.annotation.Autowired;
  27 +import org.springframework.stereotype.Service;
  28 +
  29 +import java.time.*;
  30 +import java.util.*;
  31 +
  32 +@Service
  33 +@DS("share")
  34 +public class CloudRecordServiceImpl implements ICloudRecordService {
  35 +
  36 + private final static Logger logger = LoggerFactory.getLogger(CloudRecordServiceImpl.class);
  37 +
  38 + @Autowired
  39 + private CloudRecordServiceMapper cloudRecordServiceMapper;
  40 +
  41 + @Autowired
  42 + private IMediaServerService mediaServerService;
  43 +
  44 + @Autowired
  45 + private IRedisCatchStorage redisCatchStorage;
  46 +
  47 + @Autowired
  48 + private AssistRESTfulUtils assistRESTfulUtils;
  49 +
  50 + @Autowired
  51 + private VideoStreamSessionManager streamSession;
  52 +
  53 + @Override
  54 + public PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems) {
  55 + // 开始时间和结束时间在数据库中都是以秒为单位的
  56 + Long startTimeStamp = null;
  57 + Long endTimeStamp = null;
  58 + if (startTime != null ) {
  59 + if (!DateUtil.verification(startTime, DateUtil.formatter)) {
  60 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "开始时间格式错误,正确格式为: " + DateUtil.formatter);
  61 + }
  62 + startTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
  63 +
  64 + }
  65 + if (endTime != null ) {
  66 + if (!DateUtil.verification(endTime, DateUtil.formatter)) {
  67 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "结束时间格式错误,正确格式为: " + DateUtil.formatter);
  68 + }
  69 + endTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
  70 +
  71 + }
  72 + PageHelper.startPage(page, count);
  73 + List<CloudRecordItem> all = cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
  74 + null, mediaServerItems);
  75 + return new PageInfo<>(all);
  76 + }
  77 +
  78 + @Override
  79 + public List<String> getDateList(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems) {
  80 + LocalDate startDate = LocalDate.of(year, month, 1);
  81 + LocalDate endDate;
  82 + if (month == 12) {
  83 + endDate = LocalDate.of(year + 1, 1, 1);
  84 + }else {
  85 + endDate = LocalDate.of(year, month + 1, 1);
  86 + }
  87 + long startTimeStamp = startDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
  88 + long endTimeStamp = endDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
  89 + List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp,
  90 + endTimeStamp, null, mediaServerItems);
  91 + if (cloudRecordItemList.isEmpty()) {
  92 + return new ArrayList<>();
  93 + }
  94 + Set<String> resultSet = new HashSet<>();
  95 + cloudRecordItemList.stream().forEach(cloudRecordItem -> {
  96 + String date = DateUtil.timestampTo_yyyy_MM_dd(cloudRecordItem.getStartTime());
  97 + resultSet.add(date);
  98 + });
  99 + return new ArrayList<>(resultSet);
  100 + }
  101 +
  102 + @Override
  103 + public void addRecord(OnRecordMp4HookParam param) {
  104 + CloudRecordItem cloudRecordItem = CloudRecordItem.getInstance(param);
  105 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
  106 + if (streamAuthorityInfo != null) {
  107 + cloudRecordItem.setCallId(streamAuthorityInfo.getCallId());
  108 + }
  109 + logger.info("[添加录像记录] {}/{} 文件大小:{}, 时长: {}秒", param.getApp(), param.getStream(), param.getFile_size(),param.getTime_len());
  110 + cloudRecordServiceMapper.add(cloudRecordItem);
  111 + }
  112 +
  113 + @Override
  114 + public String addTask(String app, String stream, MediaServerItem mediaServerItem, String startTime, String endTime,
  115 + String callId, String remoteHost, boolean filterMediaServer) {
  116 + // 参数校验
  117 + assert app != null;
  118 + assert stream != null;
  119 + if (mediaServerItem.getRecordAssistPort() == 0) {
  120 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "为配置Assist服务");
  121 + }
  122 + Long startTimeStamp = null;
  123 + Long endTimeStamp = null;
  124 + if (startTime != null) {
  125 + startTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
  126 + }
  127 + if (endTime != null) {
  128 + endTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
  129 + }
  130 +
  131 + List<MediaServerItem> mediaServers = new ArrayList<>();
  132 + mediaServers.add(mediaServerItem);
  133 + // 检索相关的录像文件
  134 + List<String> filePathList = cloudRecordServiceMapper.queryRecordFilePathList(app, stream, startTimeStamp,
  135 + endTimeStamp, callId, filterMediaServer ? mediaServers : null);
  136 + if (filePathList == null || filePathList.isEmpty()) {
  137 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未检索到视频文件");
  138 + }
  139 + JSONObject result = assistRESTfulUtils.addTask(mediaServerItem, app, stream, startTime, endTime, callId, filePathList, remoteHost);
  140 + if (result.getInteger("code") != 0) {
  141 + throw new ControllerException(result.getInteger("code"), result.getString("msg"));
  142 + }
  143 + return result.getString("data");
  144 + }
  145 +
  146 + @Override
  147 + public JSONArray queryTask(String app, String stream, String callId, String taskId, String mediaServerId,
  148 + Boolean isEnd, String scheme) {
  149 + MediaServerItem mediaServerItem = null;
  150 + if (mediaServerId == null) {
  151 + mediaServerItem = mediaServerService.getDefaultMediaServer();
  152 + }else {
  153 + mediaServerItem = mediaServerService.getOne(mediaServerId);
  154 + }
  155 + if (mediaServerItem == null) {
  156 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的流媒体");
  157 + }
  158 +
  159 + JSONObject result = assistRESTfulUtils.queryTaskList(mediaServerItem, app, stream, callId, taskId, isEnd, scheme);
  160 + if (result == null || result.getInteger("code") != 0) {
  161 + throw new ControllerException(ErrorCode.ERROR100.getCode(), result == null ? "查询任务列表失败" : result.getString("msg"));
  162 + }
  163 + return result.getJSONArray("data");
  164 + }
  165 +
  166 + @Override
  167 + public int changeCollect(boolean result, String app, String stream, String mediaServerId, String startTime, String endTime, String callId) {
  168 + // 开始时间和结束时间在数据库中都是以秒为单位的
  169 + Long startTimeStamp = null;
  170 + Long endTimeStamp = null;
  171 + if (startTime != null ) {
  172 + if (!DateUtil.verification(startTime, DateUtil.formatter)) {
  173 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "开始时间格式错误,正确格式为: " + DateUtil.formatter);
  174 + }
  175 + startTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
  176 +
  177 + }
  178 + if (endTime != null ) {
  179 + if (!DateUtil.verification(endTime, DateUtil.formatter)) {
  180 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "结束时间格式错误,正确格式为: " + DateUtil.formatter);
  181 + }
  182 + endTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
  183 +
  184 + }
  185 +
  186 + List<MediaServerItem> mediaServerItems;
  187 + if (!ObjectUtils.isEmpty(mediaServerId)) {
  188 + mediaServerItems = new ArrayList<>();
  189 + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
  190 + if (mediaServerItem == null) {
  191 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到流媒体: " + mediaServerId);
  192 + }
  193 + mediaServerItems.add(mediaServerItem);
  194 + } else {
  195 + mediaServerItems = null;
  196 + }
  197 +
  198 + List<CloudRecordItem> all = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp, endTimeStamp,
  199 + callId, mediaServerItems);
  200 + if (all.isEmpty()) {
  201 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到待收藏的视频");
  202 + }
  203 + int limitCount = 50;
  204 + int resultCount = 0;
  205 + if (all.size() > limitCount) {
  206 + for (int i = 0; i < all.size(); i += limitCount) {
  207 + int toIndex = i + limitCount;
  208 + if (i + limitCount > all.size()) {
  209 + toIndex = all.size();
  210 + }
  211 + resultCount += cloudRecordServiceMapper.updateCollectList(result, all.subList(i, toIndex));
  212 +
  213 + }
  214 + }else {
  215 + resultCount = cloudRecordServiceMapper.updateCollectList(result, all);
  216 + }
  217 + return resultCount;
  218 + }
  219 +
  220 + @Override
  221 + public int changeCollectById(Integer recordId, boolean result) {
  222 + return cloudRecordServiceMapper.changeCollectById(result, recordId);
  223 + }
  224 +
  225 + @Override
  226 + public DownloadFileInfo getPlayUrlPath(Integer recordId) {
  227 + CloudRecordItem recordItem = cloudRecordServiceMapper.queryOne(recordId);
  228 + if (recordItem == null) {
  229 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "资源不存在");
  230 + }
  231 + String filePath = recordItem.getFilePath();
  232 + MediaServerItem mediaServerItem = mediaServerService.getOne(recordItem.getMediaServerId());
  233 + return CloudRecordUtils.getDownloadFilePath(mediaServerItem, filePath);
  234 + }
  235 +}
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java
1 package com.genersoft.iot.vmp.service.impl; 1 package com.genersoft.iot.vmp.service.impl;
2 2
  3 +import com.baomidou.dynamic.datasource.annotation.DS;
3 import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; 4 import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
4 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; 5 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
5 import com.genersoft.iot.vmp.service.IDeviceAlarmService; 6 import com.genersoft.iot.vmp.service.IDeviceAlarmService;
@@ -12,6 +13,7 @@ import org.springframework.stereotype.Service; @@ -12,6 +13,7 @@ import org.springframework.stereotype.Service;
12 import java.util.List; 13 import java.util.List;
13 14
14 @Service 15 @Service
  16 +@DS("master")
15 public class DeviceAlarmServiceImpl implements IDeviceAlarmService { 17 public class DeviceAlarmServiceImpl implements IDeviceAlarmService {
16 18
17 @Autowired 19 @Autowired
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java
1 package com.genersoft.iot.vmp.service.impl; 1 package com.genersoft.iot.vmp.service.impl;
2 2
  3 +import com.baomidou.dynamic.datasource.annotation.DS;
3 import com.genersoft.iot.vmp.common.InviteInfo; 4 import com.genersoft.iot.vmp.common.InviteInfo;
4 import com.genersoft.iot.vmp.common.InviteSessionType; 5 import com.genersoft.iot.vmp.common.InviteSessionType;
5 import com.genersoft.iot.vmp.gb28181.bean.Device; 6 import com.genersoft.iot.vmp.gb28181.bean.Device;
@@ -27,6 +28,7 @@ import java.util.concurrent.CopyOnWriteArrayList; @@ -27,6 +28,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
27 * @author lin 28 * @author lin
28 */ 29 */
29 @Service 30 @Service
  31 +@DS("master")
30 public class DeviceChannelServiceImpl implements IDeviceChannelService { 32 public class DeviceChannelServiceImpl implements IDeviceChannelService {
31 33
32 private final static Logger logger = LoggerFactory.getLogger(DeviceChannelServiceImpl.class); 34 private final static Logger logger = LoggerFactory.getLogger(DeviceChannelServiceImpl.class);
@@ -243,6 +245,10 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService { @@ -243,6 +245,10 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
243 245
244 @Override 246 @Override
245 public void batchUpdateChannel(List<DeviceChannel> channels) { 247 public void batchUpdateChannel(List<DeviceChannel> channels) {
  248 + String now = DateUtil.getNow();
  249 + for (DeviceChannel channel : channels) {
  250 + channel.setUpdateTime(now);
  251 + }
246 channelMapper.batchUpdate(channels); 252 channelMapper.batchUpdate(channels);
247 for (DeviceChannel channel : channels) { 253 for (DeviceChannel channel : channels) {
248 if (channel.getParentId() != null) { 254 if (channel.getParentId() != null) {
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
1 package com.genersoft.iot.vmp.service.impl; 1 package com.genersoft.iot.vmp.service.impl;
2 2
  3 +import com.baomidou.dynamic.datasource.annotation.DS;
3 import com.genersoft.iot.vmp.common.VideoManagerConstants; 4 import com.genersoft.iot.vmp.common.VideoManagerConstants;
4 import com.genersoft.iot.vmp.conf.DynamicTask; 5 import com.genersoft.iot.vmp.conf.DynamicTask;
5 import com.genersoft.iot.vmp.conf.UserSetting; 6 import com.genersoft.iot.vmp.conf.UserSetting;
@@ -46,6 +47,7 @@ import java.util.concurrent.TimeUnit; @@ -46,6 +47,7 @@ import java.util.concurrent.TimeUnit;
46 * 设备业务(目录订阅) 47 * 设备业务(目录订阅)
47 */ 48 */
48 @Service 49 @Service
  50 +@DS("master")
49 public class DeviceServiceImpl implements IDeviceService { 51 public class DeviceServiceImpl implements IDeviceService {
50 52
51 private final static Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class); 53 private final static Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class);
@@ -162,6 +164,19 @@ public class DeviceServiceImpl implements IDeviceService { @@ -162,6 +164,19 @@ public class DeviceServiceImpl implements IDeviceService {
162 sync(device); 164 sync(device);
163 // TODO 如果设备下的通道级联到了其他平台,那么需要发送事件或者notify给上级平台 165 // TODO 如果设备下的通道级联到了其他平台,那么需要发送事件或者notify给上级平台
164 } 166 }
  167 + // 上线添加订阅
  168 + if (device.getSubscribeCycleForCatalog() > 0) {
  169 + // 查询在线设备那些开启了订阅,为设备开启定时的目录订阅
  170 + addCatalogSubscribe(device);
  171 + }
  172 + if (device.getSubscribeCycleForMobilePosition() > 0) {
  173 + addMobilePositionSubscribe(device);
  174 + }
  175 + if (userSetting.getDeviceStatusNotify()) {
  176 + // 发送redis消息
  177 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, true);
  178 + }
  179 +
165 }else { 180 }else {
166 if (deviceChannelMapper.queryAllChannels(device.getDeviceId()).size() == 0) { 181 if (deviceChannelMapper.queryAllChannels(device.getDeviceId()).size() == 0) {
167 logger.info("[设备上线]: {},通道数为0,查询通道信息", device.getDeviceId()); 182 logger.info("[设备上线]: {},通道数为0,查询通道信息", device.getDeviceId());
@@ -174,22 +189,10 @@ public class DeviceServiceImpl implements IDeviceService { @@ -174,22 +189,10 @@ public class DeviceServiceImpl implements IDeviceService {
174 189
175 } 190 }
176 191
177 - // 上线添加订阅  
178 - if (device.getSubscribeCycleForCatalog() > 0) {  
179 - // 查询在线设备那些开启了订阅,为设备开启定时的目录订阅  
180 - addCatalogSubscribe(device);  
181 - }  
182 - if (device.getSubscribeCycleForMobilePosition() > 0) {  
183 - addMobilePositionSubscribe(device);  
184 - }  
185 // 刷新过期任务 192 // 刷新过期任务
186 String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId(); 193 String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
187 // 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线 194 // 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线
188 dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "首次注册后未能收到心跳"), device.getKeepaliveIntervalTime() * 1000 * 3); 195 dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "首次注册后未能收到心跳"), device.getKeepaliveIntervalTime() * 1000 * 3);
189 - if (userSetting.getDeviceStatusNotify()) {  
190 - // 发送redis消息  
191 - redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, true);  
192 - }  
193 196
194 // 197 //
195 // try { 198 // try {
@@ -213,6 +216,13 @@ public class DeviceServiceImpl implements IDeviceService { @@ -213,6 +216,13 @@ public class DeviceServiceImpl implements IDeviceService {
213 } 216 }
214 String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + deviceId; 217 String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + deviceId;
215 dynamicTask.stop(registerExpireTaskKey); 218 dynamicTask.stop(registerExpireTaskKey);
  219 + if (device.isOnLine()) {
  220 + if (userSetting.getDeviceStatusNotify()) {
  221 + // 发送redis消息
  222 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, false);
  223 + }
  224 + }
  225 +
216 device.setOnLine(false); 226 device.setOnLine(false);
217 redisCatchStorage.updateDevice(device); 227 redisCatchStorage.updateDevice(device);
218 deviceMapper.update(device); 228 deviceMapper.update(device);
@@ -224,7 +234,7 @@ public class DeviceServiceImpl implements IDeviceService { @@ -224,7 +234,7 @@ public class DeviceServiceImpl implements IDeviceService {
224 for (SsrcTransaction ssrcTransaction : ssrcTransactions) { 234 for (SsrcTransaction ssrcTransaction : ssrcTransactions) {
225 mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); 235 mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
226 mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); 236 mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
227 - streamSession.remove(deviceId, ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); 237 + streamSession.removeByCallId(deviceId, ssrcTransaction.getChannelId(), ssrcTransaction.getCallId());
228 } 238 }
229 } 239 }
230 // 移除订阅 240 // 移除订阅
@@ -299,7 +309,7 @@ public class DeviceServiceImpl implements IDeviceService { @@ -299,7 +309,7 @@ public class DeviceServiceImpl implements IDeviceService {
299 // 设置最小值为30 309 // 设置最小值为30
300 int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForMobilePosition(),30); 310 int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForMobilePosition(),30);
301 // 刷新订阅 311 // 刷新订阅
302 - dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, (subscribeCycleForCatalog) * 1000); 312 + dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, subscribeCycleForCatalog * 1000);
303 return true; 313 return true;
304 } 314 }
305 315
src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
1 package com.genersoft.iot.vmp.service.impl; 1 package com.genersoft.iot.vmp.service.impl;
2 2
  3 +import com.baomidou.dynamic.datasource.annotation.DS;
3 import com.genersoft.iot.vmp.gb28181.bean.*; 4 import com.genersoft.iot.vmp.gb28181.bean.*;
4 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 5 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
5 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; 6 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
@@ -25,6 +26,7 @@ import java.util.ArrayList; @@ -25,6 +26,7 @@ import java.util.ArrayList;
25 import java.util.List; 26 import java.util.List;
26 27
27 @Service 28 @Service
  29 +@DS("master")
28 public class GbStreamServiceImpl implements IGbStreamService { 30 public class GbStreamServiceImpl implements IGbStreamService {
29 31
30 private final static Logger logger = LoggerFactory.getLogger(GbStreamServiceImpl.class); 32 private final static Logger logger = LoggerFactory.getLogger(GbStreamServiceImpl.class);
@@ -77,8 +79,6 @@ public class GbStreamServiceImpl implements IGbStreamService { @@ -77,8 +79,6 @@ public class GbStreamServiceImpl implements IGbStreamService {
77 } 79 }
78 try { 80 try {
79 List<DeviceChannel> deviceChannelList = new ArrayList<>(); 81 List<DeviceChannel> deviceChannelList = new ArrayList<>();
80 -  
81 -  
82 for (int i = 0; i < gbStreams.size(); i++) { 82 for (int i = 0; i < gbStreams.size(); i++) {
83 GbStream gbStream = gbStreams.get(i); 83 GbStream gbStream = gbStreams.get(i);
84 gbStream.setCatalogId(catalogId); 84 gbStream.setCatalogId(catalogId);
@@ -251,18 +251,17 @@ public class GbStreamServiceImpl implements IGbStreamService { @@ -251,18 +251,17 @@ public class GbStreamServiceImpl implements IGbStreamService {
251 return ; 251 return ;
252 } 252 }
253 if (ObjectUtils.isEmpty(catalogId)) { 253 if (ObjectUtils.isEmpty(catalogId)) {
254 - catalogId = platform.getDeviceGBId(); 254 + catalogId = null;
255 } 255 }
256 - if (platformGbStreamMapper.delByPlatformAndCatalogId(platformId, catalogId) > 0) {  
257 - List<GbStream> gbStreams = platformGbStreamMapper.queryChannelInParentPlatformAndCatalog(platformId, catalogId);  
258 - List<DeviceChannel> deviceChannelList = new ArrayList<>();  
259 - for (GbStream gbStream : gbStreams) {  
260 - DeviceChannel deviceChannel = new DeviceChannel();  
261 - deviceChannel.setChannelId(gbStream.getGbId());  
262 - deviceChannelList.add(deviceChannel);  
263 - }  
264 - eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); 256 + List<GbStream> gbStreams = platformGbStreamMapper.queryChannelInParentPlatformAndCatalog(platformId, catalogId);
  257 + List<DeviceChannel> deviceChannelList = new ArrayList<>();
  258 + for (GbStream gbStream : gbStreams) {
  259 + DeviceChannel deviceChannel = new DeviceChannel();
  260 + deviceChannel.setChannelId(gbStream.getGbId());
  261 + deviceChannelList.add(deviceChannel);
265 } 262 }
  263 + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL);
  264 + platformGbStreamMapper.delByPlatformAndCatalogId(platformId, catalogId);
266 } 265 }
267 266
268 @Override 267 @Override
src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java
1 package com.genersoft.iot.vmp.service.impl; 1 package com.genersoft.iot.vmp.service.impl;
2 2
3 import com.alibaba.fastjson2.JSON; 3 import com.alibaba.fastjson2.JSON;
  4 +import com.baomidou.dynamic.datasource.annotation.DS;
4 import com.genersoft.iot.vmp.common.InviteInfo; 5 import com.genersoft.iot.vmp.common.InviteInfo;
5 import com.genersoft.iot.vmp.common.InviteSessionStatus; 6 import com.genersoft.iot.vmp.common.InviteSessionStatus;
6 import com.genersoft.iot.vmp.common.InviteSessionType; 7 import com.genersoft.iot.vmp.common.InviteSessionType;
@@ -20,6 +21,7 @@ import java.util.concurrent.ConcurrentHashMap; @@ -20,6 +21,7 @@ import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.CopyOnWriteArrayList; 21 import java.util.concurrent.CopyOnWriteArrayList;
21 22
22 @Service 23 @Service
  24 +@DS("master")
23 public class InviteStreamServiceImpl implements IInviteStreamService { 25 public class InviteStreamServiceImpl implements IInviteStreamService {
24 26
25 private final Logger logger = LoggerFactory.getLogger(InviteStreamServiceImpl.class); 27 private final Logger logger = LoggerFactory.getLogger(InviteStreamServiceImpl.class);
@@ -116,9 +118,12 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @@ -116,9 +118,12 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
116 ":" + (stream != null ? stream : "*") 118 ":" + (stream != null ? stream : "*")
117 + ":*"; 119 + ":*";
118 List<Object> scanResult = RedisUtil.scan(redisTemplate, key); 120 List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
119 - if (scanResult.size() != 1) { 121 + if (scanResult.isEmpty()) {
120 return null; 122 return null;
121 } 123 }
  124 + if (scanResult.size() != 1) {
  125 + logger.warn("[获取InviteInfo] 发现 key: {}存在多条", key);
  126 + }
122 127
123 return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0)); 128 return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0));
124 } 129 }
src/main/java/com/genersoft/iot/vmp/service/impl/LogServiceImpl.java
1 package com.genersoft.iot.vmp.service.impl; 1 package com.genersoft.iot.vmp.service.impl;
2 2
  3 +import com.baomidou.dynamic.datasource.annotation.DS;
3 import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; 4 import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
4 import com.genersoft.iot.vmp.service.ILogService; 5 import com.genersoft.iot.vmp.service.ILogService;
5 import com.genersoft.iot.vmp.storager.dao.LogMapper; 6 import com.genersoft.iot.vmp.storager.dao.LogMapper;
@@ -12,6 +13,7 @@ import org.springframework.stereotype.Service; @@ -12,6 +13,7 @@ import org.springframework.stereotype.Service;
12 import java.util.List; 13 import java.util.List;
13 14
14 @Service 15 @Service
  16 +@DS("master")
15 public class LogServiceImpl implements ILogService { 17 public class LogServiceImpl implements ILogService {
16 18
17 @Autowired 19 @Autowired
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.service.impl; @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.service.impl;
3 import com.alibaba.fastjson2.JSON; 3 import com.alibaba.fastjson2.JSON;
4 import com.alibaba.fastjson2.JSONArray; 4 import com.alibaba.fastjson2.JSONArray;
5 import com.alibaba.fastjson2.JSONObject; 5 import com.alibaba.fastjson2.JSONObject;
  6 +import com.baomidou.dynamic.datasource.annotation.DS;
6 import com.genersoft.iot.vmp.common.CommonCallback; 7 import com.genersoft.iot.vmp.common.CommonCallback;
7 import com.genersoft.iot.vmp.common.VideoManagerConstants; 8 import com.genersoft.iot.vmp.common.VideoManagerConstants;
8 import com.genersoft.iot.vmp.conf.DynamicTask; 9 import com.genersoft.iot.vmp.conf.DynamicTask;
@@ -53,6 +54,7 @@ import java.util.concurrent.ExecutionException; @@ -53,6 +54,7 @@ import java.util.concurrent.ExecutionException;
53 * 媒体服务器节点管理 54 * 媒体服务器节点管理
54 */ 55 */
55 @Service 56 @Service
  57 +@DS("master")
56 public class MediaServerServiceImpl implements IMediaServerService { 58 public class MediaServerServiceImpl implements IMediaServerService {
57 59
58 private final static Logger logger = LoggerFactory.getLogger(MediaServerServiceImpl.class); 60 private final static Logger logger = LoggerFactory.getLogger(MediaServerServiceImpl.class);
@@ -165,14 +167,13 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -165,14 +167,13 @@ public class MediaServerServiceImpl implements IMediaServerService {
165 if (streamId == null) { 167 if (streamId == null) {
166 streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase(); 168 streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
167 } 169 }
168 - int ssrcCheckParam = 0;  
169 - if (ssrcCheck && tcpMode > 1) { 170 + if (ssrcCheck && tcpMode > 0) {
170 // 目前zlm不支持 tcp模式更新ssrc,暂时关闭ssrc校验 171 // 目前zlm不支持 tcp模式更新ssrc,暂时关闭ssrc校验
171 - logger.warn("[openRTPServer] TCP被动/TCP主动收流时,默认关闭ssrc检验"); 172 + logger.warn("[openRTPServer] 平台对接时下级可能自定义ssrc,但是tcp模式zlm收流目前无法更新ssrc,可能收流超时,此时请使用udp收流或者关闭ssrc校验");
172 } 173 }
173 int rtpServerPort; 174 int rtpServerPort;
174 if (mediaServerItem.isRtpEnable()) { 175 if (mediaServerItem.isRtpEnable()) {
175 - rtpServerPort = zlmServerFactory.createRTPServer(mediaServerItem, streamId, (ssrcCheck && tcpMode == 0) ? Long.parseLong(ssrc) : 0, port, onlyAuto, reUsePort, tcpMode); 176 + rtpServerPort = zlmServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck ? Long.parseLong(ssrc) : 0, port, onlyAuto, reUsePort, tcpMode);
176 } else { 177 } else {
177 rtpServerPort = mediaServerItem.getRtpProxyPort(); 178 rtpServerPort = mediaServerItem.getRtpProxyPort();
178 } 179 }
@@ -205,7 +206,10 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -205,7 +206,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
205 @Override 206 @Override
206 public void closeRTPServer(String mediaServerId, String streamId) { 207 public void closeRTPServer(String mediaServerId, String streamId) {
207 MediaServerItem mediaServerItem = this.getOne(mediaServerId); 208 MediaServerItem mediaServerItem = this.getOne(mediaServerId);
208 - closeRTPServer(mediaServerItem, streamId); 209 + if (mediaServerItem.isRtpEnable()) {
  210 + closeRTPServer(mediaServerItem, streamId);
  211 + }
  212 + zlmresTfulUtils.closeStreams(mediaServerItem, "rtp", streamId);
209 } 213 }
210 214
211 @Override 215 @Override
@@ -313,7 +317,6 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -313,7 +317,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
313 317
314 @Override 318 @Override
315 public MediaServerItem getDefaultMediaServer() { 319 public MediaServerItem getDefaultMediaServer() {
316 -  
317 return mediaServerMapper.queryDefault(); 320 return mediaServerMapper.queryDefault();
318 } 321 }
319 322
@@ -428,17 +431,6 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -428,17 +431,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
428 431
429 432
430 if (serverItem.isAutoConfig()) { 433 if (serverItem.isAutoConfig()) {
431 - // 查看assist服务的录像路径配置  
432 - if (serverItem.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {  
433 - JSONObject info = assistRESTfulUtils.getInfo(serverItem, null);  
434 - if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {  
435 - JSONObject dataJson = info.getJSONObject("data");  
436 - if (dataJson != null) {  
437 - String recordPath = dataJson.getString("record");  
438 - userSetting.setRecordPath(recordPath);  
439 - }  
440 - }  
441 - }  
442 setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable())); 434 setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable()));
443 } 435 }
444 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId(); 436 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId();
@@ -573,34 +565,30 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -573,34 +565,30 @@ public class MediaServerServiceImpl implements IMediaServerService {
573 logger.info("[ZLM] 正在设置 :{} -> {}:{}", 565 logger.info("[ZLM] 正在设置 :{} -> {}:{}",
574 mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); 566 mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
575 String protocol = sslEnabled ? "https" : "http"; 567 String protocol = sslEnabled ? "https" : "http";
576 - String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort); 568 + String hookPrefix = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort);
577 569
578 Map<String, Object> param = new HashMap<>(); 570 Map<String, Object> param = new HashMap<>();
579 param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline 571 param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
580 if (mediaServerItem.getRtspPort() != 0) { 572 if (mediaServerItem.getRtspPort() != 0) {
581 - param.put("ffmpeg.snap", "%s -rtsp_transport tcp -i %s -y -f mjpeg -t 0.001 %s"); 573 + param.put("ffmpeg.snap", "%s -rtsp_transport tcp -i %s -y -f mjpeg -frames:v 1 %s");
582 } 574 }
583 param.put("hook.enable","1"); 575 param.put("hook.enable","1");
584 param.put("hook.on_flow_report",""); 576 param.put("hook.on_flow_report","");
585 - param.put("hook.on_play",String.format("%s/on_play", hookPrex)); 577 + param.put("hook.on_play",String.format("%s/on_play", hookPrefix));
586 param.put("hook.on_http_access",""); 578 param.put("hook.on_http_access","");
587 - param.put("hook.on_publish", String.format("%s/on_publish", hookPrex)); 579 + param.put("hook.on_publish", String.format("%s/on_publish", hookPrefix));
588 param.put("hook.on_record_ts",""); 580 param.put("hook.on_record_ts","");
589 param.put("hook.on_rtsp_auth",""); 581 param.put("hook.on_rtsp_auth","");
590 param.put("hook.on_rtsp_realm",""); 582 param.put("hook.on_rtsp_realm","");
591 - param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex)); 583 + param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrefix));
592 param.put("hook.on_shell_login",""); 584 param.put("hook.on_shell_login","");
593 - param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex));  
594 - param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex));  
595 - param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex));  
596 - param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrex));  
597 - param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrex));  
598 - param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrex));  
599 - if (mediaServerItem.getRecordAssistPort() > 0) {  
600 - param.put("hook.on_record_mp4",String.format("http://127.0.0.1:%s/api/record/on_record_mp4", mediaServerItem.getRecordAssistPort()));  
601 - }else {  
602 - param.put("hook.on_record_mp4","");  
603 - } 585 + param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrefix));
  586 + param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrefix));
  587 + param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrefix));
  588 + param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrefix));
  589 + param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrefix));
  590 + param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrefix));
  591 + param.put("hook.on_record_mp4",String.format("%s/on_record_mp4", hookPrefix));
604 param.put("hook.timeoutSec","20"); 592 param.put("hook.timeoutSec","20");
605 // 推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。 593 // 推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。
606 // 置0关闭此特性(推流断开会导致立即断开播放器) 594 // 置0关闭此特性(推流断开会导致立即断开播放器)
@@ -609,15 +597,14 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -609,15 +597,14 @@ public class MediaServerServiceImpl implements IMediaServerService {
609 param.put("protocol.continue_push_ms", "3000" ); 597 param.put("protocol.continue_push_ms", "3000" );
610 // 最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流, 598 // 最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流,
611 // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项 599 // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项
612 -// param.put("general.wait_track_ready_ms", "3000" );  
613 if (mediaServerItem.isRtpEnable() && !ObjectUtils.isEmpty(mediaServerItem.getRtpPortRange())) { 600 if (mediaServerItem.isRtpEnable() && !ObjectUtils.isEmpty(mediaServerItem.getRtpPortRange())) {
614 param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-")); 601 param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-"));
615 } 602 }
616 603
617 - if (userSetting.getRecordPath() != null) {  
618 - File recordPathFile = new File(userSetting.getRecordPath());  
619 - File mp4SavePathFile = recordPathFile.getParentFile().getAbsoluteFile();  
620 - param.put("protocol.mp4_save_path", mp4SavePathFile.getAbsoluteFile()); 604 + if (!ObjectUtils.isEmpty(mediaServerItem.getRecordPath())) {
  605 + File recordPathFile = new File(mediaServerItem.getRecordPath());
  606 + param.put("protocol.mp4_save_path", recordPathFile.getParentFile().getPath());
  607 + param.put("protocol.downloadRoot", recordPathFile.getParentFile().getPath());
621 param.put("record.appName", recordPathFile.getName()); 608 param.put("record.appName", recordPathFile.getName());
622 } 609 }
623 610
@@ -722,6 +709,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -722,6 +709,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
722 ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null); 709 ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null);
723 String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId(); 710 String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId();
724 redisTemplate.opsForValue().set(key, mediaServerItem); 711 redisTemplate.opsForValue().set(key, mediaServerItem);
  712 + resetOnlineServerItem(mediaServerItem);
725 clearRTPServer(mediaServerItem); 713 clearRTPServer(mediaServerItem);
726 } 714 }
727 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerItem.getId(); 715 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerItem.getId();
@@ -750,15 +738,6 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -750,15 +738,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
750 } 738 }
751 739
752 @Override 740 @Override
753 - public boolean checkRtpServer(MediaServerItem mediaServerItem, String app, String stream) {  
754 - JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, stream);  
755 - if(rtpInfo.getInteger("code") == 0){  
756 - return rtpInfo.getBoolean("exist");  
757 - }  
758 - return false;  
759 - }  
760 -  
761 - @Override  
762 public MediaServerLoad getLoad(MediaServerItem mediaServerItem) { 741 public MediaServerLoad getLoad(MediaServerItem mediaServerItem) {
763 MediaServerLoad result = new MediaServerLoad(); 742 MediaServerLoad result = new MediaServerLoad();
764 result.setId(mediaServerItem.getId()); 743 result.setId(mediaServerItem.getId());
@@ -771,88 +750,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -771,88 +750,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
771 } 750 }
772 751
773 @Override 752 @Override
774 - public List<RecordFile> getRecords(String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems) {  
775 - Assert.notNull(app, "app不存在");  
776 - Assert.notNull(stream, "stream不存在");  
777 - Assert.notNull(startTime, "startTime不存在");  
778 - Assert.notNull(endTime, "endTime不存在");  
779 - Assert.notEmpty(mediaServerItems, "流媒体列表为空");  
780 -  
781 - CompletableFuture[] completableFutures = new CompletableFuture[mediaServerItems.size()];  
782 - for (int i = 0; i < mediaServerItems.size(); i++) {  
783 - completableFutures[i] = getRecordFilesForOne(app, stream, startTime, endTime, mediaServerItems.get(i));  
784 - }  
785 - List<RecordFile> result = new ArrayList<>();  
786 - for (int i = 0; i < completableFutures.length; i++) {  
787 - try {  
788 - List<RecordFile> list = (List<RecordFile>) completableFutures[i].get();  
789 - if (!list.isEmpty()) {  
790 - for (int g = 0; g < list.size(); g++) {  
791 - list.get(g).setMediaServerId(mediaServerItems.get(i).getId());  
792 - }  
793 - result.addAll(list);  
794 - }  
795 - } catch (InterruptedException e) {  
796 - throw new RuntimeException(e);  
797 - } catch (ExecutionException e) {  
798 - throw new RuntimeException(e);  
799 - }  
800 - }  
801 - Comparator<RecordFile> comparator = Comparator.comparing(RecordFile::getFileName);  
802 - result.sort(comparator);  
803 - return result;  
804 - }  
805 -  
806 - @Override  
807 - public List<String> getRecordDates(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems) {  
808 - Assert.notNull(app, "app不存在");  
809 - Assert.notNull(stream, "stream不存在");  
810 - Assert.notEmpty(mediaServerItems, "流媒体列表为空");  
811 - CompletableFuture[] completableFutures = new CompletableFuture[mediaServerItems.size()];  
812 -  
813 - for (int i = 0; i < mediaServerItems.size(); i++) {  
814 - completableFutures[i] = getRecordDatesForOne(app, stream, year, month, mediaServerItems.get(i));  
815 - }  
816 - List<String> result = new ArrayList<>();  
817 - CompletableFuture.allOf(completableFutures).join();  
818 - for (CompletableFuture completableFuture : completableFutures) {  
819 - try {  
820 - List<String> list = (List<String>) completableFuture.get();  
821 - result.addAll(list);  
822 - } catch (InterruptedException e) {  
823 - throw new RuntimeException(e);  
824 - } catch (ExecutionException e) {  
825 - throw new RuntimeException(e);  
826 - }  
827 - }  
828 - Collections.sort(result);  
829 - return result;  
830 - }  
831 -  
832 - @Async  
833 - public CompletableFuture<List<String>> getRecordDatesForOne(String app, String stream, int year, int month, MediaServerItem mediaServerItem) {  
834 - JSONObject fileListJson = assistRESTfulUtils.getDateList(mediaServerItem, app, stream, year, month);  
835 - if (fileListJson != null && !fileListJson.isEmpty()) {  
836 - if (fileListJson.getString("code") != null && fileListJson.getInteger("code") == 0) {  
837 - JSONArray data = fileListJson.getJSONArray("data");  
838 - return CompletableFuture.completedFuture(data.toJavaList(String.class));  
839 - }  
840 - }  
841 - return CompletableFuture.completedFuture(new ArrayList<>());  
842 - }  
843 -  
844 - @Async  
845 - public CompletableFuture<List<RecordFile>> getRecordFilesForOne(String app, String stream, String startTime, String endTime, MediaServerItem mediaServerItem) {  
846 - JSONObject fileListJson = assistRESTfulUtils.getFileList(mediaServerItem, 1, 100000000, app, stream, startTime, endTime);  
847 - if (fileListJson != null && !fileListJson.isEmpty()) {  
848 - if (fileListJson.getString("code") != null && fileListJson.getInteger("code") == 0) {  
849 - JSONObject data = fileListJson.getJSONObject("data");  
850 - JSONArray list = data.getJSONArray("list");  
851 - if (list != null) {  
852 - return CompletableFuture.completedFuture(list.toJavaList(RecordFile.class));  
853 - }  
854 - }  
855 - }  
856 - return CompletableFuture.completedFuture(new ArrayList<>()); 753 + public List<MediaServerItem> getAllWithAssistPort() {
  754 + return mediaServerMapper.queryAllWithAssistPort();
857 } 755 }
858 } 756 }
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
@@ -64,7 +64,7 @@ public class MediaServiceImpl implements IMediaService { @@ -64,7 +64,7 @@ public class MediaServiceImpl implements IMediaService {
64 if (data == null) { 64 if (data == null) {
65 return null; 65 return null;
66 } 66 }
67 - JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class); 67 + JSONObject mediaJSON = data.getJSONObject(0);
68 JSONArray tracks = mediaJSON.getJSONArray("tracks"); 68 JSONArray tracks = mediaJSON.getJSONArray("tracks");
69 if (authority) { 69 if (authority) {
70 streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr, calld, true); 70 streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr, calld, true);
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java
1 package com.genersoft.iot.vmp.service.impl; 1 package com.genersoft.iot.vmp.service.impl;
2 2
  3 +import com.baomidou.dynamic.datasource.annotation.DS;
3 import com.genersoft.iot.vmp.gb28181.bean.*; 4 import com.genersoft.iot.vmp.gb28181.bean.*;
4 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 5 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
5 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; 6 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
@@ -27,6 +28,7 @@ import java.util.Map; @@ -27,6 +28,7 @@ import java.util.Map;
27 * @author lin 28 * @author lin
28 */ 29 */
29 @Service 30 @Service
  31 +@DS("master")
30 public class PlatformChannelServiceImpl implements IPlatformChannelService { 32 public class PlatformChannelServiceImpl implements IPlatformChannelService {
31 33
32 private final static Logger logger = LoggerFactory.getLogger(PlatformChannelServiceImpl.class); 34 private final static Logger logger = LoggerFactory.getLogger(PlatformChannelServiceImpl.class);
@@ -165,10 +167,9 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService { @@ -165,10 +167,9 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
165 catalogId = null; 167 catalogId = null;
166 } 168 }
167 169
168 - if ((result = platformChannelMapper.delChannelForGBByCatalogId(platformId, catalogId)) > 0) {  
169 - List<DeviceChannel> deviceChannels = platformChannelMapper.queryAllChannelInCatalog(platformId, catalogId);  
170 - eventPublisher.catalogEventPublish(platformId, deviceChannels, CatalogEvent.DEL);  
171 - }  
172 - return result; 170 + List<DeviceChannel> deviceChannels = platformChannelMapper.queryAllChannelInCatalog(platformId, catalogId);
  171 + eventPublisher.catalogEventPublish(platformId, deviceChannels, CatalogEvent.DEL);
  172 +
  173 + return platformChannelMapper.delChannelForGBByCatalogId(platformId, catalogId);
173 } 174 }
174 } 175 }
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.service.impl; @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.service.impl;
2 2
3 import com.genersoft.iot.vmp.common.InviteInfo; 3 import com.genersoft.iot.vmp.common.InviteInfo;
4 import com.genersoft.iot.vmp.common.InviteSessionType; 4 import com.genersoft.iot.vmp.common.InviteSessionType;
  5 +import com.baomidou.dynamic.datasource.annotation.DS;
5 import com.genersoft.iot.vmp.conf.DynamicTask; 6 import com.genersoft.iot.vmp.conf.DynamicTask;
6 import com.genersoft.iot.vmp.conf.UserSetting; 7 import com.genersoft.iot.vmp.conf.UserSetting;
7 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; 8 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
@@ -55,6 +56,7 @@ import java.util.*; @@ -55,6 +56,7 @@ import java.util.*;
55 * @author lin 56 * @author lin
56 */ 57 */
57 @Service 58 @Service
  59 +@DS("master")
58 public class PlatformServiceImpl implements IPlatformService { 60 public class PlatformServiceImpl implements IPlatformService {
59 61
60 private final static String REGISTER_KEY_PREFIX = "platform_register_"; 62 private final static String REGISTER_KEY_PREFIX = "platform_register_";
@@ -169,7 +171,7 @@ public class PlatformServiceImpl implements IPlatformService { @@ -169,7 +171,7 @@ public class PlatformServiceImpl implements IPlatformService {
169 dynamicTask.stop(registerTaskKey); 171 dynamicTask.stop(registerTaskKey);
170 // 注销旧的 172 // 注销旧的
171 try { 173 try {
172 - if (parentPlatformOld.isStatus()) { 174 + if (parentPlatformOld.isStatus() && parentPlatformCatchOld != null) {
173 logger.info("保存平台{}时发现旧平台在线,发送注销命令", parentPlatformOld.getServerGBId()); 175 logger.info("保存平台{}时发现旧平台在线,发送注销命令", parentPlatformOld.getServerGBId());
174 commanderForPlatform.unregister(parentPlatformOld, parentPlatformCatchOld.getSipTransactionInfo(), null, eventResult -> { 176 commanderForPlatform.unregister(parentPlatformOld, parentPlatformCatchOld.getSipTransactionInfo(), null, eventResult -> {
175 logger.info("[国标级联] 注销成功, 平台:{}", parentPlatformOld.getServerGBId()); 177 logger.info("[国标级联] 注销成功, 平台:{}", parentPlatformOld.getServerGBId());
@@ -286,6 +288,7 @@ public class PlatformServiceImpl implements IPlatformService { @@ -286,6 +288,7 @@ public class PlatformServiceImpl implements IPlatformService {
286 } 288 }
287 if (parentPlatform.isAutoPushChannel()) { 289 if (parentPlatform.isAutoPushChannel()) {
288 if (subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()) == null) { 290 if (subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()) == null) {
  291 + logger.info("[国标级联]:{}, 添加自动通道推送模拟订阅信息", parentPlatform.getServerGBId());
289 addSimulatedSubscribeInfo(parentPlatform); 292 addSimulatedSubscribeInfo(parentPlatform);
290 } 293 }
291 }else { 294 }else {
@@ -363,9 +366,16 @@ public class PlatformServiceImpl implements IPlatformService { @@ -363,9 +366,16 @@ public class PlatformServiceImpl implements IPlatformService {
363 // 清除心跳任务 366 // 清除心跳任务
364 dynamicTask.stop(keepaliveTaskKey); 367 dynamicTask.stop(keepaliveTaskKey);
365 } 368 }
366 - // 停止目录订阅回复  
367 - logger.info("[平台离线] {}, 停止订阅回复", parentPlatform.getServerGBId());  
368 - subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId()); 369 + // 停止订阅回复
  370 + SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId());
  371 + if (catalogSubscribe != null) {
  372 + if (catalogSubscribe.getExpires() > 0) {
  373 + logger.info("[平台离线] {}, 停止目录订阅回复", parentPlatform.getServerGBId());
  374 + subscribeHolder.removeCatalogSubscribe(parentPlatform.getServerGBId());
  375 + }
  376 + }
  377 + logger.info("[平台离线] {}, 停止移动位置订阅回复", parentPlatform.getServerGBId());
  378 + subscribeHolder.removeMobilePositionSubscribe(parentPlatform.getServerGBId());
369 // 发起定时自动重新注册 379 // 发起定时自动重新注册
370 if (!stopRegister) { 380 if (!stopRegister) {
371 // 设置为60秒自动尝试重新注册 381 // 设置为60秒自动尝试重新注册
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
1 package com.genersoft.iot.vmp.service.impl; 1 package com.genersoft.iot.vmp.service.impl;
2 2
  3 +import com.alibaba.fastjson2.JSONArray;
3 import com.alibaba.fastjson2.JSONObject; 4 import com.alibaba.fastjson2.JSONObject;
  5 +import com.baomidou.dynamic.datasource.annotation.DS;
4 import com.genersoft.iot.vmp.common.InviteInfo; 6 import com.genersoft.iot.vmp.common.InviteInfo;
5 import com.genersoft.iot.vmp.common.InviteSessionStatus; 7 import com.genersoft.iot.vmp.common.InviteSessionStatus;
6 import com.genersoft.iot.vmp.common.InviteSessionType; 8 import com.genersoft.iot.vmp.common.InviteSessionType;
@@ -23,7 +25,12 @@ import com.genersoft.iot.vmp.media.zlm.*; @@ -23,7 +25,12 @@ import com.genersoft.iot.vmp.media.zlm.*;
23 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; 25 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
24 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; 26 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
25 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 27 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  28 +import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
  29 +import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
  30 +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
  31 +import com.genersoft.iot.vmp.media.zlm.dto.*;
26 import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam; 32 import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
  33 +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
27 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; 34 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
28 import com.genersoft.iot.vmp.service.*; 35 import com.genersoft.iot.vmp.service.*;
29 import com.genersoft.iot.vmp.service.bean.ErrorCallback; 36 import com.genersoft.iot.vmp.service.bean.ErrorCallback;
@@ -31,8 +38,11 @@ import com.genersoft.iot.vmp.service.bean.InviteErrorCode; @@ -31,8 +38,11 @@ import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
31 import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; 38 import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
32 import com.genersoft.iot.vmp.service.bean.SSRCInfo; 39 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
33 import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; 40 import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
  41 +import com.genersoft.iot.vmp.service.bean.*;
34 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 42 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
35 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 43 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  44 +import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
  45 +import com.genersoft.iot.vmp.utils.CloudRecordUtils;
36 import com.genersoft.iot.vmp.utils.DateUtil; 46 import com.genersoft.iot.vmp.utils.DateUtil;
37 import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; 47 import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
38 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; 48 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
@@ -61,6 +71,7 @@ import java.util.*; @@ -61,6 +71,7 @@ import java.util.*;
61 71
62 @SuppressWarnings(value = {"rawtypes", "unchecked"}) 72 @SuppressWarnings(value = {"rawtypes", "unchecked"})
63 @Service 73 @Service
  74 +@DS("master")
64 public class PlayServiceImpl implements IPlayService { 75 public class PlayServiceImpl implements IPlayService {
65 76
66 private final static Logger logger = LoggerFactory.getLogger(PlayServiceImpl.class); 77 private final static Logger logger = LoggerFactory.getLogger(PlayServiceImpl.class);
@@ -93,12 +104,18 @@ public class PlayServiceImpl implements IPlayService { @@ -93,12 +104,18 @@ public class PlayServiceImpl implements IPlayService {
93 private SendRtpPortManager sendRtpPortManager; 104 private SendRtpPortManager sendRtpPortManager;
94 105
95 @Autowired 106 @Autowired
  107 + private ZlmHttpHookSubscribe subscribe;
  108 +
  109 + @Autowired
96 private ZLMRESTfulUtils zlmresTfulUtils; 110 private ZLMRESTfulUtils zlmresTfulUtils;
97 111
98 @Autowired 112 @Autowired
99 private AssistRESTfulUtils assistRESTfulUtils; 113 private AssistRESTfulUtils assistRESTfulUtils;
100 114
101 @Autowired 115 @Autowired
  116 + private ZLMServerFactory zlmServerFactory;
  117 +
  118 + @Autowired
102 private IMediaService mediaService; 119 private IMediaService mediaService;
103 120
104 @Autowired 121 @Autowired
@@ -117,7 +134,7 @@ public class PlayServiceImpl implements IPlayService { @@ -117,7 +134,7 @@ public class PlayServiceImpl implements IPlayService {
117 private DynamicTask dynamicTask; 134 private DynamicTask dynamicTask;
118 135
119 @Autowired 136 @Autowired
120 - private ZlmHttpHookSubscribe subscribe; 137 + private CloudRecordServiceMapper cloudRecordServiceMapper;
121 138
122 @Autowired 139 @Autowired
123 private ISIPCommanderForPlatform commanderForPlatform; 140 private ISIPCommanderForPlatform commanderForPlatform;
@@ -407,6 +424,15 @@ public class PlayServiceImpl implements IPlayService { @@ -407,6 +424,15 @@ public class PlayServiceImpl implements IPlayService {
407 HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); 424 HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
408 subscribe.removeSubscribe(hookSubscribe); 425 subscribe.removeSubscribe(hookSubscribe);
409 } 426 }
  427 + }else {
  428 + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流类型:{},端口:{}, SSRC: {}",
  429 + device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流",
  430 + ssrcInfo.getPort(), ssrcInfo.getSsrc());
  431 +
  432 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  433 +
  434 + mediaServerService.closeRTPServer(mediaServerItem.getId(), ssrcInfo.getStream());
  435 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
410 } 436 }
411 }, userSetting.getPlayTimeout()); 437 }, userSetting.getPlayTimeout());
412 438
@@ -437,6 +463,7 @@ public class PlayServiceImpl implements IPlayService { @@ -437,6 +463,7 @@ public class PlayServiceImpl implements IPlayService {
437 InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId, 463 InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId,
438 timeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAY); 464 timeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAY);
439 }, (event) -> { 465 }, (event) -> {
  466 + logger.info("[点播失败] deviceId: {}, channelId:{}, {}: {}", device.getDeviceId(), channelId, event.statusCode, event.msg);
440 dynamicTask.stop(timeOutTaskKey); 467 dynamicTask.stop(timeOutTaskKey);
441 mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); 468 mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
442 // 释放ssrc 469 // 释放ssrc
@@ -478,7 +505,13 @@ public class PlayServiceImpl implements IPlayService { @@ -478,7 +505,13 @@ public class PlayServiceImpl implements IPlayService {
478 if (!device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { 505 if (!device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {
479 return; 506 return;
480 } 507 }
481 - String substring = contentString.substring(0, contentString.indexOf("y=")); 508 +
  509 + String substring;
  510 + if (contentString.indexOf("y=") > 0) {
  511 + substring = contentString.substring(0, contentString.indexOf("y="));
  512 + }else {
  513 + substring = contentString;
  514 + }
482 try { 515 try {
483 SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); 516 SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
484 int port = -1; 517 int port = -1;
@@ -568,7 +601,7 @@ public class PlayServiceImpl implements IPlayService { @@ -568,7 +601,7 @@ public class PlayServiceImpl implements IPlayService {
568 deviceChannel.setStreamId(streamInfo.getStream()); 601 deviceChannel.setStreamId(streamInfo.getStream());
569 storager.startPlay(deviceId, channelId, streamInfo.getStream()); 602 storager.startPlay(deviceId, channelId, streamInfo.getStream());
570 } 603 }
571 - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAYBACK, deviceId, channelId); 604 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, ((OnStreamChangedHookParam) param).getStream());
572 if (inviteInfo != null) { 605 if (inviteInfo != null) {
573 inviteInfo.setStatus(InviteSessionStatus.ok); 606 inviteInfo.setStatus(InviteSessionStatus.ok);
574 607
@@ -598,23 +631,6 @@ public class PlayServiceImpl implements IPlayService { @@ -598,23 +631,6 @@ public class PlayServiceImpl implements IPlayService {
598 } 631 }
599 632
600 @Override 633 @Override
601 - public MediaServerItem getNewMediaServerItemHasAssist(Device device) {  
602 - if (device == null) {  
603 - return null;  
604 - }  
605 - MediaServerItem mediaServerItem;  
606 - if (ObjectUtils.isEmpty(device.getMediaServerId()) || "auto".equals(device.getMediaServerId())) {  
607 - mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(true);  
608 - } else {  
609 - mediaServerItem = mediaServerService.getOne(device.getMediaServerId());  
610 - }  
611 - if (mediaServerItem == null) {  
612 - logger.warn("[获取可用的ZLM节点]未找到可使用的ZLM...");  
613 - }  
614 - return mediaServerItem;  
615 - }  
616 -  
617 - @Override  
618 public void playBack(String deviceId, String channelId, String startTime, 634 public void playBack(String deviceId, String channelId, String startTime,
619 String endTime, ErrorCallback<Object> callback) { 635 String endTime, ErrorCallback<Object> callback) {
620 Device device = storager.queryVideoDevice(deviceId); 636 Device device = storager.queryVideoDevice(deviceId);
@@ -711,7 +727,6 @@ public class PlayServiceImpl implements IPlayService { @@ -711,7 +727,6 @@ public class PlayServiceImpl implements IPlayService {
711 // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题 727 // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题
712 InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId, 728 InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId,
713 playBackTimeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAYBACK); 729 playBackTimeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAYBACK);
714 -  
715 }, errorEvent); 730 }, errorEvent);
716 } catch (InvalidArgumentException | SipException | ParseException e) { 731 } catch (InvalidArgumentException | SipException | ParseException e) {
717 logger.error("[命令发送失败] 录像回放: {}", e.getMessage()); 732 logger.error("[命令发送失败] 录像回放: {}", e.getMessage());
@@ -732,6 +747,10 @@ public class PlayServiceImpl implements IPlayService { @@ -732,6 +747,10 @@ public class PlayServiceImpl implements IPlayService {
732 ResponseEvent responseEvent = (ResponseEvent) eventResult.event; 747 ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
733 String contentString = new String(responseEvent.getResponse().getRawContent()); 748 String contentString = new String(responseEvent.getResponse().getRawContent());
734 String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); 749 String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString);
  750 + // 兼容回复的消息中缺少ssrc(y字段)的情况
  751 + if (ssrcInResponse == null) {
  752 + ssrcInResponse = ssrcInfo.getSsrc();
  753 + }
735 if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { 754 if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
736 // ssrc 一致 755 // ssrc 一致
737 if (mediaServerItem.isRtpEnable()) { 756 if (mediaServerItem.isRtpEnable()) {
@@ -809,13 +828,15 @@ public class PlayServiceImpl implements IPlayService { @@ -809,13 +828,15 @@ public class PlayServiceImpl implements IPlayService {
809 } 828 }
810 829
811 830
  831 +
  832 +
812 @Override 833 @Override
813 public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback) { 834 public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback) {
814 Device device = storager.queryVideoDevice(deviceId); 835 Device device = storager.queryVideoDevice(deviceId);
815 if (device == null) { 836 if (device == null) {
816 return; 837 return;
817 } 838 }
818 - MediaServerItem newMediaServerItem = getNewMediaServerItemHasAssist(device); 839 + MediaServerItem newMediaServerItem = this.getNewMediaServerItem(device);
819 if (newMediaServerItem == null) { 840 if (newMediaServerItem == null) {
820 callback.run(InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getCode(), 841 callback.run(InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getCode(),
821 InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getMsg(), 842 InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getMsg(),
@@ -894,6 +915,28 @@ public class PlayServiceImpl implements IPlayService { @@ -894,6 +915,28 @@ public class PlayServiceImpl implements IPlayService {
894 // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题 915 // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题
895 InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId, 916 InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId,
896 downLoadTimeOutTaskKey, callback, inviteInfo, InviteSessionType.DOWNLOAD); 917 downLoadTimeOutTaskKey, callback, inviteInfo, InviteSessionType.DOWNLOAD);
  918 +
  919 + // 注册录像回调事件,录像下载结束后写入下载地址
  920 + ZlmHttpHookSubscribe.Event hookEventForRecord = (mediaServerItemInuse, hookParam) -> {
  921 + logger.info("[录像下载] 收到录像写入磁盘消息: , {}/{}-{}",
  922 + inviteInfo.getDeviceId(), inviteInfo.getChannelId(), ssrcInfo.getStream());
  923 + logger.info("[录像下载] 收到录像写入磁盘消息内容: " + hookParam);
  924 + OnRecordMp4HookParam recordMp4HookParam = (OnRecordMp4HookParam)hookParam;
  925 + String filePath = recordMp4HookParam.getFile_path();
  926 + DownloadFileInfo downloadFileInfo = CloudRecordUtils.getDownloadFilePath(mediaServerItem, filePath);
  927 + InviteInfo inviteInfoForNew = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId()
  928 + , inviteInfo.getChannelId(), inviteInfo.getStream());
  929 + inviteInfoForNew.getStreamInfo().setDownLoadFilePath(downloadFileInfo);
  930 + inviteStreamService.updateInviteInfo(inviteInfoForNew);
  931 + };
  932 + HookSubscribeForRecordMp4 hookSubscribe = HookSubscribeFactory.on_record_mp4(
  933 + mediaServerItem.getId(), "rtp", ssrcInfo.getStream());
  934 +
  935 + // 设置过期时间,下载失败时自动处理订阅数据
  936 +// long difference = DateUtil.getDifference(startTime, endTime)/1000;
  937 +// Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(difference * 2));
  938 +// hookSubscribe.setExpires(expiresInstant);
  939 + subscribe.addSubscribe(hookSubscribe, hookEventForRecord);
897 }); 940 });
898 } catch (InvalidArgumentException | SipException | ParseException e) { 941 } catch (InvalidArgumentException | SipException | ParseException e) {
899 logger.error("[命令发送失败] 录像下载: {}", e.getMessage()); 942 logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
@@ -909,47 +952,71 @@ public class PlayServiceImpl implements IPlayService { @@ -909,47 +952,71 @@ public class PlayServiceImpl implements IPlayService {
909 @Override 952 @Override
910 public StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream) { 953 public StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream) {
911 InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, stream); 954 InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, stream);
  955 + if (inviteInfo == null || inviteInfo.getStreamInfo() == null) {
  956 + logger.warn("[获取下载进度] 未查询到录像下载的信息");
  957 + return null;
  958 + }
912 959
913 - if (inviteInfo != null && inviteInfo.getStreamInfo() != null) {  
914 - if (inviteInfo.getStreamInfo().getProgress() == 1) {  
915 - return inviteInfo.getStreamInfo();  
916 - } 960 + if (inviteInfo.getStreamInfo().getProgress() == 1) {
  961 + return inviteInfo.getStreamInfo();
  962 + }
917 963
918 - // 获取当前已下载时长  
919 - String mediaServerId = inviteInfo.getStreamInfo().getMediaServerId();  
920 - MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);  
921 - if (mediaServerItem == null) {  
922 - logger.warn("查询录像信息时发现节点已离线");  
923 - return null;  
924 - }  
925 - if (mediaServerItem.getRecordAssistPort() > 0) {  
926 - JSONObject jsonObject = assistRESTfulUtils.fileDuration(mediaServerItem, inviteInfo.getStreamInfo().getApp(), inviteInfo.getStreamInfo().getStream(), null);  
927 - if (jsonObject == null) {  
928 - throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接Assist服务失败");  
929 - }  
930 - if (jsonObject.getInteger("code") == 0) {  
931 - long duration = jsonObject.getLong("data"); 964 + // 获取当前已下载时长
  965 + String mediaServerId = inviteInfo.getStreamInfo().getMediaServerId();
  966 + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
  967 + if (mediaServerItem == null) {
  968 + logger.warn("[获取下载进度] 查询录像信息时发现节点不存在");
  969 + return null;
  970 + }
  971 + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId, null, stream);
932 972
933 - if (duration == 0) {  
934 - inviteInfo.getStreamInfo().setProgress(0);  
935 - } else {  
936 - String startTime = inviteInfo.getStreamInfo().getStartTime();  
937 - String endTime = inviteInfo.getStreamInfo().getEndTime();  
938 - long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);  
939 - long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);  
940 -  
941 - BigDecimal currentCount = new BigDecimal(duration / 1000);  
942 - BigDecimal totalCount = new BigDecimal(end - start);  
943 - BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP);  
944 - double process = divide.doubleValue();  
945 - inviteInfo.getStreamInfo().setProgress(process);  
946 - }  
947 - inviteStreamService.updateInviteInfo(inviteInfo);  
948 - } 973 + if (ssrcTransaction == null) {
  974 + logger.warn("[获取下载进度] 下载已结束");
  975 + return null;
  976 + }
  977 +
  978 + JSONObject mediaListJson= zlmresTfulUtils.getMediaList(mediaServerItem, "rtp", stream);
  979 + if (mediaListJson == null) {
  980 + logger.warn("[获取下载进度] 从zlm查询进度失败");
  981 + return null;
  982 + }
  983 + if (mediaListJson.getInteger("code") != 0) {
  984 + logger.warn("[获取下载进度] 从zlm查询进度出现错误: {}", mediaListJson.getString("msg"));
  985 + return null;
  986 + }
  987 + JSONArray data = mediaListJson.getJSONArray("data");
  988 + if (data == null) {
  989 + logger.warn("[获取下载进度] 从zlm查询进度时未返回数据");
  990 + return null;
  991 + }
  992 + JSONObject mediaJSON = data.getJSONObject(0);
  993 + JSONArray tracks = mediaJSON.getJSONArray("tracks");
  994 + if (tracks.isEmpty()) {
  995 + logger.warn("[获取下载进度] 从zlm查询进度时未返回数据");
  996 + return null;
  997 + }
  998 + JSONObject jsonObject = tracks.getJSONObject(0);
  999 + long duration = jsonObject.getLongValue("duration");
  1000 + if (duration == 0) {
  1001 + inviteInfo.getStreamInfo().setProgress(0);
  1002 + } else {
  1003 + String startTime = inviteInfo.getStreamInfo().getStartTime();
  1004 + String endTime = inviteInfo.getStreamInfo().getEndTime();
  1005 + // 此时start和end单位是秒
  1006 + long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
  1007 + long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
  1008 +
  1009 + BigDecimal currentCount = new BigDecimal(duration);
  1010 + BigDecimal totalCount = new BigDecimal((end - start) * 1000);
  1011 + BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP);
  1012 + double process = divide.doubleValue();
  1013 + if (process > 0.999) {
  1014 + process = 1.0;
949 } 1015 }
950 - return inviteInfo.getStreamInfo(); 1016 + inviteInfo.getStreamInfo().setProgress(process);
951 } 1017 }
952 - return null; 1018 + inviteStreamService.updateInviteInfo(inviteInfo);
  1019 + return inviteInfo.getStreamInfo();
953 } 1020 }
954 1021
955 private StreamInfo onPublishHandlerForDownload(MediaServerItem mediaServerItemInuse, HookParam hookParam, String deviceId, String channelId, String startTime, String endTime) { 1022 private StreamInfo onPublishHandlerForDownload(MediaServerItem mediaServerItemInuse, HookParam hookParam, String deviceId, String channelId, String startTime, String endTime) {
@@ -1219,7 +1286,12 @@ public class PlayServiceImpl implements IPlayService { @@ -1219,7 +1286,12 @@ public class PlayServiceImpl implements IPlayService {
1219 throw new ServiceException("mediaServer不存在"); 1286 throw new ServiceException("mediaServer不存在");
1220 } 1287 }
1221 // zlm 暂停RTP超时检查 1288 // zlm 暂停RTP超时检查
1222 - JSONObject jsonObject = zlmresTfulUtils.pauseRtpCheck(mediaServerItem, streamId); 1289 + // 使用zlm中的流ID
  1290 + String streamKey = inviteInfo.getStream();
  1291 + if (!mediaServerItem.isRtpEnable()) {
  1292 + streamKey = Long.toHexString(Long.parseLong(inviteInfo.getSsrcInfo().getSsrc())).toUpperCase();
  1293 + }
  1294 + JSONObject jsonObject = zlmresTfulUtils.pauseRtpCheck(mediaServerItem, streamKey);
1223 if (jsonObject == null || jsonObject.getInteger("code") != 0) { 1295 if (jsonObject == null || jsonObject.getInteger("code") != 0) {
1224 throw new ServiceException("暂停RTP接收失败"); 1296 throw new ServiceException("暂停RTP接收失败");
1225 } 1297 }
@@ -1242,7 +1314,12 @@ public class PlayServiceImpl implements IPlayService { @@ -1242,7 +1314,12 @@ public class PlayServiceImpl implements IPlayService {
1242 throw new ServiceException("mediaServer不存在"); 1314 throw new ServiceException("mediaServer不存在");
1243 } 1315 }
1244 // zlm 暂停RTP超时检查 1316 // zlm 暂停RTP超时检查
1245 - JSONObject jsonObject = zlmresTfulUtils.resumeRtpCheck(mediaServerItem, streamId); 1317 + // 使用zlm中的流ID
  1318 + String streamKey = inviteInfo.getStream();
  1319 + if (!mediaServerItem.isRtpEnable()) {
  1320 + streamKey = Long.toHexString(Long.parseLong(inviteInfo.getSsrcInfo().getSsrc())).toUpperCase();
  1321 + }
  1322 + JSONObject jsonObject = zlmresTfulUtils.resumeRtpCheck(mediaServerItem, streamKey);
1246 if (jsonObject == null || jsonObject.getInteger("code") != 0) { 1323 if (jsonObject == null || jsonObject.getInteger("code") != 0) {
1247 throw new ServiceException("继续RTP接收失败"); 1324 throw new ServiceException("继续RTP接收失败");
1248 } 1325 }
src/main/java/com/genersoft/iot/vmp/service/impl/RoleServerImpl.java
1 package com.genersoft.iot.vmp.service.impl; 1 package com.genersoft.iot.vmp.service.impl;
2 2
  3 +import com.baomidou.dynamic.datasource.annotation.DS;
3 import com.genersoft.iot.vmp.service.IRoleService; 4 import com.genersoft.iot.vmp.service.IRoleService;
4 import com.genersoft.iot.vmp.storager.dao.RoleMapper; 5 import com.genersoft.iot.vmp.storager.dao.RoleMapper;
5 import com.genersoft.iot.vmp.storager.dao.dto.Role; 6 import com.genersoft.iot.vmp.storager.dao.dto.Role;
@@ -9,6 +10,7 @@ import org.springframework.stereotype.Service; @@ -9,6 +10,7 @@ import org.springframework.stereotype.Service;
9 import java.util.List; 10 import java.util.List;
10 11
11 @Service 12 @Service
  13 +@DS("master")
12 public class RoleServerImpl implements IRoleService { 14 public class RoleServerImpl implements IRoleService {
13 15
14 @Autowired 16 @Autowired
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.service.impl; @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.service.impl;
2 2
3 import com.alibaba.fastjson2.JSONArray; 3 import com.alibaba.fastjson2.JSONArray;
4 import com.alibaba.fastjson2.JSONObject; 4 import com.alibaba.fastjson2.JSONObject;
  5 +import com.baomidou.dynamic.datasource.annotation.DS;
5 import com.genersoft.iot.vmp.common.GeneralCallback; 6 import com.genersoft.iot.vmp.common.GeneralCallback;
6 import com.genersoft.iot.vmp.common.StreamInfo; 7 import com.genersoft.iot.vmp.common.StreamInfo;
7 import com.genersoft.iot.vmp.conf.DynamicTask; 8 import com.genersoft.iot.vmp.conf.DynamicTask;
@@ -53,6 +54,7 @@ import java.util.stream.Collectors; @@ -53,6 +54,7 @@ import java.util.stream.Collectors;
53 * 视频代理业务 54 * 视频代理业务
54 */ 55 */
55 @Service 56 @Service
  57 +@DS("master")
56 public class StreamProxyServiceImpl implements IStreamProxyService { 58 public class StreamProxyServiceImpl implements IStreamProxyService {
57 59
58 private final static Logger logger = LoggerFactory.getLogger(StreamProxyServiceImpl.class); 60 private final static Logger logger = LoggerFactory.getLogger(StreamProxyServiceImpl.class);
@@ -126,7 +128,13 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -126,7 +128,13 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
126 } 128 }
127 JSONArray dataArray = jsonObject.getJSONArray("data"); 129 JSONArray dataArray = jsonObject.getJSONArray("data");
128 JSONObject mediaServerConfig = dataArray.getJSONObject(0); 130 JSONObject mediaServerConfig = dataArray.getJSONObject(0);
  131 + if (ObjectUtils.isEmpty(param.getFfmpegCmdKey())) {
  132 + param.setFfmpegCmdKey("ffmpeg.cmd");
  133 + }
129 String ffmpegCmd = mediaServerConfig.getString(param.getFfmpegCmdKey()); 134 String ffmpegCmd = mediaServerConfig.getString(param.getFfmpegCmdKey());
  135 + if (ffmpegCmd == null) {
  136 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "ffmpeg拉流代理无法获取ffmpeg cmd");
  137 + }
130 String schema = getSchemaFromFFmpegCmd(ffmpegCmd); 138 String schema = getSchemaFromFFmpegCmd(ffmpegCmd);
131 if (schema == null) { 139 if (schema == null) {
132 throw new ControllerException(ErrorCode.ERROR100.getCode(), "ffmpeg拉流代理无法从ffmpeg cmd中获取到输出格式"); 140 throw new ControllerException(ErrorCode.ERROR100.getCode(), "ffmpeg拉流代理无法从ffmpeg cmd中获取到输出格式");
@@ -401,6 +409,8 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -401,6 +409,8 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
401 logger.info("启用代理失败: {}/{}->{}({})", app, stream, jsonObject.getString("msg"), 409 logger.info("启用代理失败: {}/{}->{}({})", app, stream, jsonObject.getString("msg"),
402 streamProxy.getSrcUrl() == null? streamProxy.getUrl():streamProxy.getSrcUrl()); 410 streamProxy.getSrcUrl() == null? streamProxy.getUrl():streamProxy.getSrcUrl());
403 } 411 }
  412 + } else if (streamProxy != null && streamProxy.isEnable()) {
  413 + return true ;
404 } 414 }
405 return result; 415 return result;
406 } 416 }
@@ -452,7 +462,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -452,7 +462,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
452 streamProxyMapper.deleteAutoRemoveItemByMediaServerId(mediaServerId); 462 streamProxyMapper.deleteAutoRemoveItemByMediaServerId(mediaServerId);
453 463
454 // 移除拉流代理生成的流信息 464 // 移除拉流代理生成的流信息
455 -// syncPullStream(mediaServerId); 465 + syncPullStream(mediaServerId);
456 466
457 // 恢复流代理, 只查找这个这个流媒体 467 // 恢复流代理, 只查找这个这个流媒体
458 List<StreamProxyItem> streamProxyListForEnable = storager.getStreamProxyListForEnableInMediaServer( 468 List<StreamProxyItem> streamProxyListForEnable = storager.getStreamProxyListForEnableInMediaServer(
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
@@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSON; @@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSON;
4 import com.alibaba.fastjson2.JSONArray; 4 import com.alibaba.fastjson2.JSONArray;
5 import com.alibaba.fastjson2.JSONObject; 5 import com.alibaba.fastjson2.JSONObject;
6 import com.alibaba.fastjson2.TypeReference; 6 import com.alibaba.fastjson2.TypeReference;
  7 +import com.baomidou.dynamic.datasource.annotation.DS;
7 import com.genersoft.iot.vmp.conf.MediaConfig; 8 import com.genersoft.iot.vmp.conf.MediaConfig;
8 import com.genersoft.iot.vmp.conf.UserSetting; 9 import com.genersoft.iot.vmp.conf.UserSetting;
9 import com.genersoft.iot.vmp.gb28181.bean.*; 10 import com.genersoft.iot.vmp.gb28181.bean.*;
@@ -36,6 +37,7 @@ import java.util.*; @@ -36,6 +37,7 @@ import java.util.*;
36 import java.util.stream.Collectors; 37 import java.util.stream.Collectors;
37 38
38 @Service 39 @Service
  40 +@DS("master")
39 public class StreamPushServiceImpl implements IStreamPushService { 41 public class StreamPushServiceImpl implements IStreamPushService {
40 42
41 private final static Logger logger = LoggerFactory.getLogger(StreamPushServiceImpl.class); 43 private final static Logger logger = LoggerFactory.getLogger(StreamPushServiceImpl.class);
@@ -282,6 +284,8 @@ public class StreamPushServiceImpl implements IStreamPushService { @@ -282,6 +284,8 @@ public class StreamPushServiceImpl implements IStreamPushService {
282 redisCatchStorage.sendStreamChangeMsg(type, jsonObject); 284 redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
283 // 移除redis内流的信息 285 // 移除redis内流的信息
284 redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream()); 286 redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream());
  287 + // 冗余数据,自己系统中自用
  288 + redisCatchStorage.removePushListItem(offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream(), mediaServerItem.getId());
285 } 289 }
286 } 290 }
287 291
@@ -319,6 +323,9 @@ public class StreamPushServiceImpl implements IStreamPushService { @@ -319,6 +323,9 @@ public class StreamPushServiceImpl implements IStreamPushService {
319 jsonObject.put("register", false); 323 jsonObject.put("register", false);
320 jsonObject.put("mediaServerId", mediaServerId); 324 jsonObject.put("mediaServerId", mediaServerId);
321 redisCatchStorage.sendStreamChangeMsg(type, jsonObject); 325 redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
  326 +
  327 + // 冗余数据,自己系统中自用
  328 + redisCatchStorage.removePushListItem(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream(), mediaServerId);
322 } 329 }
323 } 330 }
324 } 331 }
src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java
1 package com.genersoft.iot.vmp.service.impl; 1 package com.genersoft.iot.vmp.service.impl;
2 2
  3 +import com.baomidou.dynamic.datasource.annotation.DS;
3 import com.genersoft.iot.vmp.service.IUserService; 4 import com.genersoft.iot.vmp.service.IUserService;
4 import com.genersoft.iot.vmp.storager.dao.UserMapper; 5 import com.genersoft.iot.vmp.storager.dao.UserMapper;
5 import com.genersoft.iot.vmp.storager.dao.dto.User; 6 import com.genersoft.iot.vmp.storager.dao.dto.User;
@@ -12,6 +13,7 @@ import org.springframework.util.DigestUtils; @@ -12,6 +13,7 @@ import org.springframework.util.DigestUtils;
12 import java.util.List; 13 import java.util.List;
13 14
14 @Service 15 @Service
  16 +@DS("master")
15 public class UserServiceImpl implements IUserService { 17 public class UserServiceImpl implements IUserService {
16 18
17 @Autowired 19 @Autowired