Commit 26739237e2d93460eb869067a6004bfa63a1bdb8

Authored by 648540858
2 parents 038d360d 0da45229

Merge remote-tracking branch 'origin/wvp-28181-2.0' into commercial

# Conflicts:
#	sql/update.sql
#	src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
#	web_src/src/components/dialog/deviceEdit.vue
#	web_src/src/components/dialog/devicePlayer.vue
Showing 106 changed files with 3655 additions and 1463 deletions
DOCKERFILE
  1 +#很久没维护了,已经与定前版本不匹配
  2 +
  3 +
1 FROM ubuntu:20.04 AS build 4 FROM ubuntu:20.04 AS build
2 5
3 ARG DEBIAN_FRONTEND=noninteractive 6 ARG DEBIAN_FRONTEND=noninteractive
README.md
@@ -107,6 +107,8 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git @@ -107,6 +107,8 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
107 - [X] 鈭垢敶閬蝵脣蝙嚗 107 - [X] 鈭垢敶閬蝵脣蝙嚗
108 - [X] 憭嚗韐蝸雿雿輻 108 - [X] 憭嚗韐蝸雿雿輻
109 - [X] WEB蝡舀H264銝265嚗憸.711A/G.711U/AAC,閬虜蝻撘 109 - [X] WEB蝡舀H264銝265嚗憸.711A/G.711U/AAC,閬虜蝻撘
  110 +- [X] 摮
  111 +- [X] WGS84CJ02銝斤頂
110 112
111 [//]: # (# docker敹恍) 113 [//]: # (# docker敹恍)
112 114
@@ -143,7 +145,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git @@ -143,7 +145,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
143 145
144 # 146 #
145 蝘摰之摰嗅之銝瓷移憸隞乩晶圾蝑隞交R 147 蝘摰之摰嗅之銝瓷移憸隞乩晶圾蝑隞交R
146 -嚗笆隞遣霈桀隞交SSUE嚗隞亙黎銝韏瑁賑甈Z頞憿寧銝剜犖 148 -嚗笆隞遣霈桀隞交SSUE嚗隞亙黎銝韏瑁賑甈Z頞憿寧銝剜犖
  149 +嚗笆隞遣霈桀隞交SSUE嚗隞亙黎銝韏瑁賑甈Z頞憿寧銝剜犖
147 150
148 151
149 152
@@ -163,6 +165,7 @@ QQ蝘縑銝銝, 蝎曉.甈Z之摰嗅蝢日悄霈.閫★撖嫣 @@ -163,6 +165,7 @@ QQ蝘縑銝銝, 蝎曉.甈Z之摰嗅蝢日悄霈.閫★撖嫣
163 [hotcoffie](https://github.com/hotcoffie) [xiaomu](https://github.com/nikmu) [TristingChen](https://github.com/TristingChen) 165 [hotcoffie](https://github.com/hotcoffie) [xiaomu](https://github.com/nikmu) [TristingChen](https://github.com/TristingChen)
164 [chenparty](https://github.com/chenparty) [Hotleave](https://github.com/hotleave) [ydwxb](https://github.com/ydwxb) 166 [chenparty](https://github.com/chenparty) [Hotleave](https://github.com/hotleave) [ydwxb](https://github.com/ydwxb)
165 [ydpd](https://github.com/ydpd) [szy833](https://github.com/szy833) [ydwxb](https://github.com/ydwxb) [Albertzhu666](https://github.com/Albertzhu666) 167 [ydpd](https://github.com/ydpd) [szy833](https://github.com/szy833) [ydwxb](https://github.com/ydwxb) [Albertzhu666](https://github.com/Albertzhu666)
  168 +[mk1990](https://github.com/mk1990)
166 169
167 ps: 葵摰鈭之雿穿洽餈之雿祈頂溶 170 ps: 葵摰鈭之雿穿洽餈之雿祈頂溶
168 171
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 11
12 <groupId>com.genersoft</groupId> 12 <groupId>com.genersoft</groupId>
13 <artifactId>wvp-pro</artifactId> 13 <artifactId>wvp-pro</artifactId>
14 - <version>2.2.1</version> 14 + <version>2.3.1</version>
15 <name>web video platform</name> 15 <name>web video platform</name>
16 <description>国标28181视频平台</description> 16 <description>国标28181视频平台</description>
17 17
@@ -159,9 +159,10 @@ @@ -159,9 +159,10 @@
159 <dependency> 159 <dependency>
160 <groupId>com.alibaba</groupId> 160 <groupId>com.alibaba</groupId>
161 <artifactId>fastjson</artifactId> 161 <artifactId>fastjson</artifactId>
162 - <version>1.2.73</version> 162 + <version>1.2.83</version>
163 </dependency> 163 </dependency>
164 164
  165 +
165 <!-- okhttp --> 166 <!-- okhttp -->
166 <dependency> 167 <dependency>
167 <groupId>com.squareup.okhttp3</groupId> 168 <groupId>com.squareup.okhttp3</groupId>
@@ -180,9 +181,9 @@ @@ -180,9 +181,9 @@
180 181
181 <!-- okhttp-digest --> 182 <!-- okhttp-digest -->
182 <dependency> 183 <dependency>
183 - <groupId>com.burgstaller</groupId> 184 + <groupId>io.github.rburgst</groupId>
184 <artifactId>okhttp-digest</artifactId> 185 <artifactId>okhttp-digest</artifactId>
185 - <version>2.1</version> 186 + <version>2.5</version>
186 </dependency> 187 </dependency>
187 188
188 <!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 --> 189 <!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 -->
sql/mysql.sql
@@ -471,6 +471,7 @@ CREATE TABLE `stream_push` ( @@ -471,6 +471,7 @@ CREATE TABLE `stream_push` (
471 `createStamp` bigint(20) DEFAULT NULL, 471 `createStamp` bigint(20) DEFAULT NULL,
472 `aliveSecond` int(11) DEFAULT NULL, 472 `aliveSecond` int(11) DEFAULT NULL,
473 `mediaServerId` varchar(50) DEFAULT NULL, 473 `mediaServerId` varchar(50) DEFAULT NULL,
  474 + `serverId` varchar(50) not NULL,
474 PRIMARY KEY (`id`), 475 PRIMARY KEY (`id`),
475 UNIQUE KEY `stream_push_pk` (`app`,`stream`) 476 UNIQUE KEY `stream_push_pk` (`app`,`stream`)
476 ) ENGINE=InnoDB AUTO_INCREMENT=300838 DEFAULT CHARSET=utf8mb4; 477 ) ENGINE=InnoDB AUTO_INCREMENT=300838 DEFAULT CHARSET=utf8mb4;
sql/update.sql
  1 +alter table parent_platform
  2 + add startOfflinePush int default 0 null;
  3 +
  4 +alter table parent_platform
  5 + add administrativeDivision varchar(50) not null;
  6 +
  7 +alter table parent_platform
  8 + add catalogGroup int default 1 null;
  9 +
1 alter table device 10 alter table device
2 add audioChannelForReceive VARCHAR(50) null; 11 add audioChannelForReceive VARCHAR(50) null;
3 12
4 alter table device 13 alter table device
5 - add audioChannelForSend VARCHAR(50) null;  
6 \ No newline at end of file 14 \ No newline at end of file
  15 + add audioChannelForSend VARCHAR(50) null;
  16 +
  17 +alter table stream_push
  18 + add serverId varchar(50) not null;
  19 +alter table device
  20 + add geoCoordSys varchar(50) not null;
  21 +update device set device.geoCoordSys='WGS84';
  22 +alter table device_channel
  23 + add longitudeGcj02 double default null;
  24 +alter table device_channel
  25 + add latitudeGcj02 double default null;
  26 +alter table device_channel
  27 + add longitudeWgs84 double default null;
  28 +alter table device_channel
  29 + add latitudeWgs84 double default null;
  30 +
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
@@ -97,4 +97,5 @@ public class VideoManagerConstants { @@ -97,4 +97,5 @@ public class VideoManagerConstants {
97 //************************** 第三方 **************************************** 97 //************************** 第三方 ****************************************
98 public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_"; 98 public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_";
99 public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_"; 99 public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_";
  100 +
100 } 101 }
src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java
@@ -103,12 +103,12 @@ public class DynamicTask { @@ -103,12 +103,12 @@ public class DynamicTask {
103 103
104 public void stop(String key) { 104 public void stop(String key) {
105 if (futureMap.get(key) != null && !futureMap.get(key).isCancelled()) { 105 if (futureMap.get(key) != null && !futureMap.get(key).isCancelled()) {
106 - futureMap.get(key).cancel(true);  
107 - Runnable runnable = runnableMap.get(key);  
108 - if (runnable instanceof ISubscribeTask) {  
109 - ISubscribeTask subscribeTask = (ISubscribeTask) runnable;  
110 - subscribeTask.stop();  
111 - } 106 +// Runnable runnable = runnableMap.get(key);
  107 +// if (runnable instanceof ISubscribeTask) {
  108 +// ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
  109 +// subscribeTask.stop();
  110 +// }
  111 + futureMap.get(key).cancel(false);
112 } 112 }
113 } 113 }
114 114
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
@@ -6,6 +6,10 @@ import org.springframework.beans.factory.annotation.Value; @@ -6,6 +6,10 @@ import org.springframework.beans.factory.annotation.Value;
6 import org.springframework.context.annotation.Configuration; 6 import org.springframework.context.annotation.Configuration;
7 import org.springframework.util.StringUtils; 7 import org.springframework.util.StringUtils;
8 8
  9 +import java.net.InetAddress;
  10 +import java.net.UnknownHostException;
  11 +import java.util.regex.Pattern;
  12 +
9 13
10 @Configuration("mediaConfig") 14 @Configuration("mediaConfig")
11 public class MediaConfig{ 15 public class MediaConfig{
@@ -161,7 +165,18 @@ public class MediaConfig{ @@ -161,7 +165,18 @@ public class MediaConfig{
161 if (StringUtils.isEmpty(sdpIp)){ 165 if (StringUtils.isEmpty(sdpIp)){
162 return ip; 166 return ip;
163 }else { 167 }else {
164 - return sdpIp; 168 + if (isValidIPAddress(sdpIp)) {
  169 + return sdpIp;
  170 + }else {
  171 + // 按照域名解析
  172 + String hostAddress = null;
  173 + try {
  174 + hostAddress = InetAddress.getByName(sdpIp).getHostAddress();
  175 + } catch (UnknownHostException e) {
  176 + throw new RuntimeException(e);
  177 + }
  178 + return hostAddress;
  179 + }
165 } 180 }
166 } 181 }
167 182
@@ -211,4 +226,11 @@ public class MediaConfig{ @@ -211,4 +226,11 @@ public class MediaConfig{
211 return mediaServerItem; 226 return mediaServerItem;
212 } 227 }
213 228
  229 + private boolean isValidIPAddress(String ipAddress) {
  230 + if ((ipAddress != null) && (!ipAddress.isEmpty())) {
  231 + return Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", ipAddress);
  232 + }
  233 + return false;
  234 + }
  235 +
214 } 236 }
src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
@@ -2,7 +2,9 @@ package com.genersoft.iot.vmp.conf; @@ -2,7 +2,9 @@ package com.genersoft.iot.vmp.conf;
2 2
3 import com.genersoft.iot.vmp.common.VideoManagerConstants; 3 import com.genersoft.iot.vmp.common.VideoManagerConstants;
4 import com.genersoft.iot.vmp.service.impl.RedisAlarmMsgListener; 4 import com.genersoft.iot.vmp.service.impl.RedisAlarmMsgListener;
5 -import com.genersoft.iot.vmp.service.impl.RedisGPSMsgListener; 5 +import com.genersoft.iot.vmp.service.impl.RedisGpsMsgListener;
  6 +import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener;
  7 +import com.genersoft.iot.vmp.service.impl.RedisStreamMsgListener;
6 import org.apache.commons.lang3.StringUtils; 8 import org.apache.commons.lang3.StringUtils;
7 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.beans.factory.annotation.Autowired;
8 import org.springframework.beans.factory.annotation.Value; 10 import org.springframework.beans.factory.annotation.Value;
@@ -47,11 +49,17 @@ public class RedisConfig extends CachingConfigurerSupport { @@ -47,11 +49,17 @@ public class RedisConfig extends CachingConfigurerSupport {
47 private int poolMaxWait; 49 private int poolMaxWait;
48 50
49 @Autowired 51 @Autowired
50 - private RedisGPSMsgListener redisGPSMsgListener; 52 + private RedisGpsMsgListener redisGPSMsgListener;
51 53
52 @Autowired 54 @Autowired
53 private RedisAlarmMsgListener redisAlarmMsgListener; 55 private RedisAlarmMsgListener redisAlarmMsgListener;
54 56
  57 + @Autowired
  58 + private RedisStreamMsgListener redisStreamMsgListener;
  59 +
  60 + @Autowired
  61 + private RedisGbPlayMsgListener redisGbPlayMsgListener;
  62 +
55 @Bean 63 @Bean
56 public JedisPool jedisPool() { 64 public JedisPool jedisPool() {
57 if (StringUtils.isBlank(password)) { 65 if (StringUtils.isBlank(password)) {
@@ -98,6 +106,8 @@ public class RedisConfig extends CachingConfigurerSupport { @@ -98,6 +106,8 @@ public class RedisConfig extends CachingConfigurerSupport {
98 container.setConnectionFactory(connectionFactory); 106 container.setConnectionFactory(connectionFactory);
99 container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS)); 107 container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS));
100 container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE)); 108 container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE));
  109 + container.addMessageListener(redisStreamMsgListener, new PatternTopic(VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + "PUSH"));
  110 + container.addMessageListener(redisGbPlayMsgListener, new PatternTopic(RedisGbPlayMsgListener.WVP_PUSH_STREAM_KEY));
101 return container; 111 return container;
102 } 112 }
103 113
src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java
@@ -11,6 +11,9 @@ import javax.servlet.http.HttpServletRequest; @@ -11,6 +11,9 @@ import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse; 11 import javax.servlet.http.HttpServletResponse;
12 import java.io.IOException; 12 import java.io.IOException;
13 13
  14 +/**
  15 + * @author lin
  16 + */
14 @Component 17 @Component
15 public class LoginSuccessHandler implements AuthenticationSuccessHandler { 18 public class LoginSuccessHandler implements AuthenticationSuccessHandler {
16 19
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
@@ -20,6 +20,7 @@ import java.util.List; @@ -20,6 +20,7 @@ import java.util.List;
20 20
21 /** 21 /**
22 * 配置Spring Security 22 * 配置Spring Security
  23 + * @author lin
23 */ 24 */
24 @Configuration 25 @Configuration
25 @EnableWebSecurity 26 @EnableWebSecurity
@@ -132,15 +133,19 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -132,15 +133,19 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
132 .anyRequest().authenticated() 133 .anyRequest().authenticated()
133 // 异常处理(权限拒绝、登录失效等) 134 // 异常处理(权限拒绝、登录失效等)
134 .and().exceptionHandling() 135 .and().exceptionHandling()
135 - .authenticationEntryPoint(anonymousAuthenticationEntryPoint)//匿名用户访问无权限资源时的异常处理 136 + //匿名用户访问无权限资源时的异常处理
  137 + .authenticationEntryPoint(anonymousAuthenticationEntryPoint)
136 // .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源 138 // .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源
137 - // 登入  
138 - .and().formLogin().permitAll()//允许所有用户  
139 - .successHandler(loginSuccessHandler)//登录成功处理逻辑  
140 - .failureHandler(loginFailureHandler)//登录失败处理逻辑 139 + // 登入 允许所有用户
  140 + .and().formLogin().permitAll()
  141 + //登录成功处理逻辑
  142 + .successHandler(loginSuccessHandler)
  143 + //登录失败处理逻辑
  144 + .failureHandler(loginFailureHandler)
141 // 登出 145 // 登出
142 - .and().logout().logoutUrl("/api/user/logout").permitAll()//允许所有用户  
143 - .logoutSuccessHandler(logoutHandler)//登出成功处理逻辑 146 + .and().logout().logoutUrl("/api/user/logout").permitAll()
  147 + //登出成功处理逻辑
  148 + .logoutSuccessHandler(logoutHandler)
144 .deleteCookies("JSESSIONID") 149 .deleteCookies("JSESSIONID")
145 // 会话管理 150 // 会话管理
146 // .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理 151 // .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
1 package com.genersoft.iot.vmp.gb28181.bean; 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2
3 3
  4 +/**
  5 + * 国标设备/平台
  6 + * @author lin
  7 + */
4 public class Device { 8 public class Device {
5 9
6 /** 10 /**
@@ -127,7 +131,12 @@ public class Device { @@ -127,7 +131,12 @@ public class Device {
127 /** 131 /**
128 * 是否开启ssrc校验,默认关闭,开启可以防止串流 132 * 是否开启ssrc校验,默认关闭,开启可以防止串流
129 */ 133 */
130 - private boolean ssrcCheck; 134 + private boolean ssrcCheck = true;
  135 +
  136 + /**
  137 + * 地理坐标系, 目前支持 WGS84,GCJ02 TODO CGCS2000
  138 + */
  139 + private String geoCoordSys;
131 140
132 141
133 public String getDeviceId() { 142 public String getDeviceId() {
@@ -322,4 +331,12 @@ public class Device { @@ -322,4 +331,12 @@ public class Device {
322 this.ssrcCheck = ssrcCheck; 331 this.ssrcCheck = ssrcCheck;
323 } 332 }
324 333
  334 + public String getGeoCoordSys() {
  335 + return geoCoordSys;
  336 + }
  337 +
  338 + public void setGeoCoordSys(String geoCoordSys) {
  339 + this.geoCoordSys = geoCoordSys;
  340 + }
  341 +
325 } 342 }
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java
@@ -155,6 +155,26 @@ public class DeviceChannel { @@ -155,6 +155,26 @@ public class DeviceChannel {
155 private double latitude; 155 private double latitude;
156 156
157 /** 157 /**
  158 + * 经度 GCJ02
  159 + */
  160 + private double longitudeGcj02;
  161 +
  162 + /**
  163 + * 纬度 GCJ02
  164 + */
  165 + private double latitudeGcj02;
  166 +
  167 + /**
  168 + * 经度 WGS84
  169 + */
  170 + private double longitudeWgs84;
  171 +
  172 + /**
  173 + * 纬度 WGS84
  174 + */
  175 + private double latitudeWgs84;
  176 +
  177 + /**
158 * 子设备数 178 * 子设备数
159 */ 179 */
160 private int subCount; 180 private int subCount;
@@ -407,6 +427,38 @@ public class DeviceChannel { @@ -407,6 +427,38 @@ public class DeviceChannel {
407 this.latitude = latitude; 427 this.latitude = latitude;
408 } 428 }
409 429
  430 + public double getLongitudeGcj02() {
  431 + return longitudeGcj02;
  432 + }
  433 +
  434 + public void setLongitudeGcj02(double longitudeGcj02) {
  435 + this.longitudeGcj02 = longitudeGcj02;
  436 + }
  437 +
  438 + public double getLatitudeGcj02() {
  439 + return latitudeGcj02;
  440 + }
  441 +
  442 + public void setLatitudeGcj02(double latitudeGcj02) {
  443 + this.latitudeGcj02 = latitudeGcj02;
  444 + }
  445 +
  446 + public double getLongitudeWgs84() {
  447 + return longitudeWgs84;
  448 + }
  449 +
  450 + public void setLongitudeWgs84(double longitudeWgs84) {
  451 + this.longitudeWgs84 = longitudeWgs84;
  452 + }
  453 +
  454 + public double getLatitudeWgs84() {
  455 + return latitudeWgs84;
  456 + }
  457 +
  458 + public void setLatitudeWgs84(double latitudeWgs84) {
  459 + this.latitudeWgs84 = latitudeWgs84;
  460 + }
  461 +
410 public int getSubCount() { 462 public int getSubCount() {
411 return subCount; 463 return subCount;
412 } 464 }
src/main/java/com/genersoft/iot/vmp/gb28181/bean/HandlerCatchData.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.bean;
  2 +
  3 +import org.dom4j.Element;
  4 +
  5 +import javax.sip.RequestEvent;
  6 +
  7 +/**
  8 + * @author lin
  9 + */
  10 +public class HandlerCatchData {
  11 + private RequestEvent evt;
  12 + private Device device;
  13 + private Element rootElement;
  14 +
  15 + public HandlerCatchData(RequestEvent evt, Device device, Element rootElement) {
  16 + this.evt = evt;
  17 + this.device = device;
  18 + this.rootElement = rootElement;
  19 + }
  20 +
  21 + public RequestEvent getEvt() {
  22 + return evt;
  23 + }
  24 +
  25 + public void setEvt(RequestEvent evt) {
  26 + this.evt = evt;
  27 + }
  28 +
  29 + public Device getDevice() {
  30 + return device;
  31 + }
  32 +
  33 + public void setDevice(Device device) {
  34 + this.device = device;
  35 + }
  36 +
  37 + public Element getRootElement() {
  38 + return rootElement;
  39 + }
  40 +
  41 + public void setRootElement(Element rootElement) {
  42 + this.rootElement = rootElement;
  43 + }
  44 +}
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java
@@ -72,6 +72,11 @@ public class SendRtpItem { @@ -72,6 +72,11 @@ public class SendRtpItem {
72 private String mediaServerId; 72 private String mediaServerId;
73 73
74 /** 74 /**
  75 + * 使用的服务的ID
  76 + */
  77 + private String serverId;
  78 +
  79 + /**
75 * invite的callId 80 * invite的callId
76 */ 81 */
77 private String CallId; 82 private String CallId;
@@ -259,4 +264,12 @@ public class SendRtpItem { @@ -259,4 +264,12 @@ public class SendRtpItem {
259 public void setOnlyAudio(boolean onlyAudio) { 264 public void setOnlyAudio(boolean onlyAudio) {
260 this.onlyAudio = onlyAudio; 265 this.onlyAudio = onlyAudio;
261 } 266 }
  267 +
  268 + public String getServerId() {
  269 + return serverId;
  270 + }
  271 +
  272 + public void setServerId(String serverId) {
  273 + this.serverId = serverId;
  274 + }
262 } 275 }
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.bean; @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.bean;
2 2
3 import com.genersoft.iot.vmp.common.VideoManagerConstants; 3 import com.genersoft.iot.vmp.common.VideoManagerConstants;
4 import com.genersoft.iot.vmp.conf.DynamicTask; 4 import com.genersoft.iot.vmp.conf.DynamicTask;
  5 +import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
5 import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeHandlerTask; 6 import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeHandlerTask;
6 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; 7 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
7 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 8 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -38,7 +39,6 @@ public class SubscribeHolder { @@ -38,7 +39,6 @@ public class SubscribeHolder {
38 catalogMap.put(platformId, subscribeInfo); 39 catalogMap.put(platformId, subscribeInfo);
39 // 添加订阅到期 40 // 添加订阅到期
40 String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId; 41 String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId;
41 - dynamicTask.stop(taskOverdueKey);  
42 // 添加任务处理订阅过期 42 // 添加任务处理订阅过期
43 dynamicTask.startDelay(taskOverdueKey, () -> removeCatalogSubscribe(subscribeInfo.getId()), 43 dynamicTask.startDelay(taskOverdueKey, () -> removeCatalogSubscribe(subscribeInfo.getId()),
44 subscribeInfo.getExpires() * 1000); 44 subscribeInfo.getExpires() * 1000);
@@ -49,10 +49,17 @@ public class SubscribeHolder { @@ -49,10 +49,17 @@ public class SubscribeHolder {
49 } 49 }
50 50
51 public void removeCatalogSubscribe(String platformId) { 51 public void removeCatalogSubscribe(String platformId) {
  52 +
52 catalogMap.remove(platformId); 53 catalogMap.remove(platformId);
53 String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId; 54 String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId;
  55 + Runnable runnable = dynamicTask.get(taskOverdueKey);
  56 + if (runnable instanceof ISubscribeTask) {
  57 + ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
  58 + subscribeTask.stop();
  59 + }
54 // 添加任务处理订阅过期 60 // 添加任务处理订阅过期
55 dynamicTask.stop(taskOverdueKey); 61 dynamicTask.stop(taskOverdueKey);
  62 +
56 } 63 }
57 64
58 public void putMobilePositionSubscribe(String platformId, SubscribeInfo subscribeInfo) { 65 public void putMobilePositionSubscribe(String platformId, SubscribeInfo subscribeInfo) {
@@ -63,7 +70,6 @@ public class SubscribeHolder { @@ -63,7 +70,6 @@ public class SubscribeHolder {
63 storager, platformId, subscribeInfo.getSn(), key, this, dynamicTask), 70 storager, platformId, subscribeInfo.getSn(), key, this, dynamicTask),
64 subscribeInfo.getGpsInterval() * 1000); 71 subscribeInfo.getGpsInterval() * 1000);
65 String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId; 72 String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId;
66 - dynamicTask.stop(taskOverdueKey);  
67 // 添加任务处理订阅过期 73 // 添加任务处理订阅过期
68 dynamicTask.startDelay(taskOverdueKey, () -> { 74 dynamicTask.startDelay(taskOverdueKey, () -> {
69 removeMobilePositionSubscribe(subscribeInfo.getId()); 75 removeMobilePositionSubscribe(subscribeInfo.getId());
@@ -81,6 +87,11 @@ public class SubscribeHolder { @@ -81,6 +87,11 @@ public class SubscribeHolder {
81 // 结束任务处理GPS定时推送 87 // 结束任务处理GPS定时推送
82 dynamicTask.stop(key); 88 dynamicTask.stop(key);
83 String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId; 89 String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId;
  90 + Runnable runnable = dynamicTask.get(taskOverdueKey);
  91 + if (runnable instanceof ISubscribeTask) {
  92 + ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
  93 + subscribeTask.stop();
  94 + }
84 // 添加任务处理订阅过期 95 // 添加任务处理订阅过期
85 dynamicTask.stop(taskOverdueKey); 96 dynamicTask.stop(taskOverdueKey);
86 } 97 }
src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
@@ -88,8 +88,8 @@ public class SipSubscribe { @@ -88,8 +88,8 @@ public class SipSubscribe {
88 this.type = "timeout"; 88 this.type = "timeout";
89 this.msg = "消息超时未回复"; 89 this.msg = "消息超时未回复";
90 this.statusCode = -1024; 90 this.statusCode = -1024;
91 - this.callId = timeoutEvent.getClientTransaction().getDialog().getCallId().getCallId();  
92 this.dialog = timeoutEvent.getClientTransaction().getDialog(); 91 this.dialog = timeoutEvent.getClientTransaction().getDialog();
  92 + this.callId = this.dialog != null?timeoutEvent.getClientTransaction().getDialog().getCallId().getCallId(): null;
93 }else if (event instanceof TransactionTerminatedEvent) { 93 }else if (event instanceof TransactionTerminatedEvent) {
94 TransactionTerminatedEvent transactionTerminatedEvent = (TransactionTerminatedEvent)event; 94 TransactionTerminatedEvent transactionTerminatedEvent = (TransactionTerminatedEvent)event;
95 this.type = "transactionTerminated"; 95 this.type = "transactionTerminated";
@@ -109,8 +109,8 @@ public class SipSubscribe { @@ -109,8 +109,8 @@ public class SipSubscribe {
109 this.type = "deviceNotFoundEvent"; 109 this.type = "deviceNotFoundEvent";
110 this.msg = "设备未找到"; 110 this.msg = "设备未找到";
111 this.statusCode = -1024; 111 this.statusCode = -1024;
112 - this.callId = deviceNotFoundEvent.getDialog().getCallId().getCallId();  
113 this.dialog = deviceNotFoundEvent.getDialog(); 112 this.dialog = deviceNotFoundEvent.getDialog();
  113 + this.callId = this.dialog != null ?deviceNotFoundEvent.getDialog().getCallId().getCallId() : null;
114 } 114 }
115 } 115 }
116 } 116 }
@@ -130,6 +130,9 @@ public class SipSubscribe { @@ -130,6 +130,9 @@ public class SipSubscribe {
130 } 130 }
131 131
132 public void removeErrorSubscribe(String key) { 132 public void removeErrorSubscribe(String key) {
  133 + if(key == null){
  134 + return;
  135 + }
133 errorSubscribes.remove(key); 136 errorSubscribes.remove(key);
134 errorTimeSubscribes.remove(key); 137 errorTimeSubscribes.remove(key);
135 } 138 }
@@ -139,6 +142,9 @@ public class SipSubscribe { @@ -139,6 +142,9 @@ public class SipSubscribe {
139 } 142 }
140 143
141 public void removeOkSubscribe(String key) { 144 public void removeOkSubscribe(String key) {
  145 + if(key == null){
  146 + return;
  147 + }
142 okSubscribes.remove(key); 148 okSubscribes.remove(key);
143 okTimeSubscribes.remove(key); 149 okTimeSubscribes.remove(key);
144 } 150 }
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
@@ -66,7 +66,7 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; { @@ -66,7 +66,7 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; {
66 subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId()); 66 subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId());
67 67
68 if (subscribe == null) { 68 if (subscribe == null) {
69 - logger.info("发送订阅消息时发现订阅信息已经不存在"); 69 + logger.info("发送订阅消息时发现订阅信息已经不存在: {}", event.getPlatformId());
70 return; 70 return;
71 } 71 }
72 }else { 72 }else {
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
@@ -99,8 +99,8 @@ public class VideoStreamSessionManager { @@ -99,8 +99,8 @@ public class VideoStreamSessionManager {
99 return dialog; 99 return dialog;
100 } 100 }
101 101
102 - public SIPDialog getDialogByCallId(String deviceId, String channelId, String callID){  
103 - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callID, null); 102 + public SIPDialog getDialogByCallId(String deviceId, String channelId, String callId){
  103 + SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null);
104 if (ssrcTransaction == null) { 104 if (ssrcTransaction == null) {
105 return null; 105 return null;
106 } 106 }
@@ -108,11 +108,17 @@ public class VideoStreamSessionManager { @@ -108,11 +108,17 @@ public class VideoStreamSessionManager {
108 if (dialogByteArray == null) { 108 if (dialogByteArray == null) {
109 return null; 109 return null;
110 } 110 }
111 - SIPDialog dialog = (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);  
112 - return dialog; 111 + return (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
113 } 112 }
114 113
115 public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){ 114 public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){
  115 +
  116 + if (StringUtils.isEmpty(deviceId)) {
  117 + deviceId ="*";
  118 + }
  119 + if (StringUtils.isEmpty(channelId)) {
  120 + channelId ="*";
  121 + }
116 if (StringUtils.isEmpty(callId)) { 122 if (StringUtils.isEmpty(callId)) {
117 callId ="*"; 123 callId ="*";
118 } 124 }
@@ -179,7 +185,7 @@ public class VideoStreamSessionManager { @@ -179,7 +185,7 @@ public class VideoStreamSessionManager {
179 185
180 186
181 public List<SsrcTransaction> getAllSsrc() { 187 public List<SsrcTransaction> getAllSsrc() {
182 - List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetting.getServerId() + "_" )); 188 + List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetting.getServerId()));
183 List<SsrcTransaction> result= new ArrayList<>(); 189 List<SsrcTransaction> result= new ArrayList<>();
184 for (int i = 0; i < ssrcTransactionKeys.size(); i++) { 190 for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
185 String key = (String)ssrcTransactionKeys.get(i); 191 String key = (String)ssrcTransactionKeys.get(i);
src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java
@@ -71,7 +71,9 @@ public class MobilePositionSubscribeHandlerTask implements ISubscribeTask { @@ -71,7 +71,9 @@ public class MobilePositionSubscribeHandlerTask implements ISubscribeTask {
71 String gbId = gbStream.getGbId(); 71 String gbId = gbStream.getGbId();
72 GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(gbId); 72 GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(gbId);
73 if (gpsMsgInfo != null) { // 无最新位置不发送 73 if (gpsMsgInfo != null) { // 无最新位置不发送
74 - logger.info("无最新位置不发送"); 74 + if (logger.isDebugEnabled()) {
  75 + logger.debug("无最新位置不发送");
  76 + }
75 // 经纬度都为0不发送 77 // 经纬度都为0不发送
76 if (gpsMsgInfo.getLng() == 0 && gpsMsgInfo.getLat() == 0) { 78 if (gpsMsgInfo.getLng() == 0 && gpsMsgInfo.getLat() == 0) {
77 continue; 79 continue;
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java
@@ -150,30 +150,24 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { @@ -150,30 +150,24 @@ public class SIPProcessorObserver implements ISIPProcessorObserver {
150 public void processTimeout(TimeoutEvent timeoutEvent) { 150 public void processTimeout(TimeoutEvent timeoutEvent) {
151 logger.info("[消息发送超时]"); 151 logger.info("[消息发送超时]");
152 ClientTransaction clientTransaction = timeoutEvent.getClientTransaction(); 152 ClientTransaction clientTransaction = timeoutEvent.getClientTransaction();
153 - eventPublisher.requestTimeOut(timeoutEvent); 153 +
154 if (clientTransaction != null) { 154 if (clientTransaction != null) {
  155 + logger.info("[发送错误订阅] clientTransaction != null");
155 Request request = clientTransaction.getRequest(); 156 Request request = clientTransaction.getRequest();
156 if (request != null) { 157 if (request != null) {
  158 + logger.info("[发送错误订阅] request != null");
157 CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); 159 CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
158 if (callIdHeader != null) { 160 if (callIdHeader != null) {
  161 + logger.info("[发送错误订阅]");
159 SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); 162 SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId());
160 SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(timeoutEvent); 163 SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(timeoutEvent);
161 subscribe.response(eventResult); 164 subscribe.response(eventResult);
  165 + sipSubscribe.removeOkSubscribe(callIdHeader.getCallId());
162 sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId()); 166 sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId());
163 } 167 }
164 } 168 }
165 } 169 }
166 -  
167 -// Timeout timeout = timeoutEvent.getTimeout();  
168 -// ServerTransaction serverTransaction = timeoutEvent.getServerTransaction();  
169 -// if (serverTransaction != null) {  
170 -// Request request = serverTransaction.getRequest();  
171 -// URI requestURI = request.getRequestURI();  
172 -// Header header = request.getHeader(FromHeader.NAME);  
173 -// }  
174 -// if(timeoutProcessor != null) {  
175 -// timeoutProcessor.process(timeoutEvent);  
176 -// } 170 + eventPublisher.requestTimeOut(timeoutEvent);
177 } 171 }
178 172
179 @Override 173 @Override
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -148,6 +148,14 @@ public interface ISIPCommander { @@ -148,6 +148,14 @@ public interface ISIPCommander {
148 * 回放倍速播放 148 * 回放倍速播放
149 */ 149 */
150 void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed); 150 void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed);
  151 +
  152 + /**
  153 + * 回放控制
  154 + * @param device
  155 + * @param streamInfo
  156 + * @param content
  157 + */
  158 + void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent);
151 159
152 160
153 /** 161 /**
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
@@ -104,6 +104,14 @@ public interface ISIPCommanderForPlatform { @@ -104,6 +104,14 @@ public interface ISIPCommanderForPlatform {
104 boolean recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo); 104 boolean recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo);
105 105
106 /** 106 /**
  107 + * 录像播放推送完成时发送MediaStatus消息
  108 + * @param platform
  109 + * @param sendRtpItem
  110 + * @return
  111 + */
  112 + boolean sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem);
  113 +
  114 + /**
107 * 向发起点播的上级回复bye 115 * 向发起点播的上级回复bye
108 * @param platform 平台信息 116 * @param platform 平台信息
109 * @param callId callId 117 * @param callId callId
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -32,7 +32,9 @@ import org.springframework.stereotype.Component; @@ -32,7 +32,9 @@ import org.springframework.stereotype.Component;
32 import org.springframework.util.StringUtils; 32 import org.springframework.util.StringUtils;
33 33
34 import javax.sip.*; 34 import javax.sip.*;
  35 +import javax.sip.address.Address;
35 import javax.sip.address.SipURI; 36 import javax.sip.address.SipURI;
  37 +import javax.sip.address.URI;
36 import javax.sip.header.*; 38 import javax.sip.header.*;
37 import javax.sip.message.Request; 39 import javax.sip.message.Request;
38 import java.lang.reflect.Field; 40 import java.lang.reflect.Field;
@@ -708,22 +710,19 @@ public class SIPCommander implements ISIPCommander { @@ -708,22 +710,19 @@ public class SIPCommander implements ISIPCommander {
708 } 710 }
709 SIPDialog dialog; 711 SIPDialog dialog;
710 if (callId != null) { 712 if (callId != null) {
711 - dialog = streamSession.getDialogByCallId(deviceId, channelId, callId); 713 + dialog = streamSession.getDialogByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), callId);
712 }else { 714 }else {
713 - if (stream == null) { 715 + if (stream == null && ssrcTransaction == null && ssrcTransaction.getStream() == null) {
714 return; 716 return;
715 } 717 }
716 - dialog = streamSession.getDialogByStream(deviceId, channelId, stream);  
717 - }  
718 - if (ssrcTransaction != null) {  
719 - MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());  
720 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc());  
721 - mediaServerService.closeRTPServer(deviceId, channelId, ssrcTransaction.getStream());  
722 - streamSession.remove(deviceId, channelId, ssrcTransaction.getStream()); 718 + dialog = streamSession.getDialogByStream(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
723 } 719 }
  720 + mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
  721 + mediaServerService.closeRTPServer(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
  722 + streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
724 723
725 if (dialog == null) { 724 if (dialog == null) {
726 - logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", deviceId, channelId); 725 + logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
727 return; 726 return;
728 } 727 }
729 SipStack sipStack = udpSipProvider.getSipStack(); 728 SipStack sipStack = udpSipProvider.getSipStack();
@@ -1456,12 +1455,20 @@ public class SIPCommander implements ISIPCommander { @@ -1456,12 +1455,20 @@ public class SIPCommander implements ISIPCommander {
1456 1455
1457 Request request; 1456 Request request;
1458 if (dialog != null) { 1457 if (dialog != null) {
1459 - logger.info("发送移动位置订阅消息时 dialog的状态为: {}", dialog.getState()); 1458 + SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
1460 request = dialog.createRequest(Request.SUBSCRIBE); 1459 request = dialog.createRequest(Request.SUBSCRIBE);
  1460 + ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForCatalog());
  1461 + request.setExpires(expiresHeader);
  1462 +
  1463 + request.setRequestURI(requestURI);
  1464 +
1461 ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); 1465 ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
1462 request.setContent(subscribePostitionXml.toString(), contentTypeHeader); 1466 request.setContent(subscribePostitionXml.toString(), contentTypeHeader);
1463 - ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForMobilePosition());  
1464 - request.addHeader(expireHeader); 1467 +
  1468 + CSeqHeader cSeqHeader = (CSeqHeader)request.getHeader(CSeqHeader.NAME);
  1469 + cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ(Request.SUBSCRIBE));
  1470 + request.removeHeader(CSeqHeader.NAME);
  1471 + request.addHeader(cSeqHeader);
1465 }else { 1472 }else {
1466 String tm = Long.toString(System.currentTimeMillis()); 1473 String tm = Long.toString(System.currentTimeMillis());
1467 CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() 1474 CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
@@ -1552,12 +1559,21 @@ public class SIPCommander implements ISIPCommander { @@ -1552,12 +1559,21 @@ public class SIPCommander implements ISIPCommander {
1552 1559
1553 Request request; 1560 Request request;
1554 if (dialog != null) { 1561 if (dialog != null) {
1555 - logger.info("发送目录订阅消息时 dialog的状态为: {}", dialog.getState()); 1562 + SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
1556 request = dialog.createRequest(Request.SUBSCRIBE); 1563 request = dialog.createRequest(Request.SUBSCRIBE);
  1564 + ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForCatalog());
  1565 + request.setExpires(expiresHeader);
  1566 +
  1567 + request.setRequestURI(requestURI);
  1568 +
1557 ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); 1569 ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
1558 request.setContent(cmdXml.toString(), contentTypeHeader); 1570 request.setContent(cmdXml.toString(), contentTypeHeader);
1559 - ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForMobilePosition());  
1560 - request.addHeader(expireHeader); 1571 +
  1572 + CSeqHeader cSeqHeader = (CSeqHeader)request.getHeader(CSeqHeader.NAME);
  1573 + cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ(Request.SUBSCRIBE));
  1574 + request.removeHeader(CSeqHeader.NAME);
  1575 + request.addHeader(cSeqHeader);
  1576 +
1561 }else { 1577 }else {
1562 String tm = Long.toString(System.currentTimeMillis()); 1578 String tm = Long.toString(System.currentTimeMillis());
1563 1579
@@ -1779,6 +1795,43 @@ public class SIPCommander implements ISIPCommander { @@ -1779,6 +1795,43 @@ public class SIPCommander implements ISIPCommander {
1779 e.printStackTrace(); 1795 e.printStackTrace();
1780 } 1796 }
1781 } 1797 }
  1798 +
  1799 + @Override
  1800 + public void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) {
  1801 + try {
  1802 + Request request = headerProvider.createInfoRequest(device, streamInfo, content);
  1803 + if (request == null) {
  1804 + return;
  1805 + }
  1806 + logger.info(request.toString());
  1807 + ClientTransaction clientTransaction = null;
  1808 + if ("TCP".equals(device.getTransport())) {
  1809 + clientTransaction = tcpSipProvider.getNewClientTransaction(request);
  1810 + } else if ("UDP".equals(device.getTransport())) {
  1811 + clientTransaction = udpSipProvider.getNewClientTransaction(request);
  1812 + }
  1813 + CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
  1814 + if(errorEvent != null) {
  1815 + sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
  1816 + errorEvent.response(eventResult);
  1817 + sipSubscribe.removeErrorSubscribe(eventResult.callId);
  1818 + sipSubscribe.removeOkSubscribe(eventResult.callId);
  1819 + }));
  1820 + }
  1821 +
  1822 + if(okEvent != null) {
  1823 + sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
  1824 + okEvent.response(eventResult);
  1825 + sipSubscribe.removeOkSubscribe(eventResult.callId);
  1826 + sipSubscribe.removeErrorSubscribe(eventResult.callId);
  1827 + });
  1828 + }
  1829 + clientTransaction.sendRequest();
  1830 +
  1831 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1832 + e.printStackTrace();
  1833 + }
  1834 + }
1782 1835
1783 @Override 1836 @Override
1784 public boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) { 1837 public boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -31,6 +31,7 @@ import javax.sip.address.SipURI; @@ -31,6 +31,7 @@ import javax.sip.address.SipURI;
31 import javax.sip.header.*; 31 import javax.sip.header.*;
32 import javax.sip.message.Request; 32 import javax.sip.message.Request;
33 import java.lang.reflect.Field; 33 import java.lang.reflect.Field;
  34 +import java.net.InetAddress;
34 import java.text.ParseException; 35 import java.text.ParseException;
35 import java.util.ArrayList; 36 import java.util.ArrayList;
36 import java.util.HashSet; 37 import java.util.HashSet;
@@ -276,8 +277,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -276,8 +277,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
276 catalogXml.append("<Owner>" + channel.getOwner() + "</Owner>\r\n"); 277 catalogXml.append("<Owner>" + channel.getOwner() + "</Owner>\r\n");
277 catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n"); 278 catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n");
278 catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n"); 279 catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
279 - catalogXml.append("<Longitude>" + channel.getLongitude() + "</Longitude>\r\n");  
280 - catalogXml.append("<Latitude>" + channel.getLatitude() + "</Latitude>\r\n"); 280 + catalogXml.append("<Longitude>" + channel.getLongitudeWgs84() + "</Longitude>\r\n");
  281 + catalogXml.append("<Latitude>" + channel.getLatitudeWgs84() + "</Latitude>\r\n");
281 catalogXml.append("<IPAddress>" + channel.getIpAddress() + "</IPAddress>\r\n"); 282 catalogXml.append("<IPAddress>" + channel.getIpAddress() + "</IPAddress>\r\n");
282 catalogXml.append("<Port>" + channel.getPort() + "</Port>\r\n"); 283 catalogXml.append("<Port>" + channel.getPort() + "</Port>\r\n");
283 catalogXml.append("<Info>\r\n"); 284 catalogXml.append("<Info>\r\n");
@@ -546,14 +547,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -546,14 +547,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
546 } 547 }
547 notifyRequest.addHeader(event); 548 notifyRequest.addHeader(event);
548 SipURI sipURI = (SipURI) notifyRequest.getRequestURI(); 549 SipURI sipURI = (SipURI) notifyRequest.getRequestURI();
549 - if (subscribeInfo.getTransaction() != null) {  
550 - SIPRequest request = (SIPRequest) subscribeInfo.getTransaction().getRequest();  
551 - sipURI.setHost(request.getRemoteAddress().getHostAddress());  
552 - sipURI.setPort(request.getRemotePort());  
553 - }else {  
554 - sipURI.setHost(parentPlatform.getServerIP());  
555 - sipURI.setPort(parentPlatform.getServerPort());  
556 - } 550 + sipURI.setHost(parentPlatform.getServerIP());
  551 + sipURI.setPort(parentPlatform.getServerPort());
557 552
558 ClientTransaction transaction = null; 553 ClientTransaction transaction = null;
559 if ("TCP".equals(parentPlatform.getTransport())) { 554 if ("TCP".equals(parentPlatform.getTransport())) {
@@ -751,6 +746,82 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -751,6 +746,82 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
751 } 746 }
752 747
753 @Override 748 @Override
  749 + public boolean sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem) {
  750 + if (sendRtpItem == null) {
  751 + return false;
  752 + }
  753 + if (platform == null) {
  754 + return false;
  755 + }
  756 +
  757 + byte[] dialogByteArray = sendRtpItem.getDialog();
  758 + if (dialogByteArray == null) {
  759 + return false;
  760 + }
  761 + try{
  762 + SIPDialog dialog = (SIPDialog) SerializeUtils.deSerialize(dialogByteArray);
  763 + SipStack sipStack;
  764 + if ("TCP".equals(platform.getTransport())) {
  765 + sipStack = tcpSipProvider.getSipStack();
  766 + } else {
  767 + sipStack = udpSipProvider.getSipStack();
  768 + }
  769 + SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
  770 + if (dialog != sipDialog) {
  771 + dialog = sipDialog;
  772 + }
  773 + if ("TCP".equals(platform.getTransport())) {
  774 + dialog.setSipProvider(tcpSipProvider);
  775 + } else {
  776 + dialog.setSipProvider(udpSipProvider);
  777 + }
  778 +
  779 + Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
  780 + sipStackField.setAccessible(true);
  781 + sipStackField.set(dialog, sipStack);
  782 + Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
  783 + eventListenersField.setAccessible(true);
  784 + eventListenersField.set(dialog, new HashSet<>());
  785 +
  786 + SIPRequest messageRequest = (SIPRequest)dialog.createRequest(Request.MESSAGE);
  787 + String characterSet = platform.getCharacterSet();
  788 + StringBuffer mediaStatusXml = new StringBuffer(200);
  789 + mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
  790 + mediaStatusXml.append("<Notify>\r\n");
  791 + mediaStatusXml.append("<CmdType>MediaStatus</CmdType>\r\n");
  792 + mediaStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  793 + mediaStatusXml.append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n");
  794 + mediaStatusXml.append("<NotifyType>121</NotifyType>\r\n");
  795 + mediaStatusXml.append("</Notify>\r\n");
  796 + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
  797 + messageRequest.setContent(mediaStatusXml.toString(), contentTypeHeader);
  798 + SipURI sipURI = (SipURI) messageRequest.getRequestURI();
  799 + sipURI.setHost(platform.getServerIP());
  800 + sipURI.setPort(platform.getServerPort());
  801 + ClientTransaction clientTransaction;
  802 + if ("TCP".equals(platform.getTransport())) {
  803 + clientTransaction = tcpSipProvider.getNewClientTransaction(messageRequest);
  804 + }else {
  805 + clientTransaction = udpSipProvider.getNewClientTransaction(messageRequest);
  806 + }
  807 + dialog.sendRequest(clientTransaction);
  808 + } catch (SipException e) {
  809 + e.printStackTrace();
  810 + return false;
  811 + } catch (ParseException e) {
  812 + e.printStackTrace();
  813 + return false;
  814 + } catch (NoSuchFieldException e) {
  815 + throw new RuntimeException(e);
  816 + } catch (IllegalAccessException e) {
  817 + throw new RuntimeException(e);
  818 + }
  819 + return true;
  820 +
  821 +
  822 + }
  823 +
  824 + @Override
754 public void streamByeCmd(ParentPlatform platform, String callId) { 825 public void streamByeCmd(ParentPlatform platform, String callId) {
755 if (platform == null) { 826 if (platform == null) {
756 return; 827 return;
@@ -766,45 +837,51 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -766,45 +837,51 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
766 byte[] dialogByteArray = sendRtpItem.getDialog(); 837 byte[] dialogByteArray = sendRtpItem.getDialog();
767 if (dialogByteArray != null) { 838 if (dialogByteArray != null) {
768 SIPDialog dialog = (SIPDialog) SerializeUtils.deSerialize(dialogByteArray); 839 SIPDialog dialog = (SIPDialog) SerializeUtils.deSerialize(dialogByteArray);
769 - SipStack sipStack = udpSipProvider.getSipStack(); 840 + SipStack sipStack;
  841 + if ("TCP".equals(platform.getTransport())) {
  842 + sipStack = tcpSipProvider.getSipStack();
  843 + } else {
  844 + sipStack = udpSipProvider.getSipStack();
  845 + }
770 SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog); 846 SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
771 if (dialog != sipDialog) { 847 if (dialog != sipDialog) {
772 dialog = sipDialog; 848 dialog = sipDialog;
773 - } else {  
774 - try { 849 + }
  850 + try {
  851 + if ("TCP".equals(platform.getTransport())) {
  852 + dialog.setSipProvider(tcpSipProvider);
  853 + } else {
775 dialog.setSipProvider(udpSipProvider); 854 dialog.setSipProvider(udpSipProvider);
776 - Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");  
777 - sipStackField.setAccessible(true);  
778 - sipStackField.set(dialog, sipStack);  
779 - Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");  
780 - eventListenersField.setAccessible(true);  
781 - eventListenersField.set(dialog, new HashSet<>());  
782 -  
783 - byte[] transactionByteArray = sendRtpItem.getTransaction();  
784 - ClientTransaction clientTransaction = (ClientTransaction) SerializeUtils.deSerialize(transactionByteArray);  
785 - Request byeRequest = dialog.createRequest(Request.BYE);  
786 -  
787 - SipURI byeURI = (SipURI) byeRequest.getRequestURI();  
788 - SIPRequest request = (SIPRequest) clientTransaction.getRequest();  
789 - byeURI.setHost(request.getRemoteAddress().getHostAddress());  
790 - byeURI.setPort(request.getRemotePort());  
791 - if ("TCP".equals(platform.getTransport())) {  
792 - clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);  
793 - } else if ("UDP".equals(platform.getTransport())) {  
794 - clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);  
795 - }  
796 - dialog.sendRequest(clientTransaction);  
797 - } catch (SipException e) {  
798 - e.printStackTrace();  
799 - } catch (ParseException e) {  
800 - e.printStackTrace();  
801 - } catch (NoSuchFieldException e) {  
802 - e.printStackTrace();  
803 - } catch (IllegalAccessException e) {  
804 - e.printStackTrace();  
805 } 855 }
806 - 856 + Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
  857 + sipStackField.setAccessible(true);
  858 + sipStackField.set(dialog, sipStack);
  859 + Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
  860 + eventListenersField.setAccessible(true);
  861 + eventListenersField.set(dialog, new HashSet<>());
  862 +
  863 + Request byeRequest = dialog.createRequest(Request.BYE);
  864 +
  865 + SipURI byeURI = (SipURI) byeRequest.getRequestURI();
  866 + byeURI.setHost(platform.getServerIP());
  867 + byeURI.setPort(platform.getServerPort());
  868 + ClientTransaction clientTransaction;
  869 + if ("TCP".equals(platform.getTransport())) {
  870 + clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);
  871 + } else {
  872 + clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
  873 + }
  874 + dialog.sendRequest(clientTransaction);
  875 + } catch (SipException e) {
  876 + e.printStackTrace();
  877 + } catch (ParseException e) {
  878 + e.printStackTrace();
  879 + } catch (NoSuchFieldException e) {
  880 + e.printStackTrace();
  881 + } catch (IllegalAccessException e) {
  882 + e.printStackTrace();
807 } 883 }
  884 +
808 } 885 }
809 } 886 }
810 } 887 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorAbstract.java deleted 100644 → 0
1 -package com.genersoft.iot.vmp.gb28181.transmit.event.request;  
2 -  
3 -import gov.nist.javax.sip.SipProviderImpl;  
4 -import org.springframework.beans.factory.annotation.Autowired;  
5 -import org.springframework.beans.factory.annotation.Qualifier;  
6 -  
7 -/**  
8 - * @description:处理接收IPCamera发来的SIP协议请求消息  
9 - * @author: songww  
10 - * @date: 2020年5月3日 下午4:42:22  
11 - */  
12 -public abstract class SIPRequestProcessorAbstract {  
13 -  
14 -  
15 - @Autowired  
16 - @Qualifier(value="tcpSipProvider")  
17 - private SipProviderImpl tcpSipProvider;  
18 -  
19 - @Autowired  
20 - @Qualifier(value="udpSipProvider")  
21 - private SipProviderImpl udpSipProvider;  
22 -  
23 -}  
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
@@ -15,10 +15,13 @@ import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; @@ -15,10 +15,13 @@ import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
15 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; 15 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
16 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 16 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
17 import com.genersoft.iot.vmp.service.IMediaServerService; 17 import com.genersoft.iot.vmp.service.IMediaServerService;
  18 +import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
  19 +import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener;
18 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 20 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
19 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 21 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
20 import gov.nist.javax.sip.message.SIPRequest; 22 import gov.nist.javax.sip.message.SIPRequest;
21 import gov.nist.javax.sip.stack.SIPDialog; 23 import gov.nist.javax.sip.stack.SIPDialog;
  24 +import com.genersoft.iot.vmp.utils.SerializeUtils;
22 import org.ehcache.shadow.org.terracotta.offheapstore.storage.IntegerStorageEngine; 25 import org.ehcache.shadow.org.terracotta.offheapstore.storage.IntegerStorageEngine;
23 import org.slf4j.Logger; 26 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory; 27 import org.slf4j.LoggerFactory;
@@ -46,7 +49,7 @@ import java.util.*; @@ -46,7 +49,7 @@ import java.util.*;
46 public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { 49 public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
47 50
48 private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class); 51 private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class);
49 - private String method = "ACK"; 52 + private final String method = "ACK";
50 53
51 @Autowired 54 @Autowired
52 private SIPProcessorObserver sipProcessorObserver; 55 private SIPProcessorObserver sipProcessorObserver;
@@ -84,6 +87,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In @@ -84,6 +87,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
84 @Autowired 87 @Autowired
85 private AudioBroadcastManager audioBroadcastManager; 88 private AudioBroadcastManager audioBroadcastManager;
86 89
  90 + @Autowired
  91 + private RedisGbPlayMsgListener redisGbPlayMsgListener;
  92 +
87 93
88 /** 94 /**
89 * 处理 ACK请求 95 * 处理 ACK请求
@@ -159,60 +165,41 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In @@ -159,60 +165,41 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
159 // 向上级平台 165 // 向上级平台
160 commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId()); 166 commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId());
161 } 167 }
  168 + if (mediaInfo == null) {
  169 + RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance(
  170 + sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStreamId(),
  171 + sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(),
  172 + sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio());
  173 + redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, jsonObject->{
  174 + startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader);
  175 + });
  176 + }else {
  177 + JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
  178 + startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader);
162 } 179 }
163 180
164 181
165 -// if (streamInfo == null) { // 流还没上来,对方就回复ack  
166 -// logger.info("监听流以等待流上线1 rtp/{}", sendRtpItem.getStreamId());  
167 -// // 监听流上线  
168 -// // 添加订阅  
169 -// JSONObject subscribeKey = new JSONObject();  
170 -// subscribeKey.put("app", "rtp");  
171 -// subscribeKey.put("stream", sendRtpItem.getStreamId());  
172 -// subscribeKey.put("regist", true);  
173 -// subscribeKey.put("schema", "rtmp");  
174 -// subscribeKey.put("mediaServerId", sendRtpItem.getMediaServerId());  
175 -// subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,  
176 -// (MediaServerItem mediaServerItemInUse, JSONObject json)->{  
177 -// Map<String, Object> param = new HashMap<>();  
178 -// param.put("vhost","__defaultVhost__");  
179 -// param.put("app",json.getString("app"));  
180 -// param.put("stream",json.getString("stream"));  
181 -// param.put("ssrc", sendRtpItem.getSsrc());  
182 -// param.put("dst_url",sendRtpItem.getIp());  
183 -// param.put("dst_port", sendRtpItem.getPort());  
184 -// param.put("is_udp", is_Udp);  
185 -// param.put("src_port", sendRtpItem.getLocalPort());  
186 -// zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);  
187 -// });  
188 -// }else {  
189 -// Map<String, Object> param = new HashMap<>();  
190 -// param.put("vhost","__defaultVhost__");  
191 -// param.put("app",streamInfo.getApp());  
192 -// param.put("stream",streamInfo.getStream());  
193 -// param.put("ssrc", sendRtpItem.getSsrc());  
194 -// param.put("dst_url",sendRtpItem.getIp());  
195 -// param.put("dst_port", sendRtpItem.getPort());  
196 -// param.put("is_udp", is_Udp);  
197 -// param.put("src_port", sendRtpItem.getLocalPort());  
198 -//  
199 -// JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);  
200 -// if (jsonObject.getInteger("code") != 0) {  
201 -// logger.info("监听流以等待流上线2 {}/{}", streamInfo.getApp(), streamInfo.getStream());  
202 -// // 监听流上线  
203 -// // 添加订阅  
204 -// JSONObject subscribeKey = new JSONObject();  
205 -// subscribeKey.put("app", "rtp");  
206 -// subscribeKey.put("stream", streamInfo.getStream());  
207 -// subscribeKey.put("regist", true);  
208 -// subscribeKey.put("schema", "rtmp");  
209 -// subscribeKey.put("mediaServerId", sendRtpItem.getMediaServerId());  
210 -// subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,  
211 -// (MediaServerItem mediaServerItemInUse, JSONObject json)->{  
212 -// zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);  
213 -// });  
214 -// }  
215 -// } 182 + }
  183 + }
  184 + private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform,
  185 + JSONObject jsonObject, Map<String, Object> param, CallIdHeader callIdHeader) {
  186 + if (jsonObject == null) {
  187 + logger.error("RTP推流失败: 请检查ZLM服务");
  188 + } else if (jsonObject.getInteger("code") == 0) {
  189 + logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port"));
  190 + byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
  191 + sendRtpItem.setDialog(dialogByteArray);
  192 + byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
  193 + sendRtpItem.setTransaction(transactionByteArray);
  194 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  195 + } else {
  196 + logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"),JSONObject.toJSON(param));
  197 + if (sendRtpItem.isOnlyAudio()) {
  198 + // TODO 可能是语音对讲
  199 + }else {
  200 + // 向上级平台
  201 + commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId());
  202 + }
216 } 203 }
217 } 204 }
218 } 205 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
@@ -114,13 +114,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @@ -114,13 +114,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
114 playService.stopAudioBroadcast(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); 114 playService.stopAudioBroadcast(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
115 } 115 }
116 if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) { 116 if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
117 - MessageForPushChannel messageForPushChannel = new MessageForPushChannel();  
118 - messageForPushChannel.setType(0);  
119 - messageForPushChannel.setGbId(sendRtpItem.getChannelId());  
120 - messageForPushChannel.setApp(sendRtpItem.getApp());  
121 - messageForPushChannel.setStream(sendRtpItem.getStreamId());  
122 - messageForPushChannel.setMediaServerId(sendRtpItem.getMediaServerId());  
123 - messageForPushChannel.setPlatFormId(sendRtpItem.getPlatformId()); 117 + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
  118 + sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(),
  119 + sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId());
124 redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); 120 redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
125 } 121 }
126 } 122 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java
@@ -15,7 +15,7 @@ import javax.sip.RequestEvent; @@ -15,7 +15,7 @@ import javax.sip.RequestEvent;
15 @Component 15 @Component
16 public class CancelRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { 16 public class CancelRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
17 17
18 - private String method = "CANCEL"; 18 + private final String method = "CANCEL";
19 19
20 @Autowired 20 @Autowired
21 private SIPProcessorObserver sipProcessorObserver; 21 private SIPProcessorObserver sipProcessorObserver;
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -26,11 +26,14 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; @@ -26,11 +26,14 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
26 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; 26 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
27 import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; 27 import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
28 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 28 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  29 +import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
29 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItemLite; 30 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItemLite;
30 import com.genersoft.iot.vmp.service.IMediaServerService; 31 import com.genersoft.iot.vmp.service.IMediaServerService;
31 import com.genersoft.iot.vmp.service.IPlayService; 32 import com.genersoft.iot.vmp.service.IPlayService;
  33 +import com.genersoft.iot.vmp.service.IStreamPushService;
32 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; 34 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
33 import com.genersoft.iot.vmp.service.bean.SSRCInfo; 35 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
  36 +import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener;
34 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 37 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
35 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 38 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
36 import com.genersoft.iot.vmp.utils.DateUtil; 39 import com.genersoft.iot.vmp.utils.DateUtil;
@@ -66,33 +69,42 @@ import java.util.Vector; @@ -66,33 +69,42 @@ import java.util.Vector;
66 @Component 69 @Component
67 public class InviteRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { 70 public class InviteRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
68 71
69 - private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor.class); 72 + private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor.class);
70 73
71 - private String method = "INVITE"; 74 + private final String method = "INVITE";
72 75
73 - @Autowired  
74 - private SIPCommanderFroPlatform cmderFroPlatform; 76 + @Autowired
  77 + private SIPCommanderFroPlatform cmderFroPlatform;
75 78
76 - @Autowired  
77 - private IVideoManagerStorage storager; 79 + @Autowired
  80 + private IVideoManagerStorage storager;
78 81
79 - @Autowired  
80 - private IRedisCatchStorage redisCatchStorage; 82 + @Autowired
  83 + private IStreamPushService streamPushService;
81 84
82 - @Autowired  
83 - private DynamicTask dynamicTask; 85 + @Autowired
  86 + private IRedisCatchStorage redisCatchStorage;
84 87
85 - @Autowired  
86 - private SIPCommander cmder; 88 + @Autowired
  89 + private DynamicTask dynamicTask;
87 90
88 - @Autowired  
89 - private IPlayService playService; 91 + @Autowired
  92 + private SIPCommander cmder;
90 93
  94 + @Autowired
  95 + private IPlayService playService;
  96 +
  97 + @Autowired
  98 + private ISIPCommander commander;
  99 +
91 @Autowired 100 @Autowired
92 private AudioBroadcastManager audioBroadcastManager; 101 private AudioBroadcastManager audioBroadcastManager;
93 102
94 - @Autowired  
95 - private ZLMRTPServerFactory zlmrtpServerFactory; 103 + @Autowired
  104 + private ZLMRTPServerFactory zlmrtpServerFactory;
  105 +
  106 + @Autowired
  107 + private IMediaServerService mediaServerService;
96 108
97 @Autowired 109 @Autowired
98 private ZLMRESTfulUtils zlmresTfulUtils; 110 private ZLMRESTfulUtils zlmresTfulUtils;
@@ -100,17 +112,17 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -100,17 +112,17 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
100 @Autowired 112 @Autowired
101 private IMediaServerService mediaServerService; 113 private IMediaServerService mediaServerService;
102 114
103 - @Autowired  
104 - private SIPProcessorObserver sipProcessorObserver; 115 + @Autowired
  116 + private SIPProcessorObserver sipProcessorObserver;
105 117
106 - @Autowired  
107 - private VideoStreamSessionManager sessionManager; 118 + @Autowired
  119 + private VideoStreamSessionManager sessionManager;
108 120
109 - @Autowired  
110 - private UserSetting userSetting; 121 + @Autowired
  122 + private UserSetting userSetting;
111 123
112 - @Autowired  
113 - private ZLMMediaListManager mediaListManager; 124 + @Autowired
  125 + private ZLMMediaListManager mediaListManager;
114 126
115 @Autowired 127 @Autowired
116 private DeferredResultHolder resultHolder; 128 private DeferredResultHolder resultHolder;
@@ -123,542 +135,664 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -123,542 +135,664 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
123 135
124 136
125 137
126 - @Override  
127 - public void afterPropertiesSet() throws Exception {  
128 - // 添加消息处理的订阅  
129 - sipProcessorObserver.addRequestProcessor(method, this);  
130 - }  
131 -  
132 - /**  
133 - * 处理invite请求  
134 - *  
135 - * @param evt  
136 - * 请求消息  
137 - */  
138 - @Override  
139 - public void process(RequestEvent evt) {  
140 - // Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令  
141 - try {  
142 - Request request = evt.getRequest();  
143 - SipURI sipURI = (SipURI) request.getRequestURI();  
144 - //从subject读取channelId,不再从request-line读取。 有些平台request-line是平台国标编码,不是设备国标编码。  
145 - //String channelId = sipURI.getUser();  
146 - String channelId = SipUtils.getChannelIdFromHeader(request);  
147 - String requesterId = SipUtils.getUserIdFromFromHeader(request);  
148 - CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);  
149 - if (requesterId == null || channelId == null) {  
150 - logger.info("无法从FromHeader的Address中获取到平台id,返回400");  
151 - responseAck(evt, Response.BAD_REQUEST); // 参数不全, 发400,请求错误  
152 - return;  
153 - }  
154 -  
155 - // 查询请求是否来自上级平台\设备  
156 - ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);  
157 - if (platform == null) {  
158 - inviteFromDeviceHandle(evt, requesterId, channelId);  
159 - }else {  
160 - // 查询平台下是否有该通道  
161 - DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);  
162 - GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);  
163 - PlatformCatalog catalog = storager.getCatalog(channelId);  
164 - MediaServerItem mediaServerItem = null;  
165 - // 不是通道可能是直播流  
166 - if (channel != null && gbStream == null ) {  
167 - if (channel.getStatus() == 0) {  
168 - logger.info("通道离线,返回400");  
169 - responseAck(evt, Response.BAD_REQUEST, "channel [" + channel.getChannelId() + "] offline");  
170 - return;  
171 - }  
172 - responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中  
173 - }else if(channel == null && gbStream != null){  
174 - String mediaServerId = gbStream.getMediaServerId();  
175 - mediaServerItem = mediaServerService.getOne(mediaServerId);  
176 - if (mediaServerItem == null) {  
177 - logger.info("[ app={}, stream={} ]找不到zlm {},返回410",gbStream.getApp(), gbStream.getStream(), mediaServerId);  
178 - responseAck(evt, Response.GONE);  
179 - return;  
180 - }  
181 - responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中  
182 - }else if (catalog != null) {  
183 - responseAck(evt, Response.BAD_REQUEST, "catalog channel can not play"); // 目录不支持点播  
184 - return;  
185 - } else {  
186 - logger.info("通道不存在,返回404");  
187 - responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在  
188 - return;  
189 - }  
190 - // 解析sdp消息, 使用jainsip 自带的sdp解析方式  
191 - String contentString = new String(request.getRawContent());  
192 -  
193 - // jainSip不支持y=字段, 移除以解析。  
194 - int ssrcIndex = contentString.indexOf("y=");  
195 - // 检查是否有y字段  
196 - String ssrcDefault = "0000000000";  
197 - String ssrc;  
198 - SessionDescription sdp;  
199 - if (ssrcIndex >= 0) {  
200 - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段  
201 - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);  
202 - String substring = contentString.substring(0, contentString.indexOf("y="));  
203 - sdp = SdpFactory.getInstance().createSessionDescription(substring);  
204 - }else {  
205 - ssrc = ssrcDefault;  
206 - sdp = SdpFactory.getInstance().createSessionDescription(contentString);  
207 - }  
208 - String sessionName = sdp.getSessionName().getValue();  
209 -  
210 - Long startTime = null;  
211 - Long stopTime = null;  
212 - Instant start = null;  
213 - Instant end = null;  
214 - if (sdp.getTimeDescriptions(false) != null && sdp.getTimeDescriptions(false).size() > 0) {  
215 - TimeDescriptionImpl timeDescription = (TimeDescriptionImpl)(sdp.getTimeDescriptions(false).get(0));  
216 - TimeField startTimeFiled = (TimeField)timeDescription.getTime();  
217 - startTime = startTimeFiled.getStartTime();  
218 - stopTime = startTimeFiled.getStopTime();  
219 -  
220 - start = Instant.ofEpochMilli(startTime*1000);  
221 - end = Instant.ofEpochMilli(stopTime*1000);  
222 - }  
223 - // 获取支持的格式  
224 - Vector mediaDescriptions = sdp.getMediaDescriptions(true);  
225 - // 查看是否支持PS 负载96  
226 - //String ip = null;  
227 - int port = -1;  
228 - boolean mediaTransmissionTCP = false;  
229 - Boolean tcpActive = null;  
230 - for (Object description : mediaDescriptions) {  
231 - MediaDescription mediaDescription = (MediaDescription) description;  
232 - Media media = mediaDescription.getMedia();  
233 -  
234 - Vector mediaFormats = media.getMediaFormats(false);  
235 - if (mediaFormats.contains("96")) {  
236 - port = media.getMediaPort();  
237 - //String mediaType = media.getMediaType();  
238 - String protocol = media.getProtocol();  
239 -  
240 - // 区分TCP发流还是udp, 当前默认udp  
241 - if ("TCP/RTP/AVP".equals(protocol)) {  
242 - String setup = mediaDescription.getAttribute("setup");  
243 - if (setup != null) {  
244 - mediaTransmissionTCP = true;  
245 - if ("active".equals(setup)) {  
246 - tcpActive = true;  
247 - // 不支持tcp主动  
248 - responseAck(evt, Response.NOT_IMPLEMENTED, "tcp active not support"); // 目录不支持点播  
249 - return;  
250 - } else if ("passive".equals(setup)) {  
251 - tcpActive = false;  
252 - }  
253 - }  
254 - }  
255 - break;  
256 - }  
257 - }  
258 - if (port == -1) {  
259 - logger.info("不支持的媒体格式,返回415");  
260 - // 回复不支持的格式  
261 - responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415  
262 - return;  
263 - }  
264 - String username = sdp.getOrigin().getUsername();  
265 - String addressStr = sdp.getOrigin().getAddress();  
266 -  
267 - logger.info("[上级点播]用户:{}, 地址:{}:{}, ssrc:{}", username, addressStr, port, ssrc);  
268 - Device device = null;  
269 - // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标  
270 - if (channel != null) {  
271 - device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId);  
272 - if (device == null) {  
273 - logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel);  
274 - responseAck(evt, Response.SERVER_INTERNAL_ERROR);  
275 - return;  
276 - }  
277 - mediaServerItem = playService.getNewMediaServerItem(device);  
278 - if (mediaServerItem == null) {  
279 - logger.warn("未找到可用的zlm");  
280 - responseAck(evt, Response.BUSY_HERE);  
281 - return;  
282 - }  
283 - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,  
284 - device.getDeviceId(), channelId,  
285 - mediaTransmissionTCP);  
286 - if (tcpActive != null) {  
287 - sendRtpItem.setTcpActive(tcpActive);  
288 - }  
289 - if (sendRtpItem == null) {  
290 - logger.warn("服务器端口资源不足");  
291 - responseAck(evt, Response.BUSY_HERE);  
292 - return;  
293 - }  
294 - sendRtpItem.setCallId(callIdHeader.getCallId());  
295 - sendRtpItem.setPlayType("Play".equals(sessionName)?InviteStreamType.PLAY:InviteStreamType.PLAYBACK);  
296 - byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());  
297 - sendRtpItem.setDialog(dialogByteArray);  
298 - byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());  
299 - sendRtpItem.setTransaction(transactionByteArray);  
300 - Long finalStartTime = startTime;  
301 - Long finalStopTime = stopTime;  
302 - ZLMHttpHookSubscribe.Event hookEvent = (mediaServerItemInUSe, responseJSON)->{  
303 - String app = responseJSON.getString("app");  
304 - String stream = responseJSON.getString("stream");  
305 - logger.info("[上级点播]下级已经开始推流。 回复200OK(SDP), {}/{}", app, stream);  
306 - // * 0 等待设备推流上来  
307 - // * 1 下级已经推流,等待上级平台回复ack  
308 - // * 2 推流中  
309 - sendRtpItem.setStatus(1);  
310 - redisCatchStorage.updateSendRTPSever(sendRtpItem);  
311 -  
312 - StringBuffer content = new StringBuffer(200);  
313 - content.append("v=0\r\n");  
314 - content.append("o="+ channelId +" 0 0 IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");  
315 - content.append("s=" + sessionName+"\r\n");  
316 - content.append("c=IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");  
317 - if ("Playback".equals(sessionName)) {  
318 - content.append("t=" + finalStartTime + " " + finalStopTime + "\r\n");  
319 - }else {  
320 - content.append("t=0 0\r\n");  
321 - }  
322 - content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");  
323 - content.append("a=sendonly\r\n");  
324 - content.append("a=rtpmap:96 PS/90000\r\n");  
325 - content.append("y="+ ssrc + "\r\n");  
326 - content.append("f=\r\n");  
327 -  
328 - try {  
329 - // 超时未收到Ack应该回复bye,当前等待时间为10秒  
330 - dynamicTask.startDelay(callIdHeader.getCallId(), ()->{  
331 - logger.info("Ack 等待超时");  
332 - mediaServerService.releaseSsrc(mediaServerItemInUSe.getId(), ssrc);  
333 - // 回复bye  
334 - cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId());  
335 - }, 60*1000);  
336 - responseSdpAck(evt, content.toString(), platform);  
337 -  
338 - } catch (SipException e) {  
339 - e.printStackTrace();  
340 - } catch (InvalidArgumentException e) {  
341 - e.printStackTrace();  
342 - } catch (ParseException e) {  
343 - e.printStackTrace();  
344 - }  
345 - };  
346 - SipSubscribe.Event errorEvent = ((event) -> {  
347 - // 未知错误。直接转发设备点播的错误  
348 - Response response = null;  
349 - try {  
350 - response = getMessageFactory().createResponse(event.statusCode, evt.getRequest());  
351 - ServerTransaction serverTransaction = getServerTransaction(evt);  
352 - serverTransaction.sendResponse(response);  
353 - if (serverTransaction.getDialog() != null) {  
354 - serverTransaction.getDialog().delete();  
355 - }  
356 - } catch (ParseException | SipException | InvalidArgumentException e) {  
357 - e.printStackTrace();  
358 - }  
359 - });  
360 - sendRtpItem.setApp("rtp");  
361 - if ("Playback".equals(sessionName)) {  
362 - sendRtpItem.setPlayType(InviteStreamType.PLAYBACK);  
363 - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, true, true);  
364 - sendRtpItem.setStreamId(ssrcInfo.getStream());  
365 - // 写入redis, 超时时回复  
366 - redisCatchStorage.updateSendRTPSever(sendRtpItem);  
367 - playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start),  
368 - DateUtil.formatter.format(end), null, result -> {  
369 - if (result.getCode() != 0){  
370 - logger.warn("录像回放失败");  
371 - if (result.getEvent() != null) {  
372 - errorEvent.response(result.getEvent());  
373 - }  
374 - redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);  
375 - try {  
376 - responseAck(evt, Response.REQUEST_TIMEOUT);  
377 - } catch (SipException e) {  
378 - e.printStackTrace();  
379 - } catch (InvalidArgumentException e) {  
380 - e.printStackTrace();  
381 - } catch (ParseException e) {  
382 - e.printStackTrace();  
383 - }  
384 - }else {  
385 - if (result.getMediaServerItem() != null) {  
386 - hookEvent.response(result.getMediaServerItem(), result.getResponse());  
387 - }  
388 - }  
389 - });  
390 - }else {  
391 - sendRtpItem.setPlayType(InviteStreamType.PLAY);  
392 - SsrcTransaction playTransaction = sessionManager.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);  
393 - if (playTransaction != null) {  
394 - Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, "rtp", playTransaction.getStream());  
395 - if (!streamReady) {  
396 - playTransaction = null;  
397 - }  
398 - }  
399 - if (playTransaction == null) {  
400 - String streamId = null;  
401 - if (mediaServerItem.isRtpEnable()) {  
402 - streamId = String.format("%s_%s", device.getDeviceId(), channelId);  
403 - }  
404 - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, true, false);  
405 - sendRtpItem.setStreamId(ssrcInfo.getStream());  
406 - // 写入redis, 超时时回复  
407 - redisCatchStorage.updateSendRTPSever(sendRtpItem);  
408 - playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg)->{  
409 - redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);  
410 - }, null);  
411 - }else {  
412 - sendRtpItem.setStreamId(playTransaction.getStream());  
413 - // 写入redis, 超时时回复  
414 - redisCatchStorage.updateSendRTPSever(sendRtpItem);  
415 - JSONObject jsonObject = new JSONObject();  
416 - jsonObject.put("app", sendRtpItem.getApp());  
417 - jsonObject.put("stream", sendRtpItem.getStreamId());  
418 - hookEvent.response(mediaServerItem, jsonObject);  
419 - }  
420 - }  
421 - }else if (gbStream != null) {  
422 -  
423 - Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());  
424 - if (!streamReady ) {  
425 - if ("proxy".equals(gbStream.getStreamType())) {  
426 - // TODO 控制启用以使设备上线  
427 - logger.info("[ app={}, stream={} ]通道离线,启用流后开始推流",gbStream.getApp(), gbStream.getStream());  
428 - responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");  
429 - }else if ("push".equals(gbStream.getStreamType())) {  
430 - if (!platform.isStartOfflinePush()) {  
431 - responseAck(evt, Response.TEMPORARILY_UNAVAILABLE, "channel unavailable");  
432 - return;  
433 - }  
434 - // 发送redis消息以使设备上线  
435 - logger.info("[ app={}, stream={} ]通道离线,发送redis信息控制设备开始推流",gbStream.getApp(), gbStream.getStream());  
436 - MessageForPushChannel messageForPushChannel = new MessageForPushChannel();  
437 - messageForPushChannel.setType(1);  
438 - messageForPushChannel.setGbId(gbStream.getGbId());  
439 - messageForPushChannel.setApp(gbStream.getApp());  
440 - messageForPushChannel.setStream(gbStream.getStream());  
441 - // TODO 获取低负载的节点  
442 - messageForPushChannel.setMediaServerId(gbStream.getMediaServerId());  
443 - messageForPushChannel.setPlatFormId(platform.getServerGBId());  
444 - messageForPushChannel.setPlatFormName(platform.getName());  
445 - redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);  
446 - // 设置超时  
447 - dynamicTask.startDelay(callIdHeader.getCallId(), ()->{  
448 - logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream());  
449 - try {  
450 - mediaListManager.removedChannelOnlineEventLister(gbStream.getGbId());  
451 - responseAck(evt, Response.REQUEST_TIMEOUT); // 超时  
452 - } catch (SipException e) {  
453 - e.printStackTrace();  
454 - } catch (InvalidArgumentException e) {  
455 - e.printStackTrace();  
456 - } catch (ParseException e) {  
457 - e.printStackTrace();  
458 - }  
459 - }, userSetting.getPlatformPlayTimeout());  
460 - // 添加监听  
461 - MediaServerItem finalMediaServerItem = mediaServerItem;  
462 - int finalPort = port;  
463 - boolean finalMediaTransmissionTCP = mediaTransmissionTCP;  
464 - Boolean finalTcpActive = tcpActive;  
465 - mediaListManager.addChannelOnlineEventLister(gbStream.getGbId(), (app, stream)->{  
466 - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(finalMediaServerItem, addressStr, finalPort, ssrc, requesterId,  
467 - app, stream, channelId, finalMediaTransmissionTCP);  
468 -  
469 - if (sendRtpItem == null) {  
470 - logger.warn("服务器端口资源不足");  
471 - try {  
472 - responseAck(evt, Response.BUSY_HERE);  
473 - } catch (SipException e) {  
474 - e.printStackTrace();  
475 - } catch (InvalidArgumentException e) {  
476 - e.printStackTrace();  
477 - } catch (ParseException e) {  
478 - e.printStackTrace();  
479 - }  
480 - return;  
481 - }  
482 - if (finalTcpActive != null) {  
483 - sendRtpItem.setTcpActive(finalTcpActive);  
484 - }  
485 - sendRtpItem.setPlayType(InviteStreamType.PUSH);  
486 - // 写入redis, 超时时回复  
487 - sendRtpItem.setStatus(1);  
488 - sendRtpItem.setCallId(callIdHeader.getCallId());  
489 - byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());  
490 - sendRtpItem.setDialog(dialogByteArray);  
491 - byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());  
492 - sendRtpItem.setTransaction(transactionByteArray);  
493 - redisCatchStorage.updateSendRTPSever(sendRtpItem);  
494 - sendStreamAck(finalMediaServerItem, sendRtpItem, platform, evt);  
495 -  
496 - });  
497 - }  
498 - }else {  
499 - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,  
500 - gbStream.getApp(), gbStream.getStream(), channelId,  
501 - mediaTransmissionTCP);  
502 -  
503 - if (sendRtpItem == null) {  
504 - logger.warn("服务器端口资源不足");  
505 - responseAck(evt, Response.BUSY_HERE);  
506 - return;  
507 - }  
508 - if (tcpActive != null) {  
509 - sendRtpItem.setTcpActive(tcpActive);  
510 - }  
511 - sendRtpItem.setPlayType(InviteStreamType.PUSH);  
512 - // 写入redis, 超时时回复  
513 - sendRtpItem.setStatus(1);  
514 - sendRtpItem.setCallId(callIdHeader.getCallId());  
515 - byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());  
516 - sendRtpItem.setDialog(dialogByteArray);  
517 - byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());  
518 - sendRtpItem.setTransaction(transactionByteArray);  
519 - redisCatchStorage.updateSendRTPSever(sendRtpItem);  
520 - sendStreamAck(mediaServerItem, sendRtpItem, platform, evt);  
521 - }  
522 -  
523 -  
524 - }  
525 -  
526 - }  
527 -  
528 - } catch (SipException | InvalidArgumentException | ParseException e) {  
529 - e.printStackTrace();  
530 - logger.warn("sdp解析错误");  
531 - e.printStackTrace();  
532 - } catch (SdpParseException e) {  
533 - e.printStackTrace();  
534 - } catch (SdpException e) {  
535 - e.printStackTrace();  
536 - }  
537 - }  
538 -  
539 - public void sendStreamAck(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt){  
540 -  
541 - StringBuffer content = new StringBuffer(200);  
542 - content.append("v=0\r\n");  
543 - content.append("o="+ sendRtpItem.getChannelId() +" 0 0 IN IP4 "+ mediaServerItem.getSdpIp()+"\r\n");  
544 - content.append("s=Play\r\n");  
545 - content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");  
546 - content.append("t=0 0\r\n");  
547 - content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");  
548 - content.append("a=sendonly\r\n");  
549 - content.append("a=rtpmap:96 PS/90000\r\n");  
550 - if (sendRtpItem.isTcp()) {  
551 - content.append("a=connection:new\r\n");  
552 - if (!sendRtpItem.isTcpActive()) {  
553 - content.append("a=setup:active\r\n");  
554 - }else {  
555 - content.append("a=setup:passive\r\n");  
556 - }  
557 - }  
558 - content.append("y="+ sendRtpItem.getSsrc() + "\r\n");  
559 - content.append("f=\r\n");  
560 -  
561 - try {  
562 - responseSdpAck(evt, content.toString(), platform);  
563 - } catch (SipException e) {  
564 - e.printStackTrace();  
565 - } catch (InvalidArgumentException e) {  
566 - e.printStackTrace();  
567 - } catch (ParseException e) {  
568 - e.printStackTrace();  
569 - }  
570 - }  
571 -  
572 - public void inviteFromDeviceHandle(RequestEvent evt, String requesterId, String channelId1) throws InvalidArgumentException, ParseException, SipException, SdpException {  
573 -  
574 - // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备)  
575 - Device device = redisCatchStorage.getDevice(requesterId);  
576 - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(requesterId, channelId1);  
577 - if (audioBroadcastCatch == null) {  
578 - logger.warn("来自设备的Invite请求非语音广播,已忽略");  
579 - responseAck(evt, Response.FORBIDDEN);  
580 - return;  
581 - }  
582 - Request request = evt.getRequest();  
583 - if (device != null) {  
584 - logger.info("收到设备" + requesterId + "的语音广播Invite请求");  
585 - responseAck(evt, Response.TRYING);  
586 -  
587 - String contentString = new String(request.getRawContent());  
588 - // jainSip不支持y=字段, 移除移除以解析。  
589 - String substring = contentString;  
590 - String ssrc = "0000000404";  
591 - int ssrcIndex = contentString.indexOf("y=");  
592 - if (ssrcIndex > 0) {  
593 - substring = contentString.substring(0, ssrcIndex);  
594 - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12).trim();  
595 - }  
596 - ssrcIndex = substring.indexOf("f=");  
597 - if (ssrcIndex > 0) {  
598 - substring = contentString.substring(0, ssrcIndex);  
599 - }  
600 - SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);  
601 -  
602 - // 获取支持的格式  
603 - Vector mediaDescriptions = sdp.getMediaDescriptions(true);  
604 -  
605 - // 查看是否支持PS 负载96  
606 - int port = -1;  
607 - boolean mediaTransmissionTCP = false;  
608 - Boolean tcpActive = null;  
609 - for (int i = 0; i < mediaDescriptions.size(); i++) {  
610 - MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i);  
611 - Media media = mediaDescription.getMedia();  
612 -  
613 - Vector mediaFormats = media.getMediaFormats(false);  
614 - if (mediaFormats.contains("8")) {  
615 - port = media.getMediaPort();  
616 - String protocol = media.getProtocol();  
617 - // 区分TCP发流还是udp, 当前默认udp  
618 - if ("TCP/RTP/AVP".equals(protocol)) {  
619 - String setup = mediaDescription.getAttribute("setup");  
620 - if (setup != null) {  
621 - mediaTransmissionTCP = true;  
622 - if ("active".equals(setup)) {  
623 - tcpActive = true;  
624 - } else if ("passive".equals(setup)) {  
625 - tcpActive = false;  
626 - }  
627 - }  
628 - }  
629 - break;  
630 - }  
631 - }  
632 - if (port == -1) {  
633 - logger.info("不支持的媒体格式,返回415");  
634 - // 回复不支持的格式  
635 - responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415  
636 - return;  
637 - }  
638 - String addressStr = sdp.getOrigin().getAddress();  
639 - logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", requesterId, addressStr, port, ssrc);  
640 -  
641 - MediaServerItem mediaServerItem = playService.getNewMediaServerItem(device);  
642 - if (mediaServerItem == null) {  
643 - logger.warn("未找到可用的zlm");  
644 - responseAck(evt, Response.BUSY_HERE);  
645 - return;  
646 - }  
647 - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,  
648 - device.getDeviceId(), audioBroadcastCatch.getChannelId(),  
649 - mediaTransmissionTCP);  
650 - if (sendRtpItem == null) {  
651 - logger.warn("服务器端口资源不足");  
652 - responseAck(evt, Response.BUSY_HERE);  
653 - return;  
654 - }  
655 - sendRtpItem.setTcp(mediaTransmissionTCP);  
656 - if (tcpActive != null) {  
657 - sendRtpItem.setTcpActive(tcpActive);  
658 - }  
659 - String app = "broadcast";  
660 - String stream = device.getDeviceId() + "_" + audioBroadcastCatch.getChannelId();  
661 - 138 + @Autowired
  139 + private RedisGbPlayMsgListener redisGbPlayMsgListener;
  140 +
  141 +
  142 + @Override
  143 + public void afterPropertiesSet() throws Exception {
  144 + // 添加消息处理的订阅
  145 + sipProcessorObserver.addRequestProcessor(method, this);
  146 + }
  147 +
  148 + /**
  149 + * 处理invite请求
  150 + *
  151 + * @param evt 请求消息
  152 + */
  153 + @Override
  154 + public void process(RequestEvent evt) {
  155 + // Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
  156 + try {
  157 + Request request = evt.getRequest();
  158 + SipURI sipUri = (SipURI) request.getRequestURI();
  159 + //从subject读取channelId,不再从request-line读取。 有些平台request-line是平台国标编码,不是设备国标编码。
  160 + //String channelId = sipURI.getUser();
  161 + String channelId = SipUtils.getChannelIdFromHeader(request);
  162 + String requesterId = SipUtils.getUserIdFromFromHeader(request);
  163 + CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
  164 + if (requesterId == null || channelId == null) {
  165 + logger.info("无法从FromHeader的Address中获取到平台id,返回400");
  166 + // 参数不全, 发400,请求错误
  167 + responseAck(evt, Response.BAD_REQUEST);
  168 + return;
  169 + }
  170 +
  171 + // 查询请求是否来自上级平台\设备
  172 + ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
  173 + if (platform == null) {
  174 + inviteFromDeviceHandle(evt, requesterId);
  175 + } else {
  176 + // 查询平台下是否有该通道
  177 + DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
  178 + GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
  179 + PlatformCatalog catalog = storager.getCatalog(channelId);
  180 +
  181 + MediaServerItem mediaServerItem = null;
  182 + StreamPushItem streamPushItem = null;
  183 + // 不是通道可能是直播流
  184 + if (channel != null && gbStream == null) {
  185 + if (channel.getStatus() == 0) {
  186 + logger.info("通道离线,返回400");
  187 + responseAck(evt, Response.BAD_REQUEST, "channel [" + channel.getChannelId() + "] offline");
  188 + return;
  189 + }
  190 + responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
  191 + } else if (channel == null && gbStream != null) {
  192 +
  193 + String mediaServerId = gbStream.getMediaServerId();
  194 + mediaServerItem = mediaServerService.getOne(mediaServerId);
  195 + if (mediaServerItem == null) {
  196 + if ("proxy".equals(gbStream.getStreamType())) {
  197 + logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
  198 + responseAck(evt, Response.GONE);
  199 + return;
  200 + } else {
  201 + streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream());
  202 + if (streamPushItem == null || streamPushItem.getServerId().equals(userSetting.getServerId())) {
  203 + logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
  204 + responseAck(evt, Response.GONE);
  205 + return;
  206 + }
  207 + }
  208 + } else {
  209 + if ("push".equals(gbStream.getStreamType())) {
  210 + streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream());
  211 + if (streamPushItem == null) {
  212 + logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
  213 + responseAck(evt, Response.GONE);
  214 + return;
  215 + }
  216 + }
  217 + }
  218 + responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
  219 + } else if (catalog != null) {
  220 + responseAck(evt, Response.BAD_REQUEST, "catalog channel can not play"); // 目录不支持点播
  221 + return;
  222 + } else {
  223 + logger.info("通道不存在,返回404");
  224 + responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在
  225 + return;
  226 + }
  227 + // 解析sdp消息, 使用jainsip 自带的sdp解析方式
  228 + String contentString = new String(request.getRawContent());
  229 +
  230 + // jainSip不支持y=字段, 移除以解析。
  231 + int ssrcIndex = contentString.indexOf("y=");
  232 + // 检查是否有y字段
  233 + String ssrcDefault = "0000000000";
  234 + String ssrc;
  235 + SessionDescription sdp;
  236 + if (ssrcIndex >= 0) {
  237 + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段
  238 + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  239 + String substring = contentString.substring(0, contentString.indexOf("y="));
  240 + sdp = SdpFactory.getInstance().createSessionDescription(substring);
  241 + } else {
  242 + ssrc = ssrcDefault;
  243 + sdp = SdpFactory.getInstance().createSessionDescription(contentString);
  244 + }
  245 + String sessionName = sdp.getSessionName().getValue();
  246 +
  247 + Long startTime = null;
  248 + Long stopTime = null;
  249 + Instant start = null;
  250 + Instant end = null;
  251 + if (sdp.getTimeDescriptions(false) != null && sdp.getTimeDescriptions(false).size() > 0) {
  252 + TimeDescriptionImpl timeDescription = (TimeDescriptionImpl) (sdp.getTimeDescriptions(false).get(0));
  253 + TimeField startTimeFiled = (TimeField) timeDescription.getTime();
  254 + startTime = startTimeFiled.getStartTime();
  255 + stopTime = startTimeFiled.getStopTime();
  256 +
  257 + start = Instant.ofEpochSecond(startTime);
  258 + end = Instant.ofEpochSecond(stopTime);
  259 + }
  260 + // 获取支持的格式
  261 + Vector mediaDescriptions = sdp.getMediaDescriptions(true);
  262 + // 查看是否支持PS 负载96
  263 + //String ip = null;
  264 + int port = -1;
  265 + boolean mediaTransmissionTCP = false;
  266 + Boolean tcpActive = null;
  267 + for (Object description : mediaDescriptions) {
  268 + MediaDescription mediaDescription = (MediaDescription) description;
  269 + Media media = mediaDescription.getMedia();
  270 +
  271 + Vector mediaFormats = media.getMediaFormats(false);
  272 + if (mediaFormats.contains("96")) {
  273 + port = media.getMediaPort();
  274 + //String mediaType = media.getMediaType();
  275 + String protocol = media.getProtocol();
  276 +
  277 + // 区分TCP发流还是udp, 当前默认udp
  278 + if ("TCP/RTP/AVP".equals(protocol)) {
  279 + String setup = mediaDescription.getAttribute("setup");
  280 + if (setup != null) {
  281 + mediaTransmissionTCP = true;
  282 + if ("active".equals(setup)) {
  283 + tcpActive = true;
  284 + // 不支持tcp主动
  285 + responseAck(evt, Response.NOT_IMPLEMENTED, "tcp active not support"); // 目录不支持点播
  286 + return;
  287 + } else if ("passive".equals(setup)) {
  288 + tcpActive = false;
  289 + }
  290 + }
  291 + }
  292 + break;
  293 + }
  294 + }
  295 + if (port == -1) {
  296 + logger.info("不支持的媒体格式,返回415");
  297 + // 回复不支持的格式
  298 + responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
  299 + return;
  300 + }
  301 + String username = sdp.getOrigin().getUsername();
  302 + String addressStr = sdp.getOrigin().getAddress();
  303 +
  304 + logger.info("[上级点播]用户:{}, 通道:{}, 地址:{}:{}, ssrc:{}", username, channelId, addressStr, port, ssrc);
  305 + Device device = null;
  306 + // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标
  307 + if (channel != null) {
  308 + device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId);
  309 + if (device == null) {
  310 + logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel);
  311 + responseAck(evt, Response.SERVER_INTERNAL_ERROR);
  312 + return;
  313 + }
  314 + mediaServerItem = playService.getNewMediaServerItem(device);
  315 + if (mediaServerItem == null) {
  316 + logger.warn("未找到可用的zlm");
  317 + responseAck(evt, Response.BUSY_HERE);
  318 + return;
  319 + }
  320 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
  321 + device.getDeviceId(), channelId,
  322 + mediaTransmissionTCP);
  323 + if (tcpActive != null) {
  324 + sendRtpItem.setTcpActive(tcpActive);
  325 + }
  326 + if (sendRtpItem == null) {
  327 + logger.warn("服务器端口资源不足");
  328 + responseAck(evt, Response.BUSY_HERE);
  329 + return;
  330 + }
  331 + sendRtpItem.setCallId(callIdHeader.getCallId());
  332 + sendRtpItem.setPlayType("Play".equals(sessionName) ? InviteStreamType.PLAY : InviteStreamType.PLAYBACK);
  333 +
  334 + Long finalStartTime = startTime;
  335 + Long finalStopTime = stopTime;
  336 + ZLMHttpHookSubscribe.Event hookEvent = (mediaServerItemInUSe, responseJSON) -> {
  337 + String app = responseJSON.getString("app");
  338 + String stream = responseJSON.getString("stream");
  339 + logger.info("[上级点播]下级已经开始推流。 回复200OK(SDP), {}/{}", app, stream);
  340 + // * 0 等待设备推流上来
  341 + // * 1 下级已经推流,等待上级平台回复ack
  342 + // * 2 推流中
  343 + sendRtpItem.setStatus(1);
  344 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  345 +
  346 + StringBuffer content = new StringBuffer(200);
  347 + content.append("v=0\r\n");
  348 + content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItemInUSe.getSdpIp() + "\r\n");
  349 + content.append("s=" + sessionName + "\r\n");
  350 + content.append("c=IN IP4 " + mediaServerItemInUSe.getSdpIp() + "\r\n");
  351 + if ("Playback".equals(sessionName)) {
  352 + content.append("t=" + finalStartTime + " " + finalStopTime + "\r\n");
  353 + } else {
  354 + content.append("t=0 0\r\n");
  355 + }
  356 + content.append("m=video " + sendRtpItem.getLocalPort() + " RTP/AVP 96\r\n");
  357 + content.append("a=sendonly\r\n");
  358 + content.append("a=rtpmap:96 PS/90000\r\n");
  359 + content.append("y=" + ssrc + "\r\n");
  360 + content.append("f=\r\n");
  361 +
  362 + try {
  363 + // 超时未收到Ack应该回复bye,当前等待时间为10秒
  364 + dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
  365 + logger.info("Ack 等待超时");
  366 + mediaServerService.releaseSsrc(mediaServerItemInUSe.getId(), ssrc);
  367 + // 回复bye
  368 + cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId());
  369 + }, 60 * 1000);
  370 + responseSdpAck(evt, content.toString(), platform);
  371 +
  372 + } catch (SipException e) {
  373 + e.printStackTrace();
  374 + } catch (InvalidArgumentException e) {
  375 + e.printStackTrace();
  376 + } catch (ParseException e) {
  377 + e.printStackTrace();
  378 + }
  379 + };
  380 + SipSubscribe.Event errorEvent = ((event) -> {
  381 + // 未知错误。直接转发设备点播的错误
  382 + Response response = null;
  383 + try {
  384 + response = getMessageFactory().createResponse(event.statusCode, evt.getRequest());
  385 + ServerTransaction serverTransaction = getServerTransaction(evt);
  386 + serverTransaction.sendResponse(response);
  387 + if (serverTransaction.getDialog() != null) {
  388 + serverTransaction.getDialog().delete();
  389 + }
  390 + } catch (ParseException | SipException | InvalidArgumentException e) {
  391 + e.printStackTrace();
  392 + }
  393 + });
  394 + sendRtpItem.setApp("rtp");
  395 + if ("Playback".equals(sessionName)) {
  396 + sendRtpItem.setPlayType(InviteStreamType.PLAYBACK);
  397 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, true, true);
  398 + sendRtpItem.setStreamId(ssrcInfo.getStream());
  399 + // 写入redis, 超时时回复
  400 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  401 + playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start),
  402 + DateUtil.formatter.format(end), null, result -> {
  403 + if (result.getCode() != 0) {
  404 + logger.warn("录像回放失败");
  405 + if (result.getEvent() != null) {
  406 + errorEvent.response(result.getEvent());
  407 + }
  408 + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
  409 + try {
  410 + responseAck(evt, Response.REQUEST_TIMEOUT);
  411 + } catch (SipException e) {
  412 + e.printStackTrace();
  413 + } catch (InvalidArgumentException e) {
  414 + e.printStackTrace();
  415 + } catch (ParseException e) {
  416 + e.printStackTrace();
  417 + }
  418 + } else {
  419 + if (result.getMediaServerItem() != null) {
  420 + hookEvent.response(result.getMediaServerItem(), result.getResponse());
  421 + }
  422 + }
  423 + });
  424 + } else {
  425 + sendRtpItem.setPlayType(InviteStreamType.PLAY);
  426 + SsrcTransaction playTransaction = sessionManager.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);
  427 + if (playTransaction != null) {
  428 + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, "rtp", playTransaction.getStream());
  429 + if (!streamReady) {
  430 + playTransaction = null;
  431 + }
  432 + }
  433 + if (playTransaction == null) {
  434 + String streamId = null;
  435 + if (mediaServerItem.isRtpEnable()) {
  436 + streamId = String.format("%s_%s", device.getDeviceId(), channelId);
  437 + }
  438 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false);
  439 + sendRtpItem.setStreamId(ssrcInfo.getStream());
  440 + // 写入redis, 超时时回复
  441 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  442 + playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> {
  443 + logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId);
  444 + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
  445 + }, null);
  446 + } else {
  447 + sendRtpItem.setStreamId(playTransaction.getStream());
  448 + // 写入redis, 超时时回复
  449 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  450 + JSONObject jsonObject = new JSONObject();
  451 + jsonObject.put("app", sendRtpItem.getApp());
  452 + jsonObject.put("stream", sendRtpItem.getStreamId());
  453 + hookEvent.response(mediaServerItem, jsonObject);
  454 + }
  455 + }
  456 + } else if (gbStream != null) {
  457 + if (streamPushItem.isStatus()) {
  458 + // 在线状态
  459 + pushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  460 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  461 + } else {
  462 + // 不在线 拉起
  463 + notifyStreamOnline(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  464 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  465 + }
  466 +
  467 + }
  468 +
  469 + }
  470 +
  471 + } catch (SipException | InvalidArgumentException | ParseException e) {
  472 + e.printStackTrace();
  473 + logger.warn("sdp解析错误");
  474 + e.printStackTrace();
  475 + } catch (SdpParseException e) {
  476 + e.printStackTrace();
  477 + } catch (SdpException e) {
  478 + e.printStackTrace();
  479 + }
  480 + }
  481 +
  482 + /**
  483 + * 安排推流
  484 + */
  485 +
  486 + private void pushStream(RequestEvent evt, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
  487 + CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
  488 + int port, Boolean tcpActive, boolean mediaTransmissionTCP,
  489 + String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException {
  490 + // 推流
  491 + if (streamPushItem.getServerId().equals(userSetting.getServerId())) {
  492 + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
  493 + if (streamReady) {
  494 + // 自平台内容
  495 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
  496 + gbStream.getApp(), gbStream.getStream(), channelId,
  497 + mediaTransmissionTCP);
  498 +
  499 + if (sendRtpItem == null) {
  500 + logger.warn("服务器端口资源不足");
  501 + responseAck(evt, Response.BUSY_HERE);
  502 + return;
  503 + }
  504 + if (tcpActive != null) {
  505 + sendRtpItem.setTcpActive(tcpActive);
  506 + }
  507 + sendRtpItem.setPlayType(InviteStreamType.PUSH);
  508 + // 写入redis, 超时时回复
  509 + sendRtpItem.setStatus(1);
  510 + sendRtpItem.setCallId(callIdHeader.getCallId());
  511 + byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
  512 + sendRtpItem.setDialog(dialogByteArray);
  513 + byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
  514 + sendRtpItem.setTransaction(transactionByteArray);
  515 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  516 + sendStreamAck(mediaServerItem, sendRtpItem, platform, evt);
  517 + } else {
  518 + // 不在线 拉起
  519 + notifyStreamOnline(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  520 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  521 + }
  522 +
  523 + } else {
  524 + // 其他平台内容
  525 + otherWvpPushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  526 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  527 + }
  528 +
  529 + }
  530 +
  531 + /**
  532 + * 通知流上线
  533 + */
  534 + private void notifyStreamOnline(RequestEvent evt, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
  535 + CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
  536 + int port, Boolean tcpActive, boolean mediaTransmissionTCP,
  537 + String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException {
  538 + if ("proxy".equals(gbStream.getStreamType())) {
  539 + // TODO 控制启用以使设备上线
  540 + logger.info("[ app={}, stream={} ]通道离线,启用流后开始推流", gbStream.getApp(), gbStream.getStream());
  541 + responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
  542 + } else if ("push".equals(gbStream.getStreamType())) {
  543 + if (!platform.isStartOfflinePush()) {
  544 + responseAck(evt, Response.TEMPORARILY_UNAVAILABLE, "channel unavailable");
  545 + return;
  546 + }
  547 + // 发送redis消息以使设备上线
  548 + logger.info("[ app={}, stream={} ]通道离线,发送redis信息控制设备开始推流", gbStream.getApp(), gbStream.getStream());
  549 +
  550 + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1,
  551 + gbStream.getApp(), gbStream.getStream(), gbStream.getGbId(), gbStream.getPlatformId(),
  552 + platform.getName(), null, gbStream.getMediaServerId());
  553 + redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
  554 + // 设置超时
  555 + dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
  556 + logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream());
  557 + try {
  558 + mediaListManager.removedChannelOnlineEventLister(gbStream.getGbId());
  559 + responseAck(evt, Response.REQUEST_TIMEOUT); // 超时
  560 + } catch (SipException e) {
  561 + e.printStackTrace();
  562 + } catch (InvalidArgumentException e) {
  563 + e.printStackTrace();
  564 + } catch (ParseException e) {
  565 + e.printStackTrace();
  566 + }
  567 + }, userSetting.getPlatformPlayTimeout());
  568 + // 添加监听
  569 + int finalPort = port;
  570 + Boolean finalTcpActive = tcpActive;
  571 +
  572 + // 添加在本机上线的通知
  573 + mediaListManager.addChannelOnlineEventLister(gbStream.getGbId(), (app, stream, serverId) -> {
  574 + dynamicTask.stop(callIdHeader.getCallId());
  575 + if (serverId.equals(userSetting.getServerId())) {
  576 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
  577 + app, stream, channelId, mediaTransmissionTCP);
  578 +
  579 + if (sendRtpItem == null) {
  580 + logger.warn("服务器端口资源不足");
  581 + try {
  582 + responseAck(evt, Response.BUSY_HERE);
  583 + } catch (SipException e) {
  584 + e.printStackTrace();
  585 + } catch (InvalidArgumentException e) {
  586 + e.printStackTrace();
  587 + } catch (ParseException e) {
  588 + e.printStackTrace();
  589 + }
  590 + return;
  591 + }
  592 + if (finalTcpActive != null) {
  593 + sendRtpItem.setTcpActive(finalTcpActive);
  594 + }
  595 + sendRtpItem.setPlayType(InviteStreamType.PUSH);
  596 + // 写入redis, 超时时回复
  597 + sendRtpItem.setStatus(1);
  598 + sendRtpItem.setCallId(callIdHeader.getCallId());
  599 + byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
  600 + sendRtpItem.setDialog(dialogByteArray);
  601 + byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
  602 + sendRtpItem.setTransaction(transactionByteArray);
  603 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  604 + sendStreamAck(mediaServerItem, sendRtpItem, platform, evt);
  605 + } else {
  606 + // 其他平台内容
  607 + otherWvpPushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  608 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  609 + }
  610 + });
  611 + }
  612 + }
  613 +
  614 + /**
  615 + * 来自其他wvp的推流
  616 + */
  617 + private void otherWvpPushStream(RequestEvent evt, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
  618 + CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
  619 + int port, Boolean tcpActive, boolean mediaTransmissionTCP,
  620 + String channelId, String addressStr, String ssrc, String requesterId) {
  621 + logger.info("[级联点播]直播流来自其他平台,发送redis消息");
  622 + // 发送redis消息
  623 + redisGbPlayMsgListener.sendMsg(streamPushItem.getServerId(), streamPushItem.getMediaServerId(),
  624 + streamPushItem.getApp(), streamPushItem.getStream(), addressStr, port, ssrc, requesterId,
  625 + channelId, mediaTransmissionTCP, null, responseSendItemMsg -> {
  626 + SendRtpItem sendRtpItem = responseSendItemMsg.getSendRtpItem();
  627 + if (sendRtpItem == null || responseSendItemMsg.getMediaServerItem() == null) {
  628 + logger.warn("服务器端口资源不足");
  629 + try {
  630 + responseAck(evt, Response.BUSY_HERE);
  631 + } catch (SipException e) {
  632 + e.printStackTrace();
  633 + } catch (InvalidArgumentException e) {
  634 + e.printStackTrace();
  635 + } catch (ParseException e) {
  636 + e.printStackTrace();
  637 + }
  638 + return;
  639 + }
  640 + // 收到sendItem
  641 + if (tcpActive != null) {
  642 + sendRtpItem.setTcpActive(tcpActive);
  643 + }
  644 + sendRtpItem.setPlayType(InviteStreamType.PUSH);
  645 + // 写入redis, 超时时回复
  646 + sendRtpItem.setStatus(1);
  647 + sendRtpItem.setCallId(callIdHeader.getCallId());
  648 + byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
  649 + sendRtpItem.setDialog(dialogByteArray);
  650 + byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
  651 + sendRtpItem.setTransaction(transactionByteArray);
  652 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  653 + sendStreamAck(responseSendItemMsg.getMediaServerItem(), sendRtpItem, platform, evt);
  654 + }, (wvpResult) -> {
  655 + try {
  656 + // 错误
  657 + if (wvpResult.getCode() == RedisGbPlayMsgListener.ERROR_CODE_OFFLINE) {
  658 + // 离线
  659 + // 查询是否在本机上线了
  660 + StreamPushItem currentStreamPushItem = streamPushService.getPush(streamPushItem.getApp(), streamPushItem.getStream());
  661 + if (currentStreamPushItem.isStatus()) {
  662 + // 在线状态
  663 + pushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  664 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  665 +
  666 + } else {
  667 + // 不在线 拉起
  668 + notifyStreamOnline(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  669 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  670 + }
  671 + }
  672 + } catch (InvalidArgumentException e) {
  673 + throw new RuntimeException(e);
  674 + } catch (ParseException e) {
  675 + throw new RuntimeException(e);
  676 + } catch (SipException e) {
  677 + throw new RuntimeException(e);
  678 + }
  679 +
  680 +
  681 + try {
  682 + responseAck(evt, Response.BUSY_HERE);
  683 + } catch (SipException e) {
  684 + e.printStackTrace();
  685 + } catch (InvalidArgumentException e) {
  686 + e.printStackTrace();
  687 + } catch (ParseException e) {
  688 + e.printStackTrace();
  689 + }
  690 + return;
  691 + });
  692 + }
  693 +
  694 + public void sendStreamAck(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt) {
  695 +
  696 + StringBuffer content = new StringBuffer(200);
  697 + content.append("v=0\r\n");
  698 + content.append("o=" + sendRtpItem.getChannelId() + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
  699 + content.append("s=Play\r\n");
  700 + content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
  701 + content.append("t=0 0\r\n");
  702 + content.append("m=video " + sendRtpItem.getLocalPort() + " RTP/AVP 96\r\n");
  703 + content.append("a=sendonly\r\n");
  704 + content.append("a=rtpmap:96 PS/90000\r\n");
  705 + if (sendRtpItem.isTcp()) {
  706 + content.append("a=connection:new\r\n");
  707 + if (!sendRtpItem.isTcpActive()) {
  708 + content.append("a=setup:active\r\n");
  709 + } else {
  710 + content.append("a=setup:passive\r\n");
  711 + }
  712 + }
  713 + content.append("y=" + sendRtpItem.getSsrc() + "\r\n");
  714 + content.append("f=\r\n");
  715 +
  716 + try {
  717 + responseSdpAck(evt, content.toString(), platform);
  718 + } catch (SipException e) {
  719 + e.printStackTrace();
  720 + } catch (InvalidArgumentException e) {
  721 + e.printStackTrace();
  722 + } catch (ParseException e) {
  723 + e.printStackTrace();
  724 + }
  725 + }
  726 +
  727 + public void inviteFromDeviceHandle(RequestEvent evt, String requesterId) throws InvalidArgumentException, ParseException, SipException, SdpException {
  728 +
  729 + // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备)
  730 + Device device = redisCatchStorage.getDevice(requesterId);
  731 + Request request = evt.getRequest();
  732 + if (device != null) {
  733 + logger.info("收到设备" + requesterId + "的语音广播Invite请求");
  734 + responseAck(evt, Response.TRYING);
  735 +
  736 + String contentString = new String(request.getRawContent());
  737 + // jainSip不支持y=字段, 移除移除以解析。
  738 + String substring = contentString;
  739 + String ssrc = "0000000404";
  740 + int ssrcIndex = contentString.indexOf("y=");
  741 + if (ssrcIndex > 0) {
  742 + substring = contentString.substring(0, ssrcIndex);
  743 + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  744 + }
  745 + ssrcIndex = substring.indexOf("f=");
  746 + if (ssrcIndex > 0) {
  747 + substring = contentString.substring(0, ssrcIndex);
  748 + }
  749 + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
  750 +
  751 + // 获取支持的格式
  752 + Vector mediaDescriptions = sdp.getMediaDescriptions(true);
  753 + // 查看是否支持PS 负载96
  754 + int port = -1;
  755 + //boolean recvonly = false;
  756 + boolean mediaTransmissionTCP = false;
  757 + Boolean tcpActive = null;
  758 + for (int i = 0; i < mediaDescriptions.size(); i++) {
  759 + MediaDescription mediaDescription = (MediaDescription) mediaDescriptions.get(i);
  760 + Media media = mediaDescription.getMedia();
  761 +
  762 + Vector mediaFormats = media.getMediaFormats(false);
  763 + if (mediaFormats.contains("8")) {
  764 + port = media.getMediaPort();
  765 + String protocol = media.getProtocol();
  766 + // 区分TCP发流还是udp, 当前默认udp
  767 + if ("TCP/RTP/AVP".equals(protocol)) {
  768 + String setup = mediaDescription.getAttribute("setup");
  769 + if (setup != null) {
  770 + mediaTransmissionTCP = true;
  771 + if ("active".equals(setup)) {
  772 + tcpActive = true;
  773 + } else if ("passive".equals(setup)) {
  774 + tcpActive = false;
  775 + }
  776 + }
  777 + }
  778 + break;
  779 + }
  780 + }
  781 + if (port == -1) {
  782 + logger.info("不支持的媒体格式,返回415");
  783 + // 回复不支持的格式
  784 + responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
  785 + return;
  786 + }
  787 + String username = sdp.getOrigin().getUsername();
  788 + String addressStr = sdp.getOrigin().getAddress();
  789 + logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc);
  790 +
  791 + } else {
  792 + logger.warn("来自无效设备/平台的请求");
  793 + responseAck(evt, Response.BAD_REQUEST);
  794 + }
  795 + }
662 CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); 796 CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
663 sendRtpItem.setPlayType(InviteStreamType.PLAY); 797 sendRtpItem.setPlayType(InviteStreamType.PLAY);
664 sendRtpItem.setCallId(callIdHeader.getCallId()); 798 sendRtpItem.setCallId(callIdHeader.getCallId());
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
2 2
3 import com.alibaba.fastjson.JSONObject; 3 import com.alibaba.fastjson.JSONObject;
4 -import com.genersoft.iot.vmp.common.VideoManagerConstants;  
5 import com.genersoft.iot.vmp.conf.SipConfig; 4 import com.genersoft.iot.vmp.conf.SipConfig;
6 import com.genersoft.iot.vmp.conf.UserSetting; 5 import com.genersoft.iot.vmp.conf.UserSetting;
7 import com.genersoft.iot.vmp.gb28181.bean.*; 6 import com.genersoft.iot.vmp.gb28181.bean.*;
@@ -18,6 +17,7 @@ import com.genersoft.iot.vmp.gb28181.utils.SipUtils; @@ -18,6 +17,7 @@ import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
18 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; 17 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
19 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 18 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
20 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 19 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  20 +import com.genersoft.iot.vmp.utils.DateUtil;
21 import com.genersoft.iot.vmp.utils.redis.RedisUtil; 21 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
22 import org.dom4j.DocumentException; 22 import org.dom4j.DocumentException;
23 import org.dom4j.Element; 23 import org.dom4j.Element;
@@ -25,6 +25,8 @@ import org.slf4j.Logger; @@ -25,6 +25,8 @@ import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory; 25 import org.slf4j.LoggerFactory;
26 import org.springframework.beans.factory.InitializingBean; 26 import org.springframework.beans.factory.InitializingBean;
27 import org.springframework.beans.factory.annotation.Autowired; 27 import org.springframework.beans.factory.annotation.Autowired;
  28 +import org.springframework.beans.factory.annotation.Qualifier;
  29 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
28 import org.springframework.stereotype.Component; 30 import org.springframework.stereotype.Component;
29 import org.springframework.util.StringUtils; 31 import org.springframework.util.StringUtils;
30 32
@@ -35,6 +37,7 @@ import javax.sip.header.FromHeader; @@ -35,6 +37,7 @@ import javax.sip.header.FromHeader;
35 import javax.sip.message.Response; 37 import javax.sip.message.Response;
36 import java.text.ParseException; 38 import java.text.ParseException;
37 import java.util.Iterator; 39 import java.util.Iterator;
  40 +import java.util.concurrent.ConcurrentLinkedQueue;
38 41
39 /** 42 /**
40 * SIP命令类型: NOTIFY请求 43 * SIP命令类型: NOTIFY请求
@@ -63,11 +66,19 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @@ -63,11 +66,19 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
63 @Autowired 66 @Autowired
64 private EventPublisher publisher; 67 private EventPublisher publisher;
65 68
66 - private String method = "NOTIFY"; 69 + private final String method = "NOTIFY";
67 70
68 @Autowired 71 @Autowired
69 private SIPProcessorObserver sipProcessorObserver; 72 private SIPProcessorObserver sipProcessorObserver;
70 73
  74 + private boolean taskQueueHandlerRun = false;
  75 +
  76 + private final ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
  77 +
  78 + @Qualifier("taskExecutor")
  79 + @Autowired
  80 + private ThreadPoolTaskExecutor taskExecutor;
  81 +
71 @Override 82 @Override
72 public void afterPropertiesSet() throws Exception { 83 public void afterPropertiesSet() throws Exception {
73 // 添加消息处理的订阅 84 // 添加消息处理的订阅
@@ -77,23 +88,40 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @@ -77,23 +88,40 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
77 @Override 88 @Override
78 public void process(RequestEvent evt) { 89 public void process(RequestEvent evt) {
79 try { 90 try {
80 - Element rootElement = getRootElement(evt);  
81 - String cmd = XmlUtil.getText(rootElement, "CmdType");  
82 -  
83 - if (CmdType.CATALOG.equals(cmd)) {  
84 - logger.info("接收到Catalog通知");  
85 - processNotifyCatalogList(evt);  
86 - } else if (CmdType.ALARM.equals(cmd)) {  
87 - logger.info("接收到Alarm通知");  
88 - processNotifyAlarm(evt);  
89 - } else if (CmdType.MOBILE_POSITION.equals(cmd)) {  
90 - logger.info("接收到MobilePosition通知");  
91 - processNotifyMobilePosition(evt);  
92 - } else {  
93 - logger.info("接收到消息:" + cmd);  
94 - responseAck(evt, Response.OK); 91 +
  92 + taskQueue.offer(new HandlerCatchData(evt, null, null));
  93 + responseAck(evt, Response.OK);
  94 + if (!taskQueueHandlerRun) {
  95 + taskQueueHandlerRun = true;
  96 + taskExecutor.execute(()-> {
  97 + while (!taskQueue.isEmpty()) {
  98 + try {
  99 + HandlerCatchData take = taskQueue.poll();
  100 + Element rootElement = getRootElement(take.getEvt());
  101 + String cmd = XmlUtil.getText(rootElement, "CmdType");
  102 +
  103 + if (CmdType.CATALOG.equals(cmd)) {
  104 + logger.info("接收到Catalog通知");
  105 + processNotifyCatalogList(take.getEvt());
  106 + } else if (CmdType.ALARM.equals(cmd)) {
  107 + logger.info("接收到Alarm通知");
  108 + processNotifyAlarm(take.getEvt());
  109 + } else if (CmdType.MOBILE_POSITION.equals(cmd)) {
  110 + logger.info("接收到MobilePosition通知");
  111 + processNotifyMobilePosition(take.getEvt());
  112 + } else {
  113 + logger.info("接收到消息:" + cmd);
  114 + }
  115 + } catch (DocumentException e) {
  116 + throw new RuntimeException(e);
  117 + }
  118 + }
  119 + taskQueueHandlerRun = false;
  120 + });
95 } 121 }
96 - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { 122 +
  123 +
  124 + } catch (SipException | InvalidArgumentException | ParseException e) {
97 e.printStackTrace(); 125 e.printStackTrace();
98 } 126 }
99 } 127 }
@@ -166,8 +194,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @@ -166,8 +194,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
166 jsonObject.put("direction", mobilePosition.getDirection()); 194 jsonObject.put("direction", mobilePosition.getDirection());
167 jsonObject.put("speed", mobilePosition.getSpeed()); 195 jsonObject.put("speed", mobilePosition.getSpeed());
168 redisCatchStorage.sendMobilePositionMsg(jsonObject); 196 redisCatchStorage.sendMobilePositionMsg(jsonObject);
169 - responseAck(evt, Response.OK);  
170 - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { 197 + } catch (DocumentException e) {
171 e.printStackTrace(); 198 e.printStackTrace();
172 } 199 }
173 } 200 }
@@ -188,6 +215,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @@ -188,6 +215,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
188 215
189 Device device = redisCatchStorage.getDevice(deviceId); 216 Device device = redisCatchStorage.getDevice(deviceId);
190 if (device == null) { 217 if (device == null) {
  218 + logger.warn("[ NotifyAlarm ] 未找到设备:{}", deviceId);
191 return; 219 return;
192 } 220 }
193 rootElement = getRootElement(evt, device.getCharset()); 221 rootElement = getRootElement(evt, device.getCharset());
@@ -195,7 +223,12 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @@ -195,7 +223,12 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
195 deviceAlarm.setDeviceId(deviceId); 223 deviceAlarm.setDeviceId(deviceId);
196 deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority")); 224 deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority"));
197 deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod")); 225 deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod"));
198 - deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime")); 226 + String alarmTime = XmlUtil.getText(rootElement, "AlarmTime");
  227 + if (alarmTime == null) {
  228 + logger.warn("[ NotifyAlarm ] AlarmTime cannot be null");
  229 + return;
  230 + }
  231 + deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime));
199 if (XmlUtil.getText(rootElement, "AlarmDescription") == null) { 232 if (XmlUtil.getText(rootElement, "AlarmDescription") == null) {
200 deviceAlarm.setAlarmDescription(""); 233 deviceAlarm.setAlarmDescription("");
201 } else { 234 } else {
@@ -212,7 +245,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @@ -212,7 +245,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
212 deviceAlarm.setLatitude(0.00); 245 deviceAlarm.setLatitude(0.00);
213 } 246 }
214 logger.info("[收到Notify-Alarm]:{}/{}", device.getDeviceId(), deviceAlarm.getChannelId()); 247 logger.info("[收到Notify-Alarm]:{}/{}", device.getDeviceId(), deviceAlarm.getChannelId());
215 - if (deviceAlarm.getAlarmMethod().equals("4")) { 248 + if ("4".equals(deviceAlarm.getAlarmMethod())) {
216 MobilePosition mobilePosition = new MobilePosition(); 249 MobilePosition mobilePosition = new MobilePosition();
217 mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); 250 mobilePosition.setDeviceId(deviceAlarm.getDeviceId());
218 mobilePosition.setTime(deviceAlarm.getAlarmTime()); 251 mobilePosition.setTime(deviceAlarm.getAlarmTime());
@@ -233,11 +266,10 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @@ -233,11 +266,10 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
233 // TODO: 需要实现存储报警信息、报警分类 266 // TODO: 需要实现存储报警信息、报警分类
234 267
235 // 回复200 OK 268 // 回复200 OK
236 - responseAck(evt, Response.OK);  
237 if (redisCatchStorage.deviceIsOnline(deviceId)) { 269 if (redisCatchStorage.deviceIsOnline(deviceId)) {
238 publisher.deviceAlarmEventPublish(deviceAlarm); 270 publisher.deviceAlarmEventPublish(deviceAlarm);
239 } 271 }
240 - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { 272 + } catch (DocumentException e) {
241 e.printStackTrace(); 273 e.printStackTrace();
242 } 274 }
243 } 275 }
@@ -273,64 +305,60 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @@ -273,64 +305,60 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
273 continue; 305 continue;
274 } 306 }
275 Element eventElement = itemDevice.element("Event"); 307 Element eventElement = itemDevice.element("Event");
276 - DeviceChannel channel = XmlUtil.channelContentHander(itemDevice); 308 + String event;
  309 + if (eventElement == null) {
  310 + logger.warn("[收到 目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" ));
  311 + event = CatalogEvent.ADD;
  312 + }else {
  313 + event = eventElement.getText().toUpperCase();
  314 + }
  315 + DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device);
277 channel.setDeviceId(device.getDeviceId()); 316 channel.setDeviceId(device.getDeviceId());
278 logger.info("[收到 目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId()); 317 logger.info("[收到 目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId());
279 - switch (eventElement.getText().toUpperCase()) { 318 + switch (event) {
280 case CatalogEvent.ON: 319 case CatalogEvent.ON:
281 // 上线 320 // 上线
282 logger.info("收到来自设备【{}】的通道【{}】上线通知", device.getDeviceId(), channel.getChannelId()); 321 logger.info("收到来自设备【{}】的通道【{}】上线通知", device.getDeviceId(), channel.getChannelId());
283 storager.deviceChannelOnline(deviceId, channel.getChannelId()); 322 storager.deviceChannelOnline(deviceId, channel.getChannelId());
284 - // 回复200 OK  
285 - responseAck(evt, Response.OK);  
286 break; 323 break;
287 case CatalogEvent.OFF : 324 case CatalogEvent.OFF :
288 // 离线 325 // 离线
289 logger.info("收到来自设备【{}】的通道【{}】离线通知", device.getDeviceId(), channel.getChannelId()); 326 logger.info("收到来自设备【{}】的通道【{}】离线通知", device.getDeviceId(), channel.getChannelId());
290 storager.deviceChannelOffline(deviceId, channel.getChannelId()); 327 storager.deviceChannelOffline(deviceId, channel.getChannelId());
291 - // 回复200 OK  
292 - responseAck(evt, Response.OK);  
293 break; 328 break;
294 case CatalogEvent.VLOST: 329 case CatalogEvent.VLOST:
295 // 视频丢失 330 // 视频丢失
296 logger.info("收到来自设备【{}】的通道【{}】视频丢失通知", device.getDeviceId(), channel.getChannelId()); 331 logger.info("收到来自设备【{}】的通道【{}】视频丢失通知", device.getDeviceId(), channel.getChannelId());
297 storager.deviceChannelOffline(deviceId, channel.getChannelId()); 332 storager.deviceChannelOffline(deviceId, channel.getChannelId());
298 - // 回复200 OK  
299 - responseAck(evt, Response.OK);  
300 break; 333 break;
301 case CatalogEvent.DEFECT: 334 case CatalogEvent.DEFECT:
302 // 故障 335 // 故障
303 - // 回复200 OK  
304 - responseAck(evt, Response.OK);  
305 break; 336 break;
306 case CatalogEvent.ADD: 337 case CatalogEvent.ADD:
307 // 增加 338 // 增加
308 logger.info("收到来自设备【{}】的增加通道【{}】通知", device.getDeviceId(), channel.getChannelId()); 339 logger.info("收到来自设备【{}】的增加通道【{}】通知", device.getDeviceId(), channel.getChannelId());
309 storager.updateChannel(deviceId, channel); 340 storager.updateChannel(deviceId, channel);
310 - responseAck(evt, Response.OK);  
311 break; 341 break;
312 case CatalogEvent.DEL: 342 case CatalogEvent.DEL:
313 // 删除 343 // 删除
314 logger.info("收到来自设备【{}】的删除通道【{}】通知", device.getDeviceId(), channel.getChannelId()); 344 logger.info("收到来自设备【{}】的删除通道【{}】通知", device.getDeviceId(), channel.getChannelId());
315 storager.delChannel(deviceId, channel.getChannelId()); 345 storager.delChannel(deviceId, channel.getChannelId());
316 - responseAck(evt, Response.OK);  
317 break; 346 break;
318 case CatalogEvent.UPDATE: 347 case CatalogEvent.UPDATE:
319 // 更新 348 // 更新
320 logger.info("收到来自设备【{}】的更新通道【{}】通知", device.getDeviceId(), channel.getChannelId()); 349 logger.info("收到来自设备【{}】的更新通道【{}】通知", device.getDeviceId(), channel.getChannelId());
321 storager.updateChannel(deviceId, channel); 350 storager.updateChannel(deviceId, channel);
322 - responseAck(evt, Response.OK);  
323 break; 351 break;
324 default: 352 default:
325 - responseAck(evt, Response.BAD_REQUEST, "event not found"); 353 + logger.warn("[ NotifyCatalog ] event not found : {}", event );
326 354
327 } 355 }
328 // 转发变化信息 356 // 转发变化信息
329 - eventPublisher.catalogEventPublish(null, channel, eventElement.getText().toUpperCase()); 357 + eventPublisher.catalogEventPublish(null, channel, event);
330 358
331 } 359 }
332 } 360 }
333 - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { 361 + } catch (DocumentException e) {
334 e.printStackTrace(); 362 e.printStackTrace();
335 } 363 }
336 } 364 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
2 2
3 -import com.genersoft.iot.vmp.common.VideoManagerConstants;  
4 import com.genersoft.iot.vmp.conf.SipConfig; 3 import com.genersoft.iot.vmp.conf.SipConfig;
5 -import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;  
6 import com.genersoft.iot.vmp.gb28181.bean.Device; 4 import com.genersoft.iot.vmp.gb28181.bean.Device;
7 import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate; 5 import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
8 -import com.genersoft.iot.vmp.gb28181.event.EventPublisher;  
9 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; 6 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
10 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; 7 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
11 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; 8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
  9 +import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
12 import com.genersoft.iot.vmp.service.IDeviceService; 10 import com.genersoft.iot.vmp.service.IDeviceService;
13 -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;  
14 -import com.genersoft.iot.vmp.storager.IVideoManagerStorage;  
15 import com.genersoft.iot.vmp.utils.DateUtil; 11 import com.genersoft.iot.vmp.utils.DateUtil;
16 import gov.nist.javax.sip.RequestEventExt; 12 import gov.nist.javax.sip.RequestEventExt;
17 import gov.nist.javax.sip.address.AddressImpl; 13 import gov.nist.javax.sip.address.AddressImpl;
@@ -45,21 +41,12 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen @@ -45,21 +41,12 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
45 41
46 private final Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class); 42 private final Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class);
47 43
48 - public String method = "REGISTER"; 44 + public final String method = "REGISTER";
49 45
50 @Autowired 46 @Autowired
51 private SipConfig sipConfig; 47 private SipConfig sipConfig;
52 48
53 @Autowired 49 @Autowired
54 - private IRedisCatchStorage redisCatchStorage;  
55 -  
56 - @Autowired  
57 - private IVideoManagerStorage storager;  
58 -  
59 - @Autowired  
60 - private EventPublisher publisher;  
61 -  
62 - @Autowired  
63 private SIPProcessorObserver sipProcessorObserver; 50 private SIPProcessorObserver sipProcessorObserver;
64 51
65 @Autowired 52 @Autowired
@@ -86,7 +73,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen @@ -86,7 +73,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
86 ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME); 73 ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
87 Response response = null; 74 Response response = null;
88 boolean passwordCorrect = false; 75 boolean passwordCorrect = false;
89 - // 注册标志 0:未携带授权头或者密码错误 1:注册成功 2:注销成功 76 + // 注册标志
90 boolean registerFlag = false; 77 boolean registerFlag = false;
91 FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME); 78 FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
92 AddressImpl address = (AddressImpl) fromHeader.getAddress(); 79 AddressImpl address = (AddressImpl) fromHeader.getAddress();
@@ -105,7 +92,6 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen @@ -105,7 +92,6 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
105 // 校验密码是否正确 92 // 校验密码是否正确
106 passwordCorrect = StringUtils.isEmpty(sipConfig.getPassword()) || 93 passwordCorrect = StringUtils.isEmpty(sipConfig.getPassword()) ||
107 new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, sipConfig.getPassword()); 94 new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, sipConfig.getPassword());
108 - // 未携带授权头或者密码错误 回复401  
109 95
110 if (!passwordCorrect) { 96 if (!passwordCorrect) {
111 // 注册失败 97 // 注册失败
@@ -154,6 +140,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen @@ -154,6 +140,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
154 device = new Device(); 140 device = new Device();
155 device.setStreamMode("UDP"); 141 device.setStreamMode("UDP");
156 device.setCharset("GB2312"); 142 device.setCharset("GB2312");
  143 + device.setGeoCoordSys("WGS84");
157 device.setDeviceId(deviceId); 144 device.setDeviceId(deviceId);
158 } 145 }
159 device.setIp(received); 146 device.setIp(received);
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
@@ -82,7 +82,6 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme @@ -82,7 +82,6 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
82 @Override 82 @Override
83 public void process(RequestEvent evt) { 83 public void process(RequestEvent evt) {
84 Request request = evt.getRequest(); 84 Request request = evt.getRequest();
85 -  
86 try { 85 try {
87 Element rootElement = getRootElement(evt); 86 Element rootElement = getRootElement(evt);
88 String cmd = XmlUtil.getText(rootElement, "CmdType"); 87 String cmd = XmlUtil.getText(rootElement, "CmdType");
@@ -176,6 +175,8 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme @@ -176,6 +175,8 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
176 } 175 }
177 176
178 private void processNotifyCatalogList(RequestEvent evt, Element rootElement) throws SipException { 177 private void processNotifyCatalogList(RequestEvent evt, Element rootElement) throws SipException {
  178 +
  179 + System.out.println(evt.getRequest().toString());
179 String platformId = SipUtils.getUserIdFromFromHeader(evt.getRequest()); 180 String platformId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
180 String deviceId = XmlUtil.getText(rootElement, "DeviceID"); 181 String deviceId = XmlUtil.getText(rootElement, "DeviceID");
181 ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); 182 ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.info;
  2 +
  3 +import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.gb28181.bean.*;
  5 +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
  6 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  7 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  8 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  9 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
  10 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
  11 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  12 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  13 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  14 +import gov.nist.javax.sip.message.SIPRequest;
  15 +import org.slf4j.Logger;
  16 +import org.slf4j.LoggerFactory;
  17 +import org.springframework.beans.factory.InitializingBean;
  18 +import org.springframework.beans.factory.annotation.Autowired;
  19 +import org.springframework.stereotype.Component;
  20 +import javax.sip.InvalidArgumentException;
  21 +import javax.sip.RequestEvent;
  22 +import javax.sip.SipException;
  23 +import javax.sip.header.*;
  24 +import javax.sip.message.Response;
  25 +import java.text.ParseException;
  26 +
  27 +@Component
  28 +public class InfoRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
  29 +
  30 + private final static Logger logger = LoggerFactory.getLogger(InfoRequestProcessor.class);
  31 +
  32 + private final String method = "INFO";
  33 +
  34 + @Autowired
  35 + private SIPProcessorObserver sipProcessorObserver;
  36 +
  37 + @Autowired
  38 + private IVideoManagerStorage storage;
  39 +
  40 + @Autowired
  41 + private SipSubscribe sipSubscribe;
  42 +
  43 + @Autowired
  44 + private IRedisCatchStorage redisCatchStorage;
  45 +
  46 + @Autowired
  47 + private IVideoManagerStorage storager;
  48 +
  49 + @Autowired
  50 + private SIPCommander cmder;
  51 +
  52 + @Autowired
  53 + private VideoStreamSessionManager sessionManager;
  54 +
  55 + @Override
  56 + public void afterPropertiesSet() throws Exception {
  57 + // 添加消息处理的订阅
  58 + sipProcessorObserver.addRequestProcessor(method, this);
  59 + }
  60 +
  61 + @Override
  62 + public void process(RequestEvent evt) {
  63 + logger.debug("接收到消息:" + evt.getRequest());
  64 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  65 + CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
  66 + // 先从会话内查找
  67 + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
  68 + if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
  69 + deviceId = ssrcTransaction.getDeviceId();
  70 + }
  71 + // 查询设备是否存在
  72 + Device device = redisCatchStorage.getDevice(deviceId);
  73 + // 查询上级平台是否存在
  74 + ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId);
  75 + try {
  76 + if (device != null && parentPlatform != null) {
  77 + logger.warn("[重复]平台与设备编号重复:{}", deviceId);
  78 + SIPRequest request = (SIPRequest) evt.getRequest();
  79 + String hostAddress = request.getRemoteAddress().getHostAddress();
  80 + int remotePort = request.getRemotePort();
  81 + if (device.getHostAddress().equals(hostAddress + ":" + remotePort)) {
  82 + parentPlatform = null;
  83 + }else {
  84 + device = null;
  85 + }
  86 + }
  87 + if (device == null && parentPlatform == null) {
  88 + // 不存在则回复404
  89 + responseAck(evt, Response.NOT_FOUND, "device "+ deviceId +" not found");
  90 + logger.warn("[设备未找到 ]: {}", deviceId);
  91 + if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){
  92 + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new DeviceNotFoundEvent(evt.getDialog()));
  93 + sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult);
  94 + };
  95 + }else {
  96 + ContentTypeHeader header = (ContentTypeHeader)evt.getRequest().getHeader(ContentTypeHeader.NAME);
  97 + String contentType = header.getContentType();
  98 + String contentSubType = header.getContentSubType();
  99 + if ("Application".equals(contentType) && "MANSRTSP".equals(contentSubType)) {
  100 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId());
  101 + String streamId = sendRtpItem.getStreamId();
  102 + StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null);
  103 + if (null == streamInfo) {
  104 + responseAck(evt, Response.NOT_FOUND, "stream " + streamId + " not found");
  105 + return;
  106 + }
  107 + Device device1 = storager.queryVideoDevice(streamInfo.getDeviceID());
  108 + cmder.playbackControlCmd(device1,streamInfo,new String(evt.getRequest().getRawContent()),eventResult -> {
  109 + // 失败的回复
  110 + try {
  111 + responseAck(evt, eventResult.statusCode, eventResult.msg);
  112 + } catch (SipException e) {
  113 + e.printStackTrace();
  114 + } catch (InvalidArgumentException e) {
  115 + e.printStackTrace();
  116 + } catch (ParseException e) {
  117 + e.printStackTrace();
  118 + }
  119 + }, eventResult -> {
  120 + // 成功的回复
  121 + try {
  122 + responseAck(evt, eventResult.statusCode);
  123 + } catch (SipException e) {
  124 + e.printStackTrace();
  125 + } catch (InvalidArgumentException e) {
  126 + e.printStackTrace();
  127 + } catch (ParseException e) {
  128 + e.printStackTrace();
  129 + }
  130 + });
  131 + }
  132 + }
  133 + } catch (SipException e) {
  134 + logger.warn("SIP 回复错误", e);
  135 + } catch (InvalidArgumentException e) {
  136 + logger.warn("参数无效", e);
  137 + } catch (ParseException e) {
  138 + logger.warn("SIP回复时解析异常", e);
  139 + }
  140 + }
  141 +
  142 +
  143 +}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java
@@ -72,7 +72,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement @@ -72,7 +72,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
72 String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest()); 72 String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
73 CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); 73 CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
74 // 先从会话内查找 74 // 先从会话内查找
75 - SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, null, callIdHeader.getCallId()); 75 + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
76 if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题 76 if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
77 deviceId = ssrcTransaction.getDeviceId(); 77 deviceId = ssrcTransaction.getDeviceId();
78 } 78 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
@@ -9,9 +9,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessag @@ -9,9 +9,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessag
9 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler; 9 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
10 import com.genersoft.iot.vmp.gb28181.utils.Coordtransform; 10 import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
11 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; 11 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
  12 +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
12 import com.genersoft.iot.vmp.service.IDeviceAlarmService; 13 import com.genersoft.iot.vmp.service.IDeviceAlarmService;
13 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 14 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
14 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 15 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  16 +import com.genersoft.iot.vmp.utils.DateUtil;
15 import org.dom4j.Element; 17 import org.dom4j.Element;
16 import org.slf4j.Logger; 18 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory; 19 import org.slf4j.LoggerFactory;
@@ -84,7 +86,11 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @@ -84,7 +86,11 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
84 deviceAlarm.setChannelId(channelId); 86 deviceAlarm.setChannelId(channelId);
85 deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority")); 87 deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority"));
86 deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod")); 88 deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod"));
87 - deviceAlarm.setAlarmTime(getText(rootElement, "AlarmTime")); 89 + String alarmTime = XmlUtil.getText(rootElement, "AlarmTime");
  90 + if (alarmTime == null) {
  91 + return;
  92 + }
  93 + deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime));
88 String alarmDescription = getText(rootElement, "AlarmDescription"); 94 String alarmDescription = getText(rootElement, "AlarmDescription");
89 if (alarmDescription == null) { 95 if (alarmDescription == null) {
90 deviceAlarm.setAlarmDescription(""); 96 deviceAlarm.setAlarmDescription("");
@@ -175,7 +181,11 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @@ -175,7 +181,11 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
175 deviceAlarm.setChannelId(channelId); 181 deviceAlarm.setChannelId(channelId);
176 deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority")); 182 deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority"));
177 deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod")); 183 deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod"));
178 - deviceAlarm.setAlarmTime(getText(rootElement, "AlarmTime")); 184 + String alarmTime = XmlUtil.getText(rootElement, "AlarmTime");
  185 + if (alarmTime == null) {
  186 + return;
  187 + }
  188 + deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime));
179 String alarmDescription = getText(rootElement, "AlarmDescription"); 189 String alarmDescription = getText(rootElement, "AlarmDescription");
180 if (alarmDescription == null) { 190 if (alarmDescription == null) {
181 deviceAlarm.setAlarmDescription(""); 191 deviceAlarm.setAlarmDescription("");
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
@@ -64,16 +64,14 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp @@ -64,16 +64,14 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
64 device.setHostAddress(received.concat(":").concat(String.valueOf(rPort))); 64 device.setHostAddress(received.concat(":").concat(String.valueOf(rPort)));
65 } 65 }
66 device.setKeepaliveTime(DateUtil.getNow()); 66 device.setKeepaliveTime(DateUtil.getNow());
  67 + // 回复200 OK
  68 + responseAck(evt, Response.OK);
67 if (device.getOnline() == 1) { 69 if (device.getOnline() == 1) {
68 - // 回复200 OK  
69 - responseAck(evt, Response.OK);  
70 deviceService.updateDevice(device); 70 deviceService.updateDevice(device);
71 }else { 71 }else {
72 // 对于已经离线的设备判断他的注册是否已经过期 72 // 对于已经离线的设备判断他的注册是否已经过期
73 if (!deviceService.expire(device)){ 73 if (!deviceService.expire(device)){
74 deviceService.online(device); 74 deviceService.online(device);
75 - // 回复200 OK  
76 - responseAck(evt, Response.OK);  
77 } 75 }
78 } 76 }
79 } catch (SipException e) { 77 } catch (SipException e) {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java
@@ -3,11 +3,16 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify @@ -3,11 +3,16 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify
3 import com.genersoft.iot.vmp.common.StreamInfo; 3 import com.genersoft.iot.vmp.common.StreamInfo;
4 import com.genersoft.iot.vmp.gb28181.bean.Device; 4 import com.genersoft.iot.vmp.gb28181.bean.Device;
5 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; 5 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  6 +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
  7 +import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
  8 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
6 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; 9 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  10 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
7 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; 11 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; 12 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
9 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler; 13 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
10 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 14 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  15 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
11 import org.dom4j.Element; 16 import org.dom4j.Element;
12 import org.slf4j.Logger; 17 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory; 18 import org.slf4j.LoggerFactory;
@@ -37,8 +42,17 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i @@ -37,8 +42,17 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
37 private SIPCommander cmder; 42 private SIPCommander cmder;
38 43
39 @Autowired 44 @Autowired
  45 + private SIPCommanderFroPlatform sipCommanderFroPlatform;
  46 +
  47 + @Autowired
40 private IRedisCatchStorage redisCatchStorage; 48 private IRedisCatchStorage redisCatchStorage;
41 49
  50 + @Autowired
  51 + private IVideoManagerStorage storage;
  52 +
  53 + @Autowired
  54 + private VideoStreamSessionManager sessionManager;
  55 +
42 @Override 56 @Override
43 public void afterPropertiesSet() throws Exception { 57 public void afterPropertiesSet() throws Exception {
44 notifyMessageHandler.addHandler(cmdType, this); 58 notifyMessageHandler.addHandler(cmdType, this);
@@ -59,17 +73,32 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i @@ -59,17 +73,32 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
59 } 73 }
60 CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); 74 CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
61 String NotifyType =getText(rootElement, "NotifyType"); 75 String NotifyType =getText(rootElement, "NotifyType");
62 - if (NotifyType.equals("121")){ 76 + if ("121".equals(NotifyType)){
63 logger.info("[录像流]推送完毕,收到关流通知"); 77 logger.info("[录像流]推送完毕,收到关流通知");
64 - String channelId =getText(rootElement, "DeviceID");  
65 // 查询是设备 78 // 查询是设备
66 - StreamInfo streamInfo = redisCatchStorage.queryDownload(device.getDeviceId(), channelId, null, callIdHeader.getCallId());  
67 - // 设置进度100%  
68 - streamInfo.setProgress(1);  
69 - redisCatchStorage.startDownload(streamInfo, callIdHeader.getCallId());  
70 - cmder.streamByeCmd(device.getDeviceId(), channelId, null, callIdHeader.getCallId());  
71 - // TODO 如果级联播放,需要给上级发送此通知 79 + StreamInfo streamInfo = redisCatchStorage.queryDownload(null, null, null, callIdHeader.getCallId());
  80 + if (streamInfo != null) {
  81 + // 设置进度100%
  82 + streamInfo.setProgress(1);
  83 + redisCatchStorage.startDownload(streamInfo, callIdHeader.getCallId());
  84 + }
  85 +
  86 + // 先从会话内查找
  87 + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
  88 + if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
  89 + cmder.streamByeCmd(device.getDeviceId(), ssrcTransaction.getChannelId(), null, callIdHeader.getCallId());
72 90
  91 + // 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题,需要将点播CallId进行上下级绑定
  92 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null);
  93 + if (sendRtpItem != null) {
  94 + ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
  95 + if (parentPlatform == null) {
  96 + logger.warn("[级联消息发送]:发送MediaStatus发现上级平台{}不存在", sendRtpItem.getPlatformId());
  97 + return;
  98 + }
  99 + sipCommanderFroPlatform.sendMediaStatusNotify(parentPlatform, sendRtpItem);
  100 + }
  101 + }
73 } 102 }
74 } 103 }
75 104
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
@@ -20,6 +20,8 @@ import org.slf4j.Logger; @@ -20,6 +20,8 @@ import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory; 20 import org.slf4j.LoggerFactory;
21 import org.springframework.beans.factory.InitializingBean; 21 import org.springframework.beans.factory.InitializingBean;
22 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.springframework.beans.factory.annotation.Qualifier;
  24 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
23 import org.springframework.stereotype.Component; 25 import org.springframework.stereotype.Component;
24 import org.springframework.util.StringUtils; 26 import org.springframework.util.StringUtils;
25 27
@@ -31,6 +33,7 @@ import java.text.ParseException; @@ -31,6 +33,7 @@ import java.text.ParseException;
31 import java.util.ArrayList; 33 import java.util.ArrayList;
32 import java.util.Iterator; 34 import java.util.Iterator;
33 import java.util.List; 35 import java.util.List;
  36 +import java.util.concurrent.ConcurrentLinkedQueue;
34 37
35 @Component 38 @Component
36 public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { 39 public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
@@ -38,9 +41,13 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp @@ -38,9 +41,13 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
38 private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class); 41 private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class);
39 private final String cmdType = "Catalog"; 42 private final String cmdType = "Catalog";
40 43
  44 + private boolean taskQueueHandlerRun = false;
  45 +
41 @Autowired 46 @Autowired
42 private ResponseMessageHandler responseMessageHandler; 47 private ResponseMessageHandler responseMessageHandler;
43 48
  49 + private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
  50 +
44 @Autowired 51 @Autowired
45 private IVideoManagerStorage storager; 52 private IVideoManagerStorage storager;
46 53
@@ -63,6 +70,10 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp @@ -63,6 +70,10 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
63 @Autowired 70 @Autowired
64 private IRedisCatchStorage redisCatchStorage; 71 private IRedisCatchStorage redisCatchStorage;
65 72
  73 + @Qualifier("taskExecutor")
  74 + @Autowired
  75 + private ThreadPoolTaskExecutor taskExecutor;
  76 +
66 @Override 77 @Override
67 public void afterPropertiesSet() throws Exception { 78 public void afterPropertiesSet() throws Exception {
68 responseMessageHandler.addHandler(cmdType, this); 79 responseMessageHandler.addHandler(cmdType, this);
@@ -70,68 +81,88 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp @@ -70,68 +81,88 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
70 81
71 @Override 82 @Override
72 public void handForDevice(RequestEvent evt, Device device, Element element) { 83 public void handForDevice(RequestEvent evt, Device device, Element element) {
73 - String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + device.getDeviceId();  
74 - Element rootElement = null; 84 + taskQueue.offer(new HandlerCatchData(evt, device, element));
  85 + // 回复200 OK
75 try { 86 try {
76 - rootElement = getRootElement(evt, device.getCharset());  
77 - Element deviceListElement = rootElement.element("DeviceList");  
78 - Element sumNumElement = rootElement.element("SumNum");  
79 - Element snElement = rootElement.element("SN");  
80 - if (snElement == null || sumNumElement == null || deviceListElement == null) {  
81 - responseAck(evt, Response.BAD_REQUEST, "xml error");  
82 - return;  
83 - }  
84 - int sumNum = Integer.parseInt(sumNumElement.getText());  
85 -  
86 - if (sumNum == 0) {  
87 - // 数据已经完整接收  
88 - storager.cleanChannelsForDevice(device.getDeviceId());  
89 - catalogDataCatch.setChannelSyncEnd(device.getDeviceId(), null);  
90 - }else {  
91 - Iterator<Element> deviceListIterator = deviceListElement.elementIterator();  
92 - if (deviceListIterator != null) {  
93 - List<DeviceChannel> channelList = new ArrayList<>();  
94 - // 遍历DeviceList  
95 - while (deviceListIterator.hasNext()) {  
96 - Element itemDevice = deviceListIterator.next();  
97 - Element channelDeviceElement = itemDevice.element("DeviceID");  
98 - if (channelDeviceElement == null) {  
99 - continue; 87 + responseAck(evt, Response.OK);
  88 + } catch (SipException e) {
  89 + throw new RuntimeException(e);
  90 + } catch (InvalidArgumentException e) {
  91 + throw new RuntimeException(e);
  92 + } catch (ParseException e) {
  93 + throw new RuntimeException(e);
  94 + }
  95 + if (!taskQueueHandlerRun) {
  96 + taskQueueHandlerRun = true;
  97 + taskExecutor.execute(()-> {
  98 + while (!taskQueue.isEmpty()) {
  99 + HandlerCatchData take = taskQueue.poll();
  100 + String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + take.getDevice().getDeviceId();
  101 + Element rootElement = null;
  102 + try {
  103 + rootElement = getRootElement(take.getEvt(), take.getDevice().getCharset());
  104 + Element deviceListElement = rootElement.element("DeviceList");
  105 + Element sumNumElement = rootElement.element("SumNum");
  106 + Element snElement = rootElement.element("SN");
  107 + if (snElement == null || sumNumElement == null || deviceListElement == null) {
  108 + responseAck(take.getEvt(), Response.BAD_REQUEST, "xml error");
  109 + return;
100 } 110 }
101 - //by brewswang  
102 -// if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {//如果包含位置信息,就更新一下位置  
103 -// processNotifyMobilePosition(evt, itemDevice);  
104 -// }  
105 - DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice);  
106 - deviceChannel.setDeviceId(device.getDeviceId());  
107 -  
108 - channelList.add(deviceChannel);  
109 - }  
110 - int sn = Integer.parseInt(snElement.getText());  
111 - catalogDataCatch.put(device.getDeviceId(), sn, sumNum, device, channelList);  
112 - logger.info("收到来自设备【{}】的通道: {}个,{}/{}", device.getDeviceId(), channelList.size(), catalogDataCatch.get(device.getDeviceId()) == null ? 0 :catalogDataCatch.get(device.getDeviceId()).size(), sumNum);  
113 - if (catalogDataCatch.get(device.getDeviceId()).size() == sumNum) {  
114 - // 数据已经完整接收  
115 - boolean resetChannelsResult = storager.resetChannels(device.getDeviceId(), catalogDataCatch.get(device.getDeviceId()));  
116 - if (!resetChannelsResult) {  
117 - String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.get(device.getDeviceId()).size() + "条";  
118 - catalogDataCatch.setChannelSyncEnd(device.getDeviceId(), errorMsg); 111 + int sumNum = Integer.parseInt(sumNumElement.getText());
  112 +
  113 + if (sumNum == 0) {
  114 + // 数据已经完整接收
  115 + storager.cleanChannelsForDevice(take.getDevice().getDeviceId());
  116 + catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
119 }else { 117 }else {
120 - catalogDataCatch.setChannelSyncEnd(device.getDeviceId(), null); 118 + Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
  119 + if (deviceListIterator != null) {
  120 + List<DeviceChannel> channelList = new ArrayList<>();
  121 + // 遍历DeviceList
  122 + while (deviceListIterator.hasNext()) {
  123 + Element itemDevice = deviceListIterator.next();
  124 + Element channelDeviceElement = itemDevice.element("DeviceID");
  125 + if (channelDeviceElement == null) {
  126 + continue;
  127 + }
  128 + //by brewswang
  129 + // if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {//如果包含位置信息,就更新一下位置
  130 + // processNotifyMobilePosition(evt, itemDevice);
  131 + // }
  132 + DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device);
  133 + deviceChannel.setDeviceId(take.getDevice().getDeviceId());
  134 +
  135 + channelList.add(deviceChannel);
  136 + }
  137 + int sn = Integer.parseInt(snElement.getText());
  138 + catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), channelList);
  139 + logger.info("收到来自设备【{}】的通道: {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 :catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum);
  140 + if (catalogDataCatch.get(take.getDevice().getDeviceId()).size() == sumNum) {
  141 + // 数据已经完整接收
  142 + boolean resetChannelsResult = storager.resetChannels(take.getDevice().getDeviceId(), catalogDataCatch.get(take.getDevice().getDeviceId()));
  143 + if (!resetChannelsResult) {
  144 + String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.get(take.getDevice().getDeviceId()).size() + "条";
  145 + catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), errorMsg);
  146 + }else {
  147 + catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
  148 + }
  149 + }
  150 + }
  151 +
121 } 152 }
  153 + } catch (DocumentException e) {
  154 + e.printStackTrace();
  155 + } catch (InvalidArgumentException e) {
  156 + e.printStackTrace();
  157 + } catch (ParseException e) {
  158 + e.printStackTrace();
  159 + } catch (SipException e) {
  160 + e.printStackTrace();
122 } 161 }
123 } 162 }
124 - // 回复200 OK  
125 - responseAck(evt, Response.OK);  
126 - }  
127 - } catch (DocumentException e) {  
128 - e.printStackTrace();  
129 - } catch (InvalidArgumentException e) {  
130 - e.printStackTrace();  
131 - } catch (ParseException e) {  
132 - e.printStackTrace();  
133 - } catch (SipException e) {  
134 - e.printStackTrace(); 163 + taskQueueHandlerRun = false;
  164 + });
  165 +
135 } 166 }
136 } 167 }
137 168
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java
@@ -82,7 +82,7 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen @@ -82,7 +82,7 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen
82 deviceService.offline(device.getDeviceId()); 82 deviceService.offline(device.getDeviceId());
83 } 83 }
84 RequestMessage msg = new RequestMessage(); 84 RequestMessage msg = new RequestMessage();
85 - msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId() + channelId); 85 + msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId());
86 msg.setData(json); 86 msg.setData(json);
87 deferredResultHolder.invokeAllResult(msg); 87 deferredResultHolder.invokeAllResult(msg);
88 } 88 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
2 2
3 -import com.genersoft.iot.vmp.gb28181.bean.Device;  
4 -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;  
5 -import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;  
6 -import com.genersoft.iot.vmp.gb28181.bean.RecordItem; 3 +import com.genersoft.iot.vmp.gb28181.bean.*;
7 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 4 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
8 import com.genersoft.iot.vmp.gb28181.session.RecordDataCatch; 5 import com.genersoft.iot.vmp.gb28181.session.RecordDataCatch;
9 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; 6 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
@@ -19,6 +16,8 @@ import org.slf4j.Logger; @@ -19,6 +16,8 @@ import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory; 16 import org.slf4j.LoggerFactory;
20 import org.springframework.beans.factory.InitializingBean; 17 import org.springframework.beans.factory.InitializingBean;
21 import org.springframework.beans.factory.annotation.Autowired; 18 import org.springframework.beans.factory.annotation.Autowired;
  19 +import org.springframework.beans.factory.annotation.Qualifier;
  20 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
22 import org.springframework.stereotype.Component; 21 import org.springframework.stereotype.Component;
23 import org.springframework.util.StringUtils; 22 import org.springframework.util.StringUtils;
24 23
@@ -28,6 +27,9 @@ import javax.sip.SipException; @@ -28,6 +27,9 @@ import javax.sip.SipException;
28 import javax.sip.message.Response; 27 import javax.sip.message.Response;
29 import java.text.ParseException; 28 import java.text.ParseException;
30 import java.util.*; 29 import java.util.*;
  30 +import java.util.concurrent.BlockingQueue;
  31 +import java.util.concurrent.ConcurrentLinkedQueue;
  32 +import java.util.concurrent.LinkedBlockingQueue;
31 33
32 import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; 34 import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
33 35
@@ -38,10 +40,11 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; @@ -38,10 +40,11 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
38 public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { 40 public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
39 41
40 private Logger logger = LoggerFactory.getLogger(RecordInfoResponseMessageHandler.class); 42 private Logger logger = LoggerFactory.getLogger(RecordInfoResponseMessageHandler.class);
41 - public static volatile List<String> threadNameList = new ArrayList();  
42 private final String cmdType = "RecordInfo"; 43 private final String cmdType = "RecordInfo";
43 - private final static String CACHE_RECORDINFO_KEY = "CACHE_RECORDINFO_";  
44 44
  45 + private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
  46 +
  47 + private boolean taskQueueHandlerRun = false;
45 @Autowired 48 @Autowired
46 private ResponseMessageHandler responseMessageHandler; 49 private ResponseMessageHandler responseMessageHandler;
47 50
@@ -51,11 +54,13 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent @@ -51,11 +54,13 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
51 @Autowired 54 @Autowired
52 private DeferredResultHolder deferredResultHolder; 55 private DeferredResultHolder deferredResultHolder;
53 56
54 -  
55 -  
56 @Autowired 57 @Autowired
57 private EventPublisher eventPublisher; 58 private EventPublisher eventPublisher;
58 59
  60 + @Qualifier("taskExecutor")
  61 + @Autowired
  62 + private ThreadPoolTaskExecutor taskExecutor;
  63 +
59 @Override 64 @Override
60 public void afterPropertiesSet() throws Exception { 65 public void afterPropertiesSet() throws Exception {
61 responseMessageHandler.addHandler(cmdType, this); 66 responseMessageHandler.addHandler(cmdType, this);
@@ -67,67 +72,89 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent @@ -67,67 +72,89 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
67 // 回复200 OK 72 // 回复200 OK
68 try { 73 try {
69 responseAck(evt, Response.OK); 74 responseAck(evt, Response.OK);
70 -  
71 - rootElement = getRootElement(evt, device.getCharset());  
72 - String sn = getText(rootElement, "SN");  
73 -  
74 - String sumNumStr = getText(rootElement, "SumNum");  
75 - int sumNum = 0;  
76 - if (!StringUtils.isEmpty(sumNumStr)) {  
77 - sumNum = Integer.parseInt(sumNumStr);  
78 - }  
79 - Element recordListElement = rootElement.element("RecordList");  
80 - if (recordListElement == null || sumNum == 0) {  
81 - logger.info("无录像数据");  
82 - recordDataCatch.put(device.getDeviceId(), sn, sumNum, new ArrayList<>());  
83 - releaseRequest(device.getDeviceId(), sn);  
84 - } else {  
85 - Iterator<Element> recordListIterator = recordListElement.elementIterator();  
86 - if (recordListIterator != null) {  
87 - List<RecordItem> recordList = new ArrayList<>();  
88 - // 遍历DeviceList  
89 - while (recordListIterator.hasNext()) {  
90 - Element itemRecord = recordListIterator.next();  
91 - Element recordElement = itemRecord.element("DeviceID");  
92 - if (recordElement == null) {  
93 - logger.info("记录为空,下一个...");  
94 - continue; 75 + taskQueue.offer(new HandlerCatchData(evt, device, rootElement));
  76 + if (!taskQueueHandlerRun) {
  77 + taskQueueHandlerRun = true;
  78 + taskExecutor.execute(()->{
  79 + try {
  80 + while (!taskQueue.isEmpty()) {
  81 + HandlerCatchData take = taskQueue.poll();
  82 + Element rootElementForCharset = getRootElement(take.getEvt(), take.getDevice().getCharset());
  83 + String sn = getText(rootElementForCharset, "SN");
  84 + String channelId = getText(rootElementForCharset, "DeviceID");
  85 + RecordInfo recordInfo = new RecordInfo();
  86 + recordInfo.setChannelId(channelId);
  87 + recordInfo.setDeviceId(take.getDevice().getDeviceId());
  88 + recordInfo.setSn(sn);
  89 + recordInfo.setName(getText(rootElementForCharset, "Name"));
  90 + String sumNumStr = getText(rootElementForCharset, "SumNum");
  91 + int sumNum = 0;
  92 + if (!StringUtils.isEmpty(sumNumStr)) {
  93 + sumNum = Integer.parseInt(sumNumStr);
  94 + }
  95 + recordInfo.setSumNum(sumNum);
  96 + Element recordListElement = rootElementForCharset.element("RecordList");
  97 + if (recordListElement == null || sumNum == 0) {
  98 + logger.info("无录像数据");
  99 + eventPublisher.recordEndEventPush(recordInfo);
  100 + recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, new ArrayList<>());
  101 + releaseRequest(take.getDevice().getDeviceId(), sn);
  102 + } else {
  103 + Iterator<Element> recordListIterator = recordListElement.elementIterator();
  104 + if (recordListIterator != null) {
  105 + List<RecordItem> recordList = new ArrayList<>();
  106 + // 遍历DeviceList
  107 + while (recordListIterator.hasNext()) {
  108 + Element itemRecord = recordListIterator.next();
  109 + Element recordElement = itemRecord.element("DeviceID");
  110 + if (recordElement == null) {
  111 + logger.info("记录为空,下一个...");
  112 + continue;
  113 + }
  114 + RecordItem record = new RecordItem();
  115 + record.setDeviceId(getText(itemRecord, "DeviceID"));
  116 + record.setName(getText(itemRecord, "Name"));
  117 + record.setFilePath(getText(itemRecord, "FilePath"));
  118 + record.setFileSize(getText(itemRecord, "FileSize"));
  119 + record.setAddress(getText(itemRecord, "Address"));
  120 +
  121 + String startTimeStr = getText(itemRecord, "StartTime");
  122 + record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr));
  123 +
  124 + String endTimeStr = getText(itemRecord, "EndTime");
  125 + record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr));
  126 +
  127 + record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
  128 + : Integer.parseInt(getText(itemRecord, "Secrecy")));
  129 + record.setType(getText(itemRecord, "Type"));
  130 + record.setRecorderId(getText(itemRecord, "RecorderID"));
  131 + recordList.add(record);
  132 + }
  133 + recordInfo.setRecordList(recordList);
  134 + // 发送消息,如果是上级查询此录像,则会通过这里通知给上级
  135 + eventPublisher.recordEndEventPush(recordInfo);
  136 + int count = recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, recordList);
  137 + logger.info("[国标录像], {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum);
  138 + }
  139 +
  140 + if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){
  141 + releaseRequest(take.getDevice().getDeviceId(), sn);
  142 + }
  143 + }
95 } 144 }
96 - RecordItem record = new RecordItem();  
97 - record.setDeviceId(getText(itemRecord, "DeviceID"));  
98 - record.setName(getText(itemRecord, "Name"));  
99 - record.setFilePath(getText(itemRecord, "FilePath"));  
100 - record.setFileSize(getText(itemRecord, "FileSize"));  
101 - record.setAddress(getText(itemRecord, "Address"));  
102 -  
103 - String startTimeStr = getText(itemRecord, "StartTime");  
104 - record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr));  
105 -  
106 - String endTimeStr = getText(itemRecord, "EndTime");  
107 - record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr));  
108 -  
109 - record.setSecrecy(itemRecord.element("Secrecy") == null ? 0  
110 - : Integer.parseInt(getText(itemRecord, "Secrecy")));  
111 - record.setType(getText(itemRecord, "Type"));  
112 - record.setRecorderId(getText(itemRecord, "RecorderID"));  
113 - recordList.add(record); 145 + taskQueueHandlerRun = false;
  146 + }catch (DocumentException e) {
  147 + throw new RuntimeException(e);
114 } 148 }
115 - int count = recordDataCatch.put(device.getDeviceId(), sn, sumNum, recordList);  
116 - logger.info("[国标录像], {}->{}: {}/{}", device.getDeviceId(), sn, count, sumNum);  
117 - }  
118 -  
119 - if (recordDataCatch.isComplete(device.getDeviceId(), sn)){  
120 - releaseRequest(device.getDeviceId(), sn);  
121 - } 149 + });
122 } 150 }
  151 +
123 } catch (SipException e) { 152 } catch (SipException e) {
124 e.printStackTrace(); 153 e.printStackTrace();
125 } catch (InvalidArgumentException e) { 154 } catch (InvalidArgumentException e) {
126 e.printStackTrace(); 155 e.printStackTrace();
127 } catch (ParseException e) { 156 } catch (ParseException e) {
128 e.printStackTrace(); 157 e.printStackTrace();
129 - } catch (DocumentException e) {  
130 - e.printStackTrace();  
131 } 158 }
132 } 159 }
133 160
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java
@@ -17,7 +17,7 @@ import javax.sip.ResponseEvent; @@ -17,7 +17,7 @@ import javax.sip.ResponseEvent;
17 @Component 17 @Component
18 public class ByeResponseProcessor extends SIPResponseProcessorAbstract { 18 public class ByeResponseProcessor extends SIPResponseProcessorAbstract {
19 19
20 - private String method = "BYE"; 20 + private final String method = "BYE";
21 21
22 @Autowired 22 @Autowired
23 private SipLayer sipLayer; 23 private SipLayer sipLayer;
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java
@@ -17,7 +17,7 @@ import javax.sip.ResponseEvent; @@ -17,7 +17,7 @@ import javax.sip.ResponseEvent;
17 @Component 17 @Component
18 public class CancelResponseProcessor extends SIPResponseProcessorAbstract { 18 public class CancelResponseProcessor extends SIPResponseProcessorAbstract {
19 19
20 - private String method = "CANCEL"; 20 + private final String method = "CANCEL";
21 21
22 @Autowired 22 @Autowired
23 private SipLayer sipLayer; 23 private SipLayer sipLayer;
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java
@@ -31,7 +31,7 @@ import java.text.ParseException; @@ -31,7 +31,7 @@ import java.text.ParseException;
31 public class InviteResponseProcessor extends SIPResponseProcessorAbstract { 31 public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
32 32
33 private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class); 33 private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
34 - private String method = "INVITE"; 34 + private final String method = "INVITE";
35 35
36 @Autowired 36 @Autowired
37 private SipLayer sipLayer; 37 private SipLayer sipLayer;
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java
@@ -27,7 +27,7 @@ import javax.sip.message.Response; @@ -27,7 +27,7 @@ import javax.sip.message.Response;
27 public class RegisterResponseProcessor extends SIPResponseProcessorAbstract { 27 public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
28 28
29 private Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class); 29 private Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class);
30 - private String method = "REGISTER"; 30 + private final String method = "REGISTER";
31 31
32 @Autowired 32 @Autowired
33 private ISIPCommanderForPlatform sipCommanderForPlatform; 33 private ISIPCommanderForPlatform sipCommanderForPlatform;
src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.utils; @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.utils;
2 2
3 import com.alibaba.fastjson.JSONArray; 3 import com.alibaba.fastjson.JSONArray;
4 import com.alibaba.fastjson.JSONObject; 4 import com.alibaba.fastjson.JSONObject;
  5 +import com.genersoft.iot.vmp.gb28181.bean.Device;
5 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; 6 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
6 import org.dom4j.Attribute; 7 import org.dom4j.Attribute;
7 import org.dom4j.Document; 8 import org.dom4j.Document;
@@ -180,7 +181,7 @@ public class XmlUtil { @@ -180,7 +181,7 @@ public class XmlUtil {
180 return xml.getRootElement(); 181 return xml.getRootElement();
181 } 182 }
182 183
183 - public static DeviceChannel channelContentHander(Element itemDevice){ 184 + public static DeviceChannel channelContentHander(Element itemDevice, Device device){
184 Element channdelNameElement = itemDevice.element("Name"); 185 Element channdelNameElement = itemDevice.element("Name");
185 String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : ""; 186 String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
186 Element statusElement = itemDevice.element("Status"); 187 Element statusElement = itemDevice.element("Status");
@@ -254,6 +255,8 @@ public class XmlUtil { @@ -254,6 +255,8 @@ public class XmlUtil {
254 }else if (deviceChannel.getChannelId().length() == 20) { 255 }else if (deviceChannel.getChannelId().length() == 20) {
255 if (Integer.parseInt(deviceChannel.getChannelId().substring(10, 13)) == 216) { // 虚拟组织 256 if (Integer.parseInt(deviceChannel.getChannelId().substring(10, 13)) == 216) { // 虚拟组织
256 deviceChannel.setParentId(businessGroupID); 257 deviceChannel.setParentId(businessGroupID);
  258 + }else if (Integer.parseInt(device.getDeviceId().substring(10, 13) )== 118) {//NVR 如果上级设备编号是NVR则直接将NVR的编号设置给通道的上级编号
  259 + deviceChannel.setParentId(device.getDeviceId());
257 }else if (deviceChannel.getCivilCode() != null) { 260 }else if (deviceChannel.getCivilCode() != null) {
258 // 设备, 无parentId的20位是使用CivilCode表示上级的设备, 261 // 设备, 无parentId的20位是使用CivilCode表示上级的设备,
259 // 注:215 业务分组是需要有parentId的 262 // 注:215 业务分组是需要有parentId的
@@ -308,6 +311,31 @@ public class XmlUtil { @@ -308,6 +311,31 @@ public class XmlUtil {
308 } else { 311 } else {
309 deviceChannel.setLatitude(0.00); 312 deviceChannel.setLatitude(0.00);
310 } 313 }
  314 + if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) {
  315 + if ("WGS84".equals(device.getGeoCoordSys())) {
  316 + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
  317 + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
  318 + Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude());
  319 + deviceChannel.setLongitudeGcj02(position[0]);
  320 + deviceChannel.setLatitudeGcj02(position[1]);
  321 + }else if ("GCJ02".equals(device.getGeoCoordSys())) {
  322 + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
  323 + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
  324 + Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude());
  325 + deviceChannel.setLongitudeWgs84(position[0]);
  326 + deviceChannel.setLatitudeWgs84(position[1]);
  327 + }else {
  328 + deviceChannel.setLongitudeGcj02(0.00);
  329 + deviceChannel.setLatitudeGcj02(0.00);
  330 + deviceChannel.setLongitudeWgs84(0.00);
  331 + deviceChannel.setLatitudeWgs84(0.00);
  332 + }
  333 + }else {
  334 + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
  335 + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
  336 + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
  337 + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
  338 + }
311 if (XmlUtil.getText(itemDevice, "PTZType") == null || "".equals(XmlUtil.getText(itemDevice, "PTZType"))) { 339 if (XmlUtil.getText(itemDevice, "PTZType") == null || "".equals(XmlUtil.getText(itemDevice, "PTZType"))) {
312 //兼容INFO中的信息 340 //兼容INFO中的信息
313 Element info = itemDevice.element("Info"); 341 Element info = itemDevice.element("Info");
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -11,7 +11,6 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; @@ -11,7 +11,6 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
11 import com.genersoft.iot.vmp.gb28181.bean.GbStream; 11 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
12 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; 12 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
13 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 13 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
14 -import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;  
15 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 14 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
16 import com.genersoft.iot.vmp.media.zlm.dto.*; 15 import com.genersoft.iot.vmp.media.zlm.dto.*;
17 import com.genersoft.iot.vmp.service.*; 16 import com.genersoft.iot.vmp.service.*;
@@ -92,10 +91,9 @@ public class ZLMHttpHookListener { @@ -92,10 +91,9 @@ public class ZLMHttpHookListener {
92 public ResponseEntity<String> onServerKeepalive(@RequestBody JSONObject json){ 91 public ResponseEntity<String> onServerKeepalive(@RequestBody JSONObject json){
93 92
94 if (logger.isDebugEnabled()) { 93 if (logger.isDebugEnabled()) {
95 - logger.debug("[ ZLM HOOK ]on_server_keepalive API调用,参数:" + json.toString()); 94 + logger.debug("[ ZLM HOOK ] on_server_keepalive API调用,参数:" + json.toString());
96 } 95 }
97 String mediaServerId = json.getString("mediaServerId"); 96 String mediaServerId = json.getString("mediaServerId");
98 -  
99 List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_keepalive); 97 List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_keepalive);
100 if (subscribes != null && subscribes.size() > 0) { 98 if (subscribes != null && subscribes.size() > 0) {
101 for (ZLMHttpHookSubscribe.Event subscribe : subscribes) { 99 for (ZLMHttpHookSubscribe.Event subscribe : subscribes) {
@@ -165,7 +163,6 @@ public class ZLMHttpHookListener { @@ -165,7 +163,6 @@ public class ZLMHttpHookListener {
165 if (mediaInfo != null) { 163 if (mediaInfo != null) {
166 subscribe.response(mediaInfo, json); 164 subscribe.response(mediaInfo, json);
167 } 165 }
168 -  
169 } 166 }
170 JSONObject ret = new JSONObject(); 167 JSONObject ret = new JSONObject();
171 ret.put("code", 0); 168 ret.put("code", 0);
@@ -248,6 +245,23 @@ public class ZLMHttpHookListener { @@ -248,6 +245,23 @@ public class ZLMHttpHookListener {
248 ret.put("msg", "success"); 245 ret.put("msg", "success");
249 return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); 246 return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
250 } 247 }
  248 + /**
  249 + * 录制hls完成后通知事件;此事件对回复不敏感。
  250 + *
  251 + */
  252 + @ResponseBody
  253 + @PostMapping(value = "/on_record_ts", produces = "application/json;charset=UTF-8")
  254 + public ResponseEntity<String> onRecordTs(@RequestBody JSONObject json){
  255 +
  256 + if (logger.isDebugEnabled()) {
  257 + logger.debug("[ ZLM HOOK ]on_record_ts API调用,参数:" + json.toString());
  258 + }
  259 + String mediaServerId = json.getString("mediaServerId");
  260 + JSONObject ret = new JSONObject();
  261 + ret.put("code", 0);
  262 + ret.put("msg", "success");
  263 + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
  264 + }
251 265
252 /** 266 /**
253 * rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。 267 * rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。
@@ -383,21 +397,22 @@ public class ZLMHttpHookListener { @@ -383,21 +397,22 @@ public class ZLMHttpHookListener {
383 if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal() 397 if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal()
384 || item.getOriginType() == OriginType.RTMP_PUSH.ordinal() 398 || item.getOriginType() == OriginType.RTMP_PUSH.ordinal()
385 || item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) { 399 || item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) {
386 - streamPushItem = zlmMediaListManager.addPush(item); 400 + item.setSeverId(userSetting.getServerId());
  401 + zlmMediaListManager.addPush(item);
387 } 402 }
388 403
389 - List<GbStream> gbStreams = new ArrayList<>();  
390 - if (streamPushItem == null || streamPushItem.getGbId() == null) {  
391 - GbStream gbStream = storager.getGbStream(app, streamId);  
392 - gbStreams.add(gbStream);  
393 - }else {  
394 - if (streamPushItem.getGbId() != null) {  
395 - gbStreams.add(streamPushItem);  
396 - }  
397 - }  
398 - if (gbStreams.size() > 0) { 404 +// List<GbStream> gbStreams = new ArrayList<>();
  405 +// if (streamPushItem == null || streamPushItem.getGbId() == null) {
  406 +// GbStream gbStream = storager.getGbStream(app, streamId);
  407 +// gbStreams.add(gbStream);
  408 +// }else {
  409 +// if (streamPushItem.getGbId() != null) {
  410 +// gbStreams.add(streamPushItem);
  411 +// }
  412 +// }
  413 +// if (gbStreams.size() > 0) {
399 // eventPublisher.catalogEventPublishForStream(null, gbStreams, CatalogEvent.ON); 414 // eventPublisher.catalogEventPublishForStream(null, gbStreams, CatalogEvent.ON);
400 - } 415 +// }
401 416
402 }else { 417 }else {
403 // 兼容流注销时类型从redis记录获取 418 // 兼容流注销时类型从redis记录获取
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
@@ -24,6 +24,9 @@ import java.util.concurrent.ConcurrentHashMap; @@ -24,6 +24,9 @@ import java.util.concurrent.ConcurrentHashMap;
24 import java.util.regex.Matcher; 24 import java.util.regex.Matcher;
25 import java.util.regex.Pattern; 25 import java.util.regex.Pattern;
26 26
  27 +/**
  28 + * @author lin
  29 + */
27 @Component 30 @Component
28 public class ZLMMediaListManager { 31 public class ZLMMediaListManager {
29 32
@@ -147,7 +150,6 @@ public class ZLMMediaListManager { @@ -147,7 +150,6 @@ public class ZLMMediaListManager {
147 } 150 }
148 } 151 }
149 } 152 }
150 - // StreamProxyItem streamProxyItem = gbStreamMapper.selectOne(transform.getApp(), transform.getStream());  
151 List<GbStream> gbStreamList = gbStreamMapper.selectByGBId(transform.getGbId()); 153 List<GbStream> gbStreamList = gbStreamMapper.selectByGBId(transform.getGbId());
152 if (gbStreamList != null && gbStreamList.size() == 1) { 154 if (gbStreamList != null && gbStreamList.size() == 1) {
153 transform.setGbStreamId(gbStreamList.get(0).getGbStreamId()); 155 transform.setGbStreamId(gbStreamList.get(0).getGbStreamId());
@@ -162,13 +164,12 @@ public class ZLMMediaListManager { @@ -162,13 +164,12 @@ public class ZLMMediaListManager {
162 } 164 }
163 if (transform != null) { 165 if (transform != null) {
164 if (channelOnlineEvents.get(transform.getGbId()) != null) { 166 if (channelOnlineEvents.get(transform.getGbId()) != null) {
165 - channelOnlineEvents.get(transform.getGbId()).run(transform.getApp(), transform.getStream()); 167 + channelOnlineEvents.get(transform.getGbId()).run(transform.getApp(), transform.getStream(), transform.getServerId());
166 channelOnlineEvents.remove(transform.getGbId()); 168 channelOnlineEvents.remove(transform.getGbId());
167 } 169 }
168 } 170 }
169 } 171 }
170 172
171 -  
172 storager.updateMedia(transform); 173 storager.updateMedia(transform);
173 return transform; 174 return transform;
174 } 175 }
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -12,6 +12,7 @@ import org.springframework.stereotype.Component; @@ -12,6 +12,7 @@ import org.springframework.stereotype.Component;
12 12
13 import java.io.*; 13 import java.io.*;
14 import java.net.ConnectException; 14 import java.net.ConnectException;
  15 +import java.net.SocketTimeoutException;
15 import java.util.HashMap; 16 import java.util.HashMap;
16 import java.util.Map; 17 import java.util.Map;
17 import java.util.Objects; 18 import java.util.Objects;
@@ -28,6 +29,9 @@ public class ZLMRESTfulUtils { @@ -28,6 +29,9 @@ public class ZLMRESTfulUtils {
28 29
29 private OkHttpClient getClient(){ 30 private OkHttpClient getClient(){
30 OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); 31 OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
  32 + //todo 暂时写死超时时间 均为5s
  33 + httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS); //设置连接超时时间
  34 + httpClientBuilder.readTimeout(5,TimeUnit.SECONDS); //设置读取超时时间
31 if (logger.isDebugEnabled()) { 35 if (logger.isDebugEnabled()) {
32 HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> { 36 HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
33 logger.debug("http请求参数:" + message); 37 logger.debug("http请求参数:" + message);
@@ -47,7 +51,10 @@ public class ZLMRESTfulUtils { @@ -47,7 +51,10 @@ public class ZLMRESTfulUtils {
47 return null; 51 return null;
48 } 52 }
49 String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api); 53 String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
50 - JSONObject responseJSON = null; 54 + JSONObject responseJSON = new JSONObject();
  55 + //-2自定义流媒体 调用错误码
  56 + responseJSON.put("code",-2);
  57 + responseJSON.put("msg","流媒体调用失败");
51 58
52 FormBody.Builder builder = new FormBody.Builder(); 59 FormBody.Builder builder = new FormBody.Builder();
53 builder.add("secret",mediaServerItem.getSecret()); 60 builder.add("secret",mediaServerItem.getSecret());
@@ -78,11 +85,20 @@ public class ZLMRESTfulUtils { @@ -78,11 +85,20 @@ public class ZLMRESTfulUtils {
78 response.close(); 85 response.close();
79 Objects.requireNonNull(response.body()).close(); 86 Objects.requireNonNull(response.body()).close();
80 } 87 }
81 - } catch (ConnectException e) {  
82 - logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage()));  
83 - logger.info("请检查media配置并确认ZLM已启动...");  
84 }catch (IOException e) { 88 }catch (IOException e) {
85 logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); 89 logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage()));
  90 +
  91 + if(e instanceof SocketTimeoutException){
  92 + //读取超时超时异常
  93 + logger.error(String.format("读取ZLM数据失败: %s, %s", url, e.getMessage()));
  94 + }
  95 + if(e instanceof ConnectException){
  96 + //判断连接异常,我这里是报Failed to connect to 10.7.5.144
  97 + logger.error(String.format("连接ZLM失败: %s, %s", url, e.getMessage()));
  98 + }
  99 +
  100 + }catch (Exception e){
  101 + logger.error(String.format("访问ZLM失败: %s, %s", url, e.getMessage()));
86 } 102 }
87 }else { 103 }else {
88 client.newCall(request).enqueue(new Callback(){ 104 client.newCall(request).enqueue(new Callback(){
@@ -105,8 +121,16 @@ public class ZLMRESTfulUtils { @@ -105,8 +121,16 @@ public class ZLMRESTfulUtils {
105 121
106 @Override 122 @Override
107 public void onFailure(@NotNull Call call, @NotNull IOException e) { 123 public void onFailure(@NotNull Call call, @NotNull IOException e) {
108 - logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage()));  
109 - logger.info("请检查media配置并确认ZLM已启动..."); 124 + logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
  125 +
  126 + if(e instanceof SocketTimeoutException){
  127 + //读取超时超时异常
  128 + logger.error(String.format("读取ZLM数据失败: %s, %s", call.request().toString(), e.getMessage()));
  129 + }
  130 + if(e instanceof ConnectException){
  131 + //判断连接异常,我这里是报Failed to connect to 10.7.5.144
  132 + logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
  133 + }
110 } 134 }
111 }); 135 });
112 } 136 }
@@ -151,7 +175,7 @@ public class ZLMRESTfulUtils { @@ -151,7 +175,7 @@ public class ZLMRESTfulUtils {
151 } 175 }
152 176
153 } 177 }
154 - File snapFile = new File(targetPath + "/" + fileName); 178 + File snapFile = new File(targetPath + File.separator + fileName);
155 FileOutputStream outStream = new FileOutputStream(snapFile); 179 FileOutputStream outStream = new FileOutputStream(snapFile);
156 180
157 outStream.write(Objects.requireNonNull(response.body()).bytes()); 181 outStream.write(Objects.requireNonNull(response.body()).bytes());
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.media.zlm; @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.media.zlm;
2 2
3 import com.alibaba.fastjson.JSONArray; 3 import com.alibaba.fastjson.JSONArray;
4 import com.alibaba.fastjson.JSONObject; 4 import com.alibaba.fastjson.JSONObject;
  5 +import com.genersoft.iot.vmp.conf.UserSetting;
5 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; 6 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
6 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 7 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
7 import org.slf4j.Logger; 8 import org.slf4j.Logger;
@@ -20,6 +21,9 @@ public class ZLMRTPServerFactory { @@ -20,6 +21,9 @@ public class ZLMRTPServerFactory {
20 @Autowired 21 @Autowired
21 private ZLMRESTfulUtils zlmresTfulUtils; 22 private ZLMRESTfulUtils zlmresTfulUtils;
22 23
  24 + @Autowired
  25 + private UserSetting userSetting;
  26 +
23 private int[] portRangeArray = new int[2]; 27 private int[] portRangeArray = new int[2];
24 28
25 public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List<Integer> usedFreelist) { 29 public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List<Integer> usedFreelist) {
@@ -87,10 +91,15 @@ public class ZLMRTPServerFactory { @@ -87,10 +91,15 @@ public class ZLMRTPServerFactory {
87 int result = -1; 91 int result = -1;
88 // 查询此rtp server 是否已经存在 92 // 查询此rtp server 是否已经存在
89 JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId); 93 JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId);
90 - if (rtpInfo != null && rtpInfo.getInteger("code") == 0 && rtpInfo.getBoolean("exist")) {  
91 - result = rtpInfo.getInteger("local_port"); 94 + if(rtpInfo.getInteger("code") == 0){
  95 + if (rtpInfo.getBoolean("exist")) {
  96 + result = rtpInfo.getInteger("local_port");
  97 + return result;
  98 + }
  99 + }else if(rtpInfo.getInteger("code") == -2){
92 return result; 100 return result;
93 } 101 }
  102 +
94 Map<String, Object> param = new HashMap<>(); 103 Map<String, Object> param = new HashMap<>();
95 // 推流端口设置0则使用随机端口 104 // 推流端口设置0则使用随机端口
96 param.put("enable_tcp", 1); 105 param.put("enable_tcp", 1);
@@ -197,6 +206,7 @@ public class ZLMRTPServerFactory { @@ -197,6 +206,7 @@ public class ZLMRTPServerFactory {
197 sendRtpItem.setTcp(tcp); 206 sendRtpItem.setTcp(tcp);
198 sendRtpItem.setApp("rtp"); 207 sendRtpItem.setApp("rtp");
199 sendRtpItem.setLocalPort(localPort); 208 sendRtpItem.setLocalPort(localPort);
  209 + sendRtpItem.setServerId(userSetting.getServerId());
200 sendRtpItem.setMediaServerId(serverItem.getId()); 210 sendRtpItem.setMediaServerId(serverItem.getId());
201 return sendRtpItem; 211 return sendRtpItem;
202 } 212 }
@@ -238,6 +248,7 @@ public class ZLMRTPServerFactory { @@ -238,6 +248,7 @@ public class ZLMRTPServerFactory {
238 sendRtpItem.setChannelId(channelId); 248 sendRtpItem.setChannelId(channelId);
239 sendRtpItem.setTcp(tcp); 249 sendRtpItem.setTcp(tcp);
240 sendRtpItem.setLocalPort(localPort); 250 sendRtpItem.setLocalPort(localPort);
  251 + sendRtpItem.setServerId(userSetting.getServerId());
241 sendRtpItem.setMediaServerId(serverItem.getId()); 252 sendRtpItem.setMediaServerId(serverItem.getId());
242 return sendRtpItem; 253 return sendRtpItem;
243 } 254 }
@@ -279,10 +290,10 @@ public class ZLMRTPServerFactory { @@ -279,10 +290,10 @@ public class ZLMRTPServerFactory {
279 */ 290 */
280 public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) { 291 public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) {
281 JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId); 292 JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
282 - Integer code = mediaInfo.getInteger("code");  
283 if (mediaInfo == null) { 293 if (mediaInfo == null) {
284 return 0; 294 return 0;
285 } 295 }
  296 + Integer code = mediaInfo.getInteger("code");
286 if ( code < 0) { 297 if ( code < 0) {
287 logger.warn("查询流({}/{})是否有其它观看者时得到: {}", app, streamId, mediaInfo.getString("msg")); 298 logger.warn("查询流({}/{})是否有其它观看者时得到: {}", app, streamId, mediaInfo.getString("msg"));
288 return -1; 299 return -1;
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ChannelOnlineEvent.java
1 package com.genersoft.iot.vmp.media.zlm.dto; 1 package com.genersoft.iot.vmp.media.zlm.dto;
2 2
  3 +/**
  4 + * @author lin
  5 + */
3 public interface ChannelOnlineEvent { 6 public interface ChannelOnlineEvent {
4 7
5 - void run(String app, String stream); 8 + void run(String app, String stream, String serverId);
6 } 9 }
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java
@@ -61,11 +61,16 @@ public class MediaItem { @@ -61,11 +61,16 @@ public class MediaItem {
61 private String originUrl; 61 private String originUrl;
62 62
63 /** 63 /**
64 - * 服务器id 64 + * 流媒体服务器id
65 */ 65 */
66 private String mediaServerId; 66 private String mediaServerId;
67 67
68 /** 68 /**
  69 + * 服务器id
  70 + */
  71 + private String severId;
  72 +
  73 + /**
69 * GMT unix系统时间戳,单位秒 74 * GMT unix系统时间戳,单位秒
70 */ 75 */
71 private Long createStamp; 76 private Long createStamp;
@@ -414,4 +419,12 @@ public class MediaItem { @@ -414,4 +419,12 @@ public class MediaItem {
414 public void setStreamInfo(StreamInfo streamInfo) { 419 public void setStreamInfo(StreamInfo streamInfo) {
415 this.streamInfo = streamInfo; 420 this.streamInfo = streamInfo;
416 } 421 }
  422 +
  423 + public String getSeverId() {
  424 + return severId;
  425 + }
  426 +
  427 + public void setSeverId(String severId) {
  428 + this.severId = severId;
  429 + }
417 } 430 }
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java
@@ -81,6 +81,11 @@ public class StreamPushItem extends GbStream implements Comparable&lt;StreamPushIte @@ -81,6 +81,11 @@ public class StreamPushItem extends GbStream implements Comparable&lt;StreamPushIte
81 */ 81 */
82 private String mediaServerId; 82 private String mediaServerId;
83 83
  84 + /**
  85 + * 使用的服务ID
  86 + */
  87 + private String serverId;
  88 +
84 public String getVhost() { 89 public String getVhost() {
85 return vhost; 90 return vhost;
86 } 91 }
@@ -219,5 +224,13 @@ public class StreamPushItem extends GbStream implements Comparable&lt;StreamPushIte @@ -219,5 +224,13 @@ public class StreamPushItem extends GbStream implements Comparable&lt;StreamPushIte
219 public void setMediaServerId(String mediaServerId) { 224 public void setMediaServerId(String mediaServerId) {
220 this.mediaServerId = mediaServerId; 225 this.mediaServerId = mediaServerId;
221 } 226 }
  227 +
  228 + public String getServerId() {
  229 + return serverId;
  230 + }
  231 +
  232 + public void setServerId(String serverId) {
  233 + this.serverId = serverId;
  234 + }
222 } 235 }
223 236
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMKeepliveTimeoutListener.java
@@ -61,13 +61,12 @@ public class ZLMKeepliveTimeoutListener extends RedisKeyExpirationEventMessageLi @@ -61,13 +61,12 @@ public class ZLMKeepliveTimeoutListener extends RedisKeyExpirationEventMessageLi
61 // 发起http请求验证zlm是否确实无法连接,如果确实无法连接则发送离线事件,否则不作处理 61 // 发起http请求验证zlm是否确实无法连接,如果确实无法连接则发送离线事件,否则不作处理
62 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); 62 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
63 JSONObject mediaServerConfig = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); 63 JSONObject mediaServerConfig = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
64 - if (mediaServerConfig == null) {  
65 - publisher.zlmOfflineEventPublish(mediaServerId);  
66 - }else { 64 + if (mediaServerConfig != null && mediaServerConfig.getInteger("code") == 0) {
67 logger.info("[zlm心跳到期]:{}验证后zlm仍在线,恢复心跳信息", mediaServerId); 65 logger.info("[zlm心跳到期]:{}验证后zlm仍在线,恢复心跳信息", mediaServerId);
68 // 添加zlm信息 66 // 添加zlm信息
69 mediaServerService.updateMediaServerKeepalive(mediaServerId, mediaServerConfig); 67 mediaServerService.updateMediaServerKeepalive(mediaServerId, mediaServerConfig);
  68 + }else {
  69 + publisher.zlmOfflineEventPublish(mediaServerId);
70 } 70 }
71 -  
72 } 71 }
73 } 72 }
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java
@@ -42,7 +42,7 @@ public class ZLMStatusEventListener { @@ -42,7 +42,7 @@ public class ZLMStatusEventListener {
42 logger.info("[ZLM] 上线 ID:" + event.getMediaServerId()); 42 logger.info("[ZLM] 上线 ID:" + event.getMediaServerId());
43 streamPushService.zlmServerOnline(event.getMediaServerId()); 43 streamPushService.zlmServerOnline(event.getMediaServerId());
44 streamProxyService.zlmServerOnline(event.getMediaServerId()); 44 streamProxyService.zlmServerOnline(event.getMediaServerId());
45 - 45 + playService.zlmServerOnline(event.getMediaServerId());
46 } 46 }
47 47
48 @Async 48 @Async
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
@@ -42,6 +42,8 @@ public interface IPlayService { @@ -42,6 +42,8 @@ public interface IPlayService {
42 42
43 StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream); 43 StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream);
44 44
  45 + void zlmServerOnline(String mediaServerId);
  46 +
45 void audioBroadcast(Device device, String channelId, int timeout, AudioBroadcastEvent event); 47 void audioBroadcast(Device device, String channelId, int timeout, AudioBroadcastEvent event);
46 void stopAudioBroadcast(String deviceId, String channelId); 48 void stopAudioBroadcast(String deviceId, String channelId);
47 } 49 }
src/main/java/com/genersoft/iot/vmp/service/StreamGPSSubscribeTask.java
@@ -23,7 +23,6 @@ public class StreamGPSSubscribeTask { @@ -23,7 +23,6 @@ public class StreamGPSSubscribeTask {
23 private IVideoManagerStorage storager; 23 private IVideoManagerStorage storager;
24 24
25 25
26 -  
27 @Scheduled(fixedRate = 30 * 1000) //每30秒执行一次 26 @Scheduled(fixedRate = 30 * 1000) //每30秒执行一次
28 public void execute(){ 27 public void execute(){
29 List<GPSMsgInfo> gpsMsgInfo = redisCatchStorage.getAllGpsMsgInfo(); 28 List<GPSMsgInfo> gpsMsgInfo = redisCatchStorage.getAllGpsMsgInfo();
src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java
1 package com.genersoft.iot.vmp.service.bean; 1 package com.genersoft.iot.vmp.service.bean;
2 2
  3 +import java.util.stream.Stream;
  4 +
3 /** 5 /**
4 * 当上级平台 6 * 当上级平台
  7 + * @author lin
5 */ 8 */
6 public class MessageForPushChannel { 9 public class MessageForPushChannel {
7 /** 10 /**
@@ -45,6 +48,20 @@ public class MessageForPushChannel { @@ -45,6 +48,20 @@ public class MessageForPushChannel {
45 */ 48 */
46 private String mediaServerId; 49 private String mediaServerId;
47 50
  51 + public static MessageForPushChannel getInstance(int type, String app, String stream, String gbId,
  52 + String platFormId, String platFormName, String serverId,
  53 + String mediaServerId){
  54 + MessageForPushChannel messageForPushChannel = new MessageForPushChannel();
  55 + messageForPushChannel.setType(type);
  56 + messageForPushChannel.setGbId(gbId);
  57 + messageForPushChannel.setApp(app);
  58 + messageForPushChannel.setStream(stream);
  59 + messageForPushChannel.setMediaServerId(mediaServerId);
  60 + messageForPushChannel.setPlatFormId(platFormId);
  61 + messageForPushChannel.setPlatFormName(platFormName);
  62 + return messageForPushChannel;
  63 + }
  64 +
48 65
49 public int getType() { 66 public int getType() {
50 return type; 67 return type;
src/main/java/com/genersoft/iot/vmp/service/bean/RequestPushStreamMsg.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.bean;
  2 +
  3 +/**
  4 + * redis消息:请求下级推送流信息
  5 + * @author lin
  6 + */
  7 +public class RequestPushStreamMsg {
  8 +
  9 +
  10 + /**
  11 + * 下级服务ID
  12 + */
  13 + private String mediaServerId;
  14 +
  15 + /**
  16 + * 流ID
  17 + */
  18 + private String app;
  19 +
  20 + /**
  21 + * 应用名
  22 + */
  23 + private String stream;
  24 +
  25 + /**
  26 + * 目标IP
  27 + */
  28 + private String ip;
  29 +
  30 + /**
  31 + * 目标端口
  32 + */
  33 + private int port;
  34 +
  35 + /**
  36 + * ssrc
  37 + */
  38 + private String ssrc;
  39 +
  40 + /**
  41 + * 是否使用TCP方式
  42 + */
  43 + private boolean tcp;
  44 +
  45 + /**
  46 + * 本地使用的端口
  47 + */
  48 + private int srcPort;
  49 +
  50 + /**
  51 + * 发送时,rtp的pt(uint8_t),不传时默认为96
  52 + */
  53 + private int pt;
  54 +
  55 + /**
  56 + * 发送时,rtp的负载类型。为true时,负载为ps;为false时,为es;
  57 + */
  58 + private boolean ps;
  59 +
  60 + /**
  61 + * 是否只有音频
  62 + */
  63 + private boolean onlyAudio;
  64 +
  65 +
  66 + public static RequestPushStreamMsg getInstance(String mediaServerId, String app, String stream, String ip, int port, String ssrc,
  67 + boolean tcp, int srcPort, int pt, boolean ps, boolean onlyAudio) {
  68 + RequestPushStreamMsg requestPushStreamMsg = new RequestPushStreamMsg();
  69 + requestPushStreamMsg.setMediaServerId(mediaServerId);
  70 + requestPushStreamMsg.setApp(app);
  71 + requestPushStreamMsg.setStream(stream);
  72 + requestPushStreamMsg.setIp(ip);
  73 + requestPushStreamMsg.setPort(port);
  74 + requestPushStreamMsg.setSsrc(ssrc);
  75 + requestPushStreamMsg.setTcp(tcp);
  76 + requestPushStreamMsg.setSrcPort(srcPort);
  77 + requestPushStreamMsg.setPt(pt);
  78 + requestPushStreamMsg.setPs(ps);
  79 + requestPushStreamMsg.setOnlyAudio(onlyAudio);
  80 + return requestPushStreamMsg;
  81 + }
  82 +
  83 + public String getMediaServerId() {
  84 + return mediaServerId;
  85 + }
  86 +
  87 + public void setMediaServerId(String mediaServerId) {
  88 + this.mediaServerId = mediaServerId;
  89 + }
  90 +
  91 + public String getApp() {
  92 + return app;
  93 + }
  94 +
  95 + public void setApp(String app) {
  96 + this.app = app;
  97 + }
  98 +
  99 + public String getStream() {
  100 + return stream;
  101 + }
  102 +
  103 + public void setStream(String stream) {
  104 + this.stream = stream;
  105 + }
  106 +
  107 + public String getIp() {
  108 + return ip;
  109 + }
  110 +
  111 + public void setIp(String ip) {
  112 + this.ip = ip;
  113 + }
  114 +
  115 + public int getPort() {
  116 + return port;
  117 + }
  118 +
  119 + public void setPort(int port) {
  120 + this.port = port;
  121 + }
  122 +
  123 + public String getSsrc() {
  124 + return ssrc;
  125 + }
  126 +
  127 + public void setSsrc(String ssrc) {
  128 + this.ssrc = ssrc;
  129 + }
  130 +
  131 + public boolean isTcp() {
  132 + return tcp;
  133 + }
  134 +
  135 + public void setTcp(boolean tcp) {
  136 + this.tcp = tcp;
  137 + }
  138 +
  139 + public int getSrcPort() {
  140 + return srcPort;
  141 + }
  142 +
  143 + public void setSrcPort(int srcPort) {
  144 + this.srcPort = srcPort;
  145 + }
  146 +
  147 + public int getPt() {
  148 + return pt;
  149 + }
  150 +
  151 + public void setPt(int pt) {
  152 + this.pt = pt;
  153 + }
  154 +
  155 + public boolean isPs() {
  156 + return ps;
  157 + }
  158 +
  159 + public void setPs(boolean ps) {
  160 + this.ps = ps;
  161 + }
  162 +
  163 + public boolean isOnlyAudio() {
  164 + return onlyAudio;
  165 + }
  166 +
  167 + public void setOnlyAudio(boolean onlyAudio) {
  168 + this.onlyAudio = onlyAudio;
  169 + }
  170 +}
src/main/java/com/genersoft/iot/vmp/service/bean/RequestSendItemMsg.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.bean;
  2 +
  3 +/**
  4 + * redis消息:请求下级回复推送信息
  5 + * @author lin
  6 + */
  7 +public class RequestSendItemMsg {
  8 +
  9 + /**
  10 + * 下级服务ID
  11 + */
  12 + private String serverId;
  13 +
  14 + /**
  15 + * 下级服务ID
  16 + */
  17 + private String mediaServerId;
  18 +
  19 + /**
  20 + * 流ID
  21 + */
  22 + private String app;
  23 +
  24 + /**
  25 + * 应用名
  26 + */
  27 + private String stream;
  28 +
  29 + /**
  30 + * 目标IP
  31 + */
  32 + private String ip;
  33 +
  34 + /**
  35 + * 目标端口
  36 + */
  37 + private int port;
  38 +
  39 + /**
  40 + * ssrc
  41 + */
  42 + private String ssrc;
  43 +
  44 + /**
  45 + * 平台国标编号
  46 + */
  47 + private String platformId;
  48 +
  49 + /**
  50 + * 平台名称
  51 + */
  52 + private String platformName;
  53 +
  54 + /**
  55 + * 通道ID
  56 + */
  57 + private String channelId;
  58 +
  59 +
  60 + /**
  61 + * 是否使用TCP
  62 + */
  63 + private Boolean isTcp;
  64 +
  65 +
  66 +
  67 +
  68 + public static RequestSendItemMsg getInstance(String serverId, String mediaServerId, String app, String stream, String ip, int port,
  69 + String ssrc, String platformId, String channelId, Boolean isTcp, String platformName) {
  70 + RequestSendItemMsg requestSendItemMsg = new RequestSendItemMsg();
  71 + requestSendItemMsg.setServerId(serverId);
  72 + requestSendItemMsg.setMediaServerId(mediaServerId);
  73 + requestSendItemMsg.setApp(app);
  74 + requestSendItemMsg.setStream(stream);
  75 + requestSendItemMsg.setIp(ip);
  76 + requestSendItemMsg.setPort(port);
  77 + requestSendItemMsg.setSsrc(ssrc);
  78 + requestSendItemMsg.setPlatformId(platformId);
  79 + requestSendItemMsg.setPlatformName(platformName);
  80 + requestSendItemMsg.setChannelId(channelId);
  81 + requestSendItemMsg.setTcp(isTcp);
  82 +
  83 + return requestSendItemMsg;
  84 + }
  85 +
  86 + public String getServerId() {
  87 + return serverId;
  88 + }
  89 +
  90 + public void setServerId(String serverId) {
  91 + this.serverId = serverId;
  92 + }
  93 +
  94 + public String getMediaServerId() {
  95 + return mediaServerId;
  96 + }
  97 +
  98 + public void setMediaServerId(String mediaServerId) {
  99 + this.mediaServerId = mediaServerId;
  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 getIp() {
  119 + return ip;
  120 + }
  121 +
  122 + public void setIp(String ip) {
  123 + this.ip = ip;
  124 + }
  125 +
  126 + public int getPort() {
  127 + return port;
  128 + }
  129 +
  130 + public void setPort(int port) {
  131 + this.port = port;
  132 + }
  133 +
  134 + public String getSsrc() {
  135 + return ssrc;
  136 + }
  137 +
  138 + public void setSsrc(String ssrc) {
  139 + this.ssrc = ssrc;
  140 + }
  141 +
  142 + public String getPlatformId() {
  143 + return platformId;
  144 + }
  145 +
  146 + public void setPlatformId(String platformId) {
  147 + this.platformId = platformId;
  148 + }
  149 +
  150 + public String getPlatformName() {
  151 + return platformName;
  152 + }
  153 +
  154 + public void setPlatformName(String platformName) {
  155 + this.platformName = platformName;
  156 + }
  157 +
  158 + public String getChannelId() {
  159 + return channelId;
  160 + }
  161 +
  162 + public void setChannelId(String channelId) {
  163 + this.channelId = channelId;
  164 + }
  165 +
  166 + public Boolean getTcp() {
  167 + return isTcp;
  168 + }
  169 +
  170 + public void setTcp(Boolean tcp) {
  171 + isTcp = tcp;
  172 + }
  173 +}
src/main/java/com/genersoft/iot/vmp/service/bean/ResponseSendItemMsg.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.bean;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
  4 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  5 +
  6 +/**
  7 + * redis消息:下级回复推送信息
  8 + * @author lin
  9 + */
  10 +public class ResponseSendItemMsg {
  11 +
  12 + private SendRtpItem sendRtpItem;
  13 +
  14 + private MediaServerItem mediaServerItem;
  15 +
  16 + public SendRtpItem getSendRtpItem() {
  17 + return sendRtpItem;
  18 + }
  19 +
  20 + public void setSendRtpItem(SendRtpItem sendRtpItem) {
  21 + this.sendRtpItem = sendRtpItem;
  22 + }
  23 +
  24 + public MediaServerItem getMediaServerItem() {
  25 + return mediaServerItem;
  26 + }
  27 +
  28 + public void setMediaServerItem(MediaServerItem mediaServerItem) {
  29 + this.mediaServerItem = mediaServerItem;
  30 + }
  31 +}
src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsg.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.bean;
  2 +
  3 +/**
  4 + * @author lin
  5 + */
  6 +public class WvpRedisMsg {
  7 +
  8 + public static WvpRedisMsg getInstance(String fromId, String toId, String type, String cmd, String serial, String content){
  9 + WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
  10 + wvpRedisMsg.setFromId(fromId);
  11 + wvpRedisMsg.setToId(toId);
  12 + wvpRedisMsg.setType(type);
  13 + wvpRedisMsg.setCmd(cmd);
  14 + wvpRedisMsg.setSerial(serial);
  15 + wvpRedisMsg.setContent(content);
  16 + return wvpRedisMsg;
  17 + }
  18 +
  19 + private String fromId;
  20 +
  21 + private String toId;
  22 + /**
  23 + * req 请求, res 回复
  24 + */
  25 + private String type;
  26 + private String cmd;
  27 +
  28 + /**
  29 + * 消息的ID
  30 + */
  31 + private String serial;
  32 + private Object content;
  33 +
  34 + private final static String requestTag = "req";
  35 + private final static String responseTag = "res";
  36 +
  37 + public static WvpRedisMsg getRequestInstance(String fromId, String toId, String cmd, String serial, Object content) {
  38 + WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
  39 + wvpRedisMsg.setType(requestTag);
  40 + wvpRedisMsg.setFromId(fromId);
  41 + wvpRedisMsg.setToId(toId);
  42 + wvpRedisMsg.setCmd(cmd);
  43 + wvpRedisMsg.setSerial(serial);
  44 + wvpRedisMsg.setContent(content);
  45 + return wvpRedisMsg;
  46 + }
  47 +
  48 + public static WvpRedisMsg getResponseInstance() {
  49 + WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
  50 + wvpRedisMsg.setType(responseTag);
  51 + return wvpRedisMsg;
  52 + }
  53 +
  54 + public static WvpRedisMsg getResponseInstance(String fromId, String toId, String cmd, String serial, Object content) {
  55 + WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
  56 + wvpRedisMsg.setType(responseTag);
  57 + wvpRedisMsg.setFromId(fromId);
  58 + wvpRedisMsg.setToId(toId);
  59 + wvpRedisMsg.setCmd(cmd);
  60 + wvpRedisMsg.setSerial(serial);
  61 + wvpRedisMsg.setContent(content);
  62 + return wvpRedisMsg;
  63 + }
  64 +
  65 + public static boolean isRequest(WvpRedisMsg wvpRedisMsg) {
  66 + return requestTag.equals(wvpRedisMsg.getType());
  67 + }
  68 +
  69 + public String getSerial() {
  70 + return serial;
  71 + }
  72 +
  73 + public void setSerial(String serial) {
  74 + this.serial = serial;
  75 + }
  76 +
  77 + public String getFromId() {
  78 + return fromId;
  79 + }
  80 +
  81 + public void setFromId(String fromId) {
  82 + this.fromId = fromId;
  83 + }
  84 +
  85 + public String getToId() {
  86 + return toId;
  87 + }
  88 +
  89 + public void setToId(String toId) {
  90 + this.toId = toId;
  91 + }
  92 +
  93 + public String getType() {
  94 + return type;
  95 + }
  96 +
  97 + public void setType(String type) {
  98 + this.type = type;
  99 + }
  100 +
  101 + public String getCmd() {
  102 + return cmd;
  103 + }
  104 +
  105 + public void setCmd(String cmd) {
  106 + this.cmd = cmd;
  107 + }
  108 +
  109 + public Object getContent() {
  110 + return content;
  111 + }
  112 +
  113 + public void setContent(Object content) {
  114 + this.content = content;
  115 + }
  116 +}
src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsgCmd.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.bean;
  2 +
  3 +/**
  4 + * @author lin
  5 + */
  6 +
  7 +public class WvpRedisMsgCmd {
  8 +
  9 + public static final String GET_SEND_ITEM = "GetSendItem";
  10 + public static final String REQUEST_PUSH_STREAM = "RequestPushStream";
  11 +
  12 +}
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
@@ -2,16 +2,21 @@ package com.genersoft.iot.vmp.service.impl; @@ -2,16 +2,21 @@ package com.genersoft.iot.vmp.service.impl;
2 2
3 import com.genersoft.iot.vmp.conf.DynamicTask; 3 import com.genersoft.iot.vmp.conf.DynamicTask;
4 import com.genersoft.iot.vmp.gb28181.bean.Device; 4 import com.genersoft.iot.vmp.gb28181.bean.Device;
  5 +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
5 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; 6 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
6 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 7 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  8 +import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
7 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; 9 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; 10 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler;
  11 +import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
9 import com.genersoft.iot.vmp.service.IDeviceService; 12 import com.genersoft.iot.vmp.service.IDeviceService;
10 import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; 13 import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;
11 import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; 14 import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
12 import com.genersoft.iot.vmp.gb28181.bean.SyncStatus; 15 import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
13 import com.genersoft.iot.vmp.service.IMediaServerService; 16 import com.genersoft.iot.vmp.service.IMediaServerService;
14 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 17 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  18 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  19 +import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
15 import com.genersoft.iot.vmp.storager.dao.DeviceMapper; 20 import com.genersoft.iot.vmp.storager.dao.DeviceMapper;
16 import com.genersoft.iot.vmp.utils.DateUtil; 21 import com.genersoft.iot.vmp.utils.DateUtil;
17 import org.slf4j.Logger; 22 import org.slf4j.Logger;
@@ -50,6 +55,12 @@ public class DeviceServiceImpl implements IDeviceService { @@ -50,6 +55,12 @@ public class DeviceServiceImpl implements IDeviceService {
50 private DeviceMapper deviceMapper; 55 private DeviceMapper deviceMapper;
51 56
52 @Autowired 57 @Autowired
  58 + private DeviceChannelMapper deviceChannelMapper;
  59 +
  60 + @Autowired
  61 + private IVideoManagerStorage storage;
  62 +
  63 + @Autowired
53 private ISIPCommander commander; 64 private ISIPCommander commander;
54 65
55 @Autowired 66 @Autowired
@@ -68,7 +79,6 @@ public class DeviceServiceImpl implements IDeviceService { @@ -68,7 +79,6 @@ public class DeviceServiceImpl implements IDeviceService {
68 if (deviceInRedis != null && deviceInDb == null) { 79 if (deviceInRedis != null && deviceInDb == null) {
69 // redis 存在脏数据 80 // redis 存在脏数据
70 redisCatchStorage.clearCatchByDeviceId(device.getDeviceId()); 81 redisCatchStorage.clearCatchByDeviceId(device.getDeviceId());
71 -  
72 } 82 }
73 device.setUpdateTime(now); 83 device.setUpdateTime(now);
74 device.setOnline(1); 84 device.setOnline(1);
@@ -77,13 +87,15 @@ public class DeviceServiceImpl implements IDeviceService { @@ -77,13 +87,15 @@ public class DeviceServiceImpl implements IDeviceService {
77 if (device.getCreateTime() == null) { 87 if (device.getCreateTime() == null) {
78 device.setCreateTime(now); 88 device.setCreateTime(now);
79 logger.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId()); 89 logger.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId());
  90 + deviceMapper.add(device);
  91 + redisCatchStorage.updateDevice(device);
80 commander.deviceInfoQuery(device); 92 commander.deviceInfoQuery(device);
81 sync(device); 93 sync(device);
82 - deviceMapper.add(device);  
83 }else { 94 }else {
84 deviceMapper.update(device); 95 deviceMapper.update(device);
  96 + redisCatchStorage.updateDevice(device);
85 } 97 }
86 - redisCatchStorage.updateDevice(device); 98 +
87 // 上线添加订阅 99 // 上线添加订阅
88 if (device.getSubscribeCycleForCatalog() > 0) { 100 if (device.getSubscribeCycleForCatalog() > 0) {
89 // 查询在线设备那些开启了订阅,为设备开启定时的目录订阅 101 // 查询在线设备那些开启了订阅,为设备开启定时的目录订阅
@@ -94,7 +106,6 @@ public class DeviceServiceImpl implements IDeviceService { @@ -94,7 +106,6 @@ public class DeviceServiceImpl implements IDeviceService {
94 } 106 }
95 // 刷新过期任务 107 // 刷新过期任务
96 String registerExpireTaskKey = registerExpireTaskKeyPrefix + device.getDeviceId(); 108 String registerExpireTaskKey = registerExpireTaskKeyPrefix + device.getDeviceId();
97 - dynamicTask.stop(registerExpireTaskKey);  
98 dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId()), device.getExpires() * 1000); 109 dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId()), device.getExpires() * 1000);
99 } 110 }
100 111
@@ -143,8 +154,16 @@ public class DeviceServiceImpl implements IDeviceService { @@ -143,8 +154,16 @@ public class DeviceServiceImpl implements IDeviceService {
143 if (device == null || device.getSubscribeCycleForCatalog() < 0) { 154 if (device == null || device.getSubscribeCycleForCatalog() < 0) {
144 return false; 155 return false;
145 } 156 }
146 - logger.info("移除目录订阅: {}", device.getDeviceId());  
147 - dynamicTask.stop(device.getDeviceId() + "catalog"); 157 + logger.info("[移除目录订阅]: {}", device.getDeviceId());
  158 + String taskKey = device.getDeviceId() + "catalog";
  159 + if (device.getOnline() == 1) {
  160 + Runnable runnable = dynamicTask.get(taskKey);
  161 + if (runnable instanceof ISubscribeTask) {
  162 + ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
  163 + subscribeTask.stop();
  164 + }
  165 + }
  166 + dynamicTask.stop(taskKey);
148 return true; 167 return true;
149 } 168 }
150 169
@@ -168,8 +187,16 @@ public class DeviceServiceImpl implements IDeviceService { @@ -168,8 +187,16 @@ public class DeviceServiceImpl implements IDeviceService {
168 if (device == null || device.getSubscribeCycleForCatalog() < 0) { 187 if (device == null || device.getSubscribeCycleForCatalog() < 0) {
169 return false; 188 return false;
170 } 189 }
171 - logger.info("移除移动位置订阅: {}", device.getDeviceId());  
172 - dynamicTask.stop(device.getDeviceId() + "mobile_position"); 190 + logger.info("[移除移动位置订阅]: {}", device.getDeviceId());
  191 + String taskKey = device.getDeviceId() + "mobile_position";
  192 + if (device.getOnline() == 1) {
  193 + Runnable runnable = dynamicTask.get(taskKey);
  194 + if (runnable instanceof ISubscribeTask) {
  195 + ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
  196 + subscribeTask.stop();
  197 + }
  198 + }
  199 + dynamicTask.stop(taskKey);
173 return true; 200 return true;
174 } 201 }
175 202
@@ -275,6 +302,10 @@ public class DeviceServiceImpl implements IDeviceService { @@ -275,6 +302,10 @@ public class DeviceServiceImpl implements IDeviceService {
275 removeMobilePositionSubscribe(deviceInStore); 302 removeMobilePositionSubscribe(deviceInStore);
276 } 303 }
277 } 304 }
  305 + // 坐标系变化,需要重新计算GCJ02坐标和WGS84坐标
  306 + if (!deviceInStore.getGeoCoordSys().equals(device.getGeoCoordSys())) {
  307 + updateDeviceChannelGeoCoordSys(device);
  308 + }
278 309
279 String now = DateUtil.getNow(); 310 String now = DateUtil.getNow();
280 device.setUpdateTime(now); 311 device.setUpdateTime(now);
@@ -282,6 +313,32 @@ public class DeviceServiceImpl implements IDeviceService { @@ -282,6 +313,32 @@ public class DeviceServiceImpl implements IDeviceService {
282 device.setUpdateTime(DateUtil.getNow()); 313 device.setUpdateTime(DateUtil.getNow());
283 if (deviceMapper.update(device) > 0) { 314 if (deviceMapper.update(device) > 0) {
284 redisCatchStorage.updateDevice(device); 315 redisCatchStorage.updateDevice(device);
  316 +
285 } 317 }
286 } 318 }
  319 +
  320 + /**
  321 + * 更新通道坐标系
  322 + */
  323 + private void updateDeviceChannelGeoCoordSys(Device device) {
  324 + List<DeviceChannel> deviceChannels = deviceChannelMapper.getAllChannelWithCoordinate(device.getDeviceId());
  325 + if (deviceChannels.size() > 0) {
  326 + for (DeviceChannel deviceChannel : deviceChannels) {
  327 + if ("WGS84".equals(device.getGeoCoordSys())) {
  328 + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
  329 + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
  330 + Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude());
  331 + deviceChannel.setLongitudeGcj02(position[0]);
  332 + deviceChannel.setLatitudeGcj02(position[1]);
  333 + }else if ("GCJ02".equals(device.getGeoCoordSys())) {
  334 + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
  335 + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
  336 + Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude());
  337 + deviceChannel.setLongitudeWgs84(position[0]);
  338 + deviceChannel.setLatitudeWgs84(position[1]);
  339 + }
  340 + }
  341 + }
  342 + storage.updateChannels(device.getDeviceId(), deviceChannels);
  343 + }
287 } 344 }
src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
@@ -106,7 +106,8 @@ public class GbStreamServiceImpl implements IGbStreamService { @@ -106,7 +106,8 @@ public class GbStreamServiceImpl implements IGbStreamService {
106 deviceChannel.setStatus(1); 106 deviceChannel.setStatus(1);
107 deviceChannel.setParentId(catalogId ==null?gbStream.getCatalogId():catalogId); 107 deviceChannel.setParentId(catalogId ==null?gbStream.getCatalogId():catalogId);
108 deviceChannel.setRegisterWay(1); 108 deviceChannel.setRegisterWay(1);
109 - if (catalogId.length() <= 10) { // 父节点是行政区划,则设置CivilCode使用此行政区划 109 + if (catalogId.length() > 0 && catalogId.length() <= 10) {
  110 + // 父节点是行政区划,则设置CivilCode使用此行政区划
110 deviceChannel.setCivilCode(catalogId); 111 deviceChannel.setCivilCode(catalogId);
111 }else { 112 }else {
112 deviceChannel.setCivilCode(platform.getAdministrativeDivision()); 113 deviceChannel.setCivilCode(platform.getAdministrativeDivision());
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSONObject; @@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSONObject;
6 import com.genersoft.iot.vmp.common.VideoManagerConstants; 6 import com.genersoft.iot.vmp.common.VideoManagerConstants;
7 import com.genersoft.iot.vmp.conf.SipConfig; 7 import com.genersoft.iot.vmp.conf.SipConfig;
8 import com.genersoft.iot.vmp.conf.UserSetting; 8 import com.genersoft.iot.vmp.conf.UserSetting;
  9 +import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
9 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 10 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
10 import com.genersoft.iot.vmp.gb28181.session.SsrcConfig; 11 import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
11 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 12 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
@@ -35,7 +36,9 @@ import org.springframework.util.StringUtils; @@ -35,7 +36,9 @@ import org.springframework.util.StringUtils;
35 36
36 import java.text.ParseException; 37 import java.text.ParseException;
37 import java.text.SimpleDateFormat; 38 import java.text.SimpleDateFormat;
  39 +import java.time.LocalDateTime;
38 import java.util.*; 40 import java.util.*;
  41 +import java.util.stream.Collectors;
39 42
40 /** 43 /**
41 * 媒体服务器节点管理 44 * 媒体服务器节点管理
@@ -189,6 +192,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -189,6 +192,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
189 public void clearRTPServer(MediaServerItem mediaServerItem) { 192 public void clearRTPServer(MediaServerItem mediaServerItem) {
190 mediaServerItem.setSsrcConfig(new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getDomain())); 193 mediaServerItem.setSsrcConfig(new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getDomain()));
191 redisUtil.zAdd(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(), mediaServerItem.getId(), 0); 194 redisUtil.zAdd(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(), mediaServerItem.getId(), 0);
  195 +
192 } 196 }
193 197
194 198
@@ -229,11 +233,10 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -229,11 +233,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
229 } 233 }
230 result.sort((serverItem1, serverItem2)->{ 234 result.sort((serverItem1, serverItem2)->{
231 int sortResult = 0; 235 int sortResult = 0;
232 - try {  
233 - sortResult = DateUtil.format.parse(serverItem1.getCreateTime()).compareTo(DateUtil.format.parse(serverItem2.getCreateTime()));  
234 - } catch (ParseException e) {  
235 - e.printStackTrace();  
236 - } 236 + LocalDateTime localDateTime1 = LocalDateTime.parse(serverItem1.getCreateTime(), DateUtil.formatter);
  237 + LocalDateTime localDateTime2 = LocalDateTime.parse(serverItem2.getCreateTime(), DateUtil.formatter);
  238 +
  239 + sortResult = localDateTime1.compareTo(localDateTime2);
237 return sortResult; 240 return sortResult;
238 }); 241 });
239 return result; 242 return result;
@@ -495,14 +498,14 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -495,14 +498,14 @@ public class MediaServerServiceImpl implements IMediaServerService {
495 param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline 498 param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
496 param.put("ffmpeg.cmd","%s -fflags nobuffer -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"); 499 param.put("ffmpeg.cmd","%s -fflags nobuffer -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s");
497 param.put("hook.enable","1"); 500 param.put("hook.enable","1");
498 - param.put("hook.on_flow_report",""); 501 + param.put("hook.on_flow_report",String.format("%s/on_flow_report", hookPrex));
499 param.put("hook.on_play",String.format("%s/on_play", hookPrex)); 502 param.put("hook.on_play",String.format("%s/on_play", hookPrex));
500 - param.put("hook.on_http_access",""); 503 + param.put("hook.on_http_access",String.format("%s/on_http_access", hookPrex));
501 param.put("hook.on_publish", String.format("%s/on_publish", hookPrex)); 504 param.put("hook.on_publish", String.format("%s/on_publish", hookPrex));
502 param.put("hook.on_record_mp4",recordHookPrex != null? String.format("%s/on_record_mp4", recordHookPrex): ""); 505 param.put("hook.on_record_mp4",recordHookPrex != null? String.format("%s/on_record_mp4", recordHookPrex): "");
503 - param.put("hook.on_record_ts","");  
504 - param.put("hook.on_rtsp_auth","");  
505 - param.put("hook.on_rtsp_realm",""); 506 + param.put("hook.on_record_ts",String.format("%s/on_record_ts", hookPrex));
  507 + param.put("hook.on_rtsp_auth",String.format("%s/on_rtsp_auth", hookPrex));
  508 + param.put("hook.on_rtsp_realm",String.format("%s/on_rtsp_realm", hookPrex));
506 param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex)); 509 param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex));
507 param.put("hook.on_shell_login",String.format("%s/on_shell_login", hookPrex)); 510 param.put("hook.on_shell_login",String.format("%s/on_shell_login", hookPrex));
508 param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex)); 511 param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex));
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -21,6 +21,8 @@ import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; @@ -21,6 +21,8 @@ import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
21 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; 21 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
22 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 22 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
23 import com.genersoft.iot.vmp.service.IMediaServerService; 23 import com.genersoft.iot.vmp.service.IMediaServerService;
  24 +import com.genersoft.iot.vmp.service.IMediaService;
  25 +import com.genersoft.iot.vmp.service.IPlayService;
24 import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; 26 import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
25 import com.genersoft.iot.vmp.service.bean.PlayBackCallback; 27 import com.genersoft.iot.vmp.service.bean.PlayBackCallback;
26 import com.genersoft.iot.vmp.service.bean.PlayBackResult; 28 import com.genersoft.iot.vmp.service.bean.PlayBackResult;
@@ -32,8 +34,6 @@ import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; @@ -32,8 +34,6 @@ import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
32 import com.genersoft.iot.vmp.vmanager.bean.WVPResult; 34 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
33 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; 35 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
34 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; 36 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
35 -import com.genersoft.iot.vmp.service.IMediaService;  
36 -import com.genersoft.iot.vmp.service.IPlayService;  
37 import gov.nist.javax.sip.stack.SIPDialog; 37 import gov.nist.javax.sip.stack.SIPDialog;
38 import org.slf4j.Logger; 38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory; 39 import org.slf4j.LoggerFactory;
@@ -49,6 +49,7 @@ import javax.sip.SipException; @@ -49,6 +49,7 @@ import javax.sip.SipException;
49 import java.io.FileNotFoundException; 49 import java.io.FileNotFoundException;
50 import java.math.BigDecimal; 50 import java.math.BigDecimal;
51 import java.text.ParseException; 51 import java.text.ParseException;
  52 +import java.math.RoundingMode;
52 import java.util.*; 53 import java.util.*;
53 import java.util.stream.Collectors; 54 import java.util.stream.Collectors;
54 import java.util.stream.Stream; 55 import java.util.stream.Stream;
@@ -75,9 +76,6 @@ public class PlayServiceImpl implements IPlayService { @@ -75,9 +76,6 @@ public class PlayServiceImpl implements IPlayService {
75 private IRedisCatchStorage redisCatchStorage; 76 private IRedisCatchStorage redisCatchStorage;
76 77
77 @Autowired 78 @Autowired
78 - private RedisUtil redis;  
79 -  
80 - @Autowired  
81 private DeferredResultHolder resultHolder; 79 private DeferredResultHolder resultHolder;
82 80
83 @Autowired 81 @Autowired
@@ -140,36 +138,19 @@ public class PlayServiceImpl implements IPlayService { @@ -140,36 +138,19 @@ public class PlayServiceImpl implements IPlayService {
140 result.onCompletion(()->{ 138 result.onCompletion(()->{
141 // 点播结束时调用截图接口 139 // 点播结束时调用截图接口
142 // TODO 应该在上流时调用更好,结束也可能是错误结束 140 // TODO 应该在上流时调用更好,结束也可能是错误结束
143 - try {  
144 - String classPath = ResourceUtils.getURL("classpath:").getPath();  
145 - // 兼容打包为jar的class路径  
146 - if(classPath.contains("jar")) {  
147 - classPath = classPath.substring(0, classPath.lastIndexOf("."));  
148 - classPath = classPath.substring(0, classPath.lastIndexOf("/") + 1);  
149 - }  
150 - if (classPath.startsWith("file:")) {  
151 - classPath = classPath.substring(classPath.indexOf(":") + 1); 141 + String path = "static/static/snap/";
  142 + String fileName = deviceId + "_" + channelId + ".jpg";
  143 + ResponseEntity responseEntity = (ResponseEntity)result.getResult();
  144 + if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
  145 + WVPResult wvpResult = (WVPResult)responseEntity.getBody();
  146 + if (Objects.requireNonNull(wvpResult).getCode() == 0) {
  147 + StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
  148 + MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
  149 + String streamUrl = streamInfoForSuccess.getFmp4();
  150 + // 请求截图
  151 + logger.info("[请求截图]: " + fileName);
  152 + zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName);
152 } 153 }
153 - String path = classPath + "static/static/snap/";  
154 - // 兼容Windows系统路径(去除前面的“/”)  
155 - if(System.getProperty("os.name").contains("indows")) {  
156 - path = path.substring(1);  
157 - }  
158 - String fileName = deviceId + "_" + channelId + ".jpg";  
159 - ResponseEntity responseEntity = (ResponseEntity)result.getResult();  
160 - if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {  
161 - WVPResult wvpResult = (WVPResult)responseEntity.getBody();  
162 - if (Objects.requireNonNull(wvpResult).getCode() == 0) {  
163 - StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();  
164 - MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());  
165 - String streamUrl = streamInfoForSuccess.getFmp4();  
166 - // 请求截图  
167 - logger.info("[请求截图]: " + fileName);  
168 - zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName);  
169 - }  
170 - }  
171 - } catch (FileNotFoundException e) {  
172 - e.printStackTrace();  
173 } 154 }
174 }); 155 });
175 if (streamInfo != null) { 156 if (streamInfo != null) {
@@ -186,24 +167,33 @@ public class PlayServiceImpl implements IPlayService { @@ -186,24 +167,33 @@ public class PlayServiceImpl implements IPlayService {
186 MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); 167 MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
187 168
188 JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); 169 JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId);
189 - if (rtpInfo != null && rtpInfo.getBoolean("exist")) { 170 + if(rtpInfo.getInteger("code") == 0){
  171 + if (rtpInfo.getBoolean("exist")) {
190 172
191 - WVPResult wvpResult = new WVPResult();  
192 - wvpResult.setCode(0);  
193 - wvpResult.setMsg("success");  
194 - wvpResult.setData(streamInfo);  
195 - msg.setData(wvpResult); 173 + WVPResult wvpResult = new WVPResult();
  174 + wvpResult.setCode(0);
  175 + wvpResult.setMsg("success");
  176 + wvpResult.setData(streamInfo);
  177 + msg.setData(wvpResult);
196 178
197 - resultHolder.invokeAllResult(msg);  
198 - if (hookEvent != null) {  
199 - hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo))); 179 + resultHolder.invokeAllResult(msg);
  180 + if (hookEvent != null) {
  181 + hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo)));
  182 + }
  183 + }else {
  184 + redisCatchStorage.stopPlay(streamInfo);
  185 + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
  186 + streamInfo = null;
200 } 187 }
201 }else { 188 }else {
  189 + //zlm连接失败
202 redisCatchStorage.stopPlay(streamInfo); 190 redisCatchStorage.stopPlay(streamInfo);
203 storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); 191 storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
204 streamInfo = null; 192 streamInfo = null;
  193 +
205 } 194 }
206 195
  196 +
207 } 197 }
208 if (streamInfo == null) { 198 if (streamInfo == null) {
209 String streamId = null; 199 String streamId = null;
@@ -256,33 +246,41 @@ public class PlayServiceImpl implements IPlayService { @@ -256,33 +246,41 @@ public class PlayServiceImpl implements IPlayService {
256 if (ssrcInfo == null) { 246 if (ssrcInfo == null) {
257 ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); 247 ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false);
258 } 248 }
259 - 249 + logger.info("[点播开始] deviceId: {}, channelId: {}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getSsrc() );
260 // 超时处理 250 // 超时处理
261 String timeOutTaskKey = UUID.randomUUID().toString(); 251 String timeOutTaskKey = UUID.randomUUID().toString();
262 SSRCInfo finalSsrcInfo = ssrcInfo; 252 SSRCInfo finalSsrcInfo = ssrcInfo;
263 dynamicTask.startDelay( timeOutTaskKey,()->{ 253 dynamicTask.startDelay( timeOutTaskKey,()->{
264 - logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", device.getDeviceId(), channelId));  
265 254
266 SIPDialog dialog = streamSession.getDialogByStream(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); 255 SIPDialog dialog = streamSession.getDialogByStream(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
267 if (dialog != null) { 256 if (dialog != null) {
  257 + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
268 timeoutCallback.run(1, "收流超时"); 258 timeoutCallback.run(1, "收流超时");
269 // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 259 // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
270 cmder.streamByeCmd(device.getDeviceId(), channelId, finalSsrcInfo.getStream(), null); 260 cmder.streamByeCmd(device.getDeviceId(), channelId, finalSsrcInfo.getStream(), null);
271 }else { 261 }else {
  262 + logger.info("[点播超时] 消息未响应 deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
272 timeoutCallback.run(0, "点播超时"); 263 timeoutCallback.run(0, "点播超时");
273 mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); 264 mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
274 mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); 265 mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
275 streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); 266 streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
276 } 267 }
277 - }, userSetting.getPlayTimeout()*1000); 268 + }, userSetting.getPlayTimeout());
278 final String ssrc = ssrcInfo.getSsrc(); 269 final String ssrc = ssrcInfo.getSsrc();
279 final String stream = ssrcInfo.getStream(); 270 final String stream = ssrcInfo.getStream();
  271 + //端口获取失败的ssrcInfo 没有必要发送点播指令
  272 + if(ssrcInfo.getPort() <= 0){
  273 + logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo);
  274 + return;
  275 + }
280 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { 276 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
281 logger.info("收到订阅消息: " + response.toJSONString()); 277 logger.info("收到订阅消息: " + response.toJSONString());
282 dynamicTask.stop(timeOutTaskKey); 278 dynamicTask.stop(timeOutTaskKey);
283 // hook响应 279 // hook响应
284 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid); 280 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid);
285 hookEvent.response(mediaServerItemInuse, response); 281 hookEvent.response(mediaServerItemInuse, response);
  282 + logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
  283 +
286 }, (event) -> { 284 }, (event) -> {
287 ResponseEvent responseEvent = (ResponseEvent)event.event; 285 ResponseEvent responseEvent = (ResponseEvent)event.event;
288 String contentString = new String(responseEvent.getResponse().getRawContent()); 286 String contentString = new String(responseEvent.getResponse().getRawContent());
@@ -296,8 +294,10 @@ public class PlayServiceImpl implements IPlayService { @@ -296,8 +294,10 @@ public class PlayServiceImpl implements IPlayService {
296 if (ssrc.equals(ssrcInResponse)) { 294 if (ssrc.equals(ssrcInResponse)) {
297 return; 295 return;
298 } 296 }
299 - logger.info("[SIP 消息] 收到invite 200, 发现下级自定义了ssrc 开启修正"); 297 + logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse );
300 if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { 298 if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
  299 + logger.info("[SIP 消息] SSRC修正 {}->{}", ssrc, ssrcInResponse);
  300 +
301 if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) { 301 if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
302 // ssrc 不可用 302 // ssrc 不可用
303 // 释放ssrc 303 // 释放ssrc
@@ -450,7 +450,7 @@ public class PlayServiceImpl implements IPlayService { @@ -450,7 +450,7 @@ public class PlayServiceImpl implements IPlayService {
450 cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null); 450 cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
451 // 回复之前所有的点播请求 451 // 回复之前所有的点播请求
452 playBackCallback.call(playBackResult); 452 playBackCallback.call(playBackResult);
453 - }, userSetting.getPlayTimeout()*1000); 453 + }, userSetting.getPlayTimeout());
454 454
455 cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack, 455 cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack,
456 (InviteStreamInfo inviteStreamInfo) -> { 456 (InviteStreamInfo inviteStreamInfo) -> {
@@ -539,7 +539,7 @@ public class PlayServiceImpl implements IPlayService { @@ -539,7 +539,7 @@ public class PlayServiceImpl implements IPlayService {
539 cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null); 539 cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
540 // 回复之前所有的点播请求 540 // 回复之前所有的点播请求
541 hookCallBack.call(downloadResult); 541 hookCallBack.call(downloadResult);
542 - }, userSetting.getPlayTimeout()*1000); 542 + }, userSetting.getPlayTimeout());
543 cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack, 543 cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
544 inviteStreamInfo -> { 544 inviteStreamInfo -> {
545 logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString()); 545 logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
@@ -605,7 +605,7 @@ public class PlayServiceImpl implements IPlayService { @@ -605,7 +605,7 @@ public class PlayServiceImpl implements IPlayService {
605 605
606 BigDecimal currentCount = new BigDecimal(duration/1000); 606 BigDecimal currentCount = new BigDecimal(duration/1000);
607 BigDecimal totalCount = new BigDecimal(end-start); 607 BigDecimal totalCount = new BigDecimal(end-start);
608 - BigDecimal divide = currentCount.divide(totalCount,2, BigDecimal.ROUND_HALF_UP); 608 + BigDecimal divide = currentCount.divide(totalCount,2, RoundingMode.HALF_UP);
609 double process = divide.doubleValue(); 609 double process = divide.doubleValue();
610 streamInfo.setProgress(process); 610 streamInfo.setProgress(process);
611 } 611 }
@@ -728,4 +728,9 @@ public class PlayServiceImpl implements IPlayService { @@ -728,4 +728,9 @@ public class PlayServiceImpl implements IPlayService {
728 728
729 729
730 } 730 }
  731 +
  732 + @Override
  733 + public void zlmServerOnline(String mediaServerId) {
  734 + // 似乎没啥需要做的
  735 + }
731 } 736 }
src/main/java/com/genersoft/iot/vmp/service/impl/RedisGbPlayMsgListener.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.impl;
  2 +
  3 +import com.alibaba.fastjson.JSON;
  4 +import com.alibaba.fastjson.JSONObject;
  5 +import com.genersoft.iot.vmp.conf.DynamicTask;
  6 +import com.genersoft.iot.vmp.conf.UserSetting;
  7 +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
  8 +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
  9 +import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
  10 +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
  11 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  12 +import com.genersoft.iot.vmp.service.IMediaServerService;
  13 +import com.genersoft.iot.vmp.service.bean.*;
  14 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  15 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
  16 +import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
  17 +import org.slf4j.Logger;
  18 +import org.slf4j.LoggerFactory;
  19 +import org.springframework.beans.factory.annotation.Autowired;
  20 +import org.springframework.data.redis.connection.Message;
  21 +import org.springframework.data.redis.connection.MessageListener;
  22 +import org.springframework.stereotype.Component;
  23 +
  24 +import javax.sip.InvalidArgumentException;
  25 +import javax.sip.SipException;
  26 +import java.text.ParseException;
  27 +import java.util.HashMap;
  28 +import java.util.Map;
  29 +import java.util.UUID;
  30 +import java.util.concurrent.ConcurrentHashMap;
  31 +
  32 +
  33 +/**
  34 + * 监听下级发送推送信息,并发送国标推流消息上级
  35 + * @author lin
  36 + */
  37 +@Component
  38 +public class RedisGbPlayMsgListener implements MessageListener {
  39 +
  40 + private final static Logger logger = LoggerFactory.getLogger(RedisGbPlayMsgListener.class);
  41 +
  42 + public static final String WVP_PUSH_STREAM_KEY = "WVP_PUSH_STREAM";
  43 +
  44 + /**
  45 + * 流媒体不存在的错误玛
  46 + */
  47 + public static final int ERROR_CODE_MEDIA_SERVER_NOT_FOUND = -1;
  48 +
  49 + /**
  50 + * 离线的错误玛
  51 + */
  52 + public static final int ERROR_CODE_OFFLINE = -2;
  53 +
  54 + /**
  55 + * 超时的错误玛
  56 + */
  57 + public static final int ERROR_CODE_TIMEOUT = -3;
  58 +
  59 + private Map<String, PlayMsgCallback> callbacks = new ConcurrentHashMap<>();
  60 + private Map<String, PlayMsgCallbackForStartSendRtpStream> callbacksForStartSendRtpStream = new ConcurrentHashMap<>();
  61 + private Map<String, PlayMsgErrorCallback> callbacksForError = new ConcurrentHashMap<>();
  62 +
  63 + @Autowired
  64 + private UserSetting userSetting;
  65 +
  66 + @Autowired
  67 + private RedisUtil redis;
  68 +
  69 + @Autowired
  70 + private ZLMMediaListManager zlmMediaListManager;
  71 +
  72 + @Autowired
  73 + private ZLMRTPServerFactory zlmrtpServerFactory;
  74 +
  75 + @Autowired
  76 + private IMediaServerService mediaServerService;
  77 +
  78 + @Autowired
  79 + private IRedisCatchStorage redisCatchStorage;
  80 +
  81 + @Autowired
  82 + private DynamicTask dynamicTask;
  83 +
  84 + @Autowired
  85 + private ZLMMediaListManager mediaListManager;
  86 +
  87 + @Autowired
  88 + private ZLMHttpHookSubscribe subscribe;
  89 +
  90 +
  91 + public interface PlayMsgCallback{
  92 + void handler(ResponseSendItemMsg responseSendItemMsg);
  93 + }
  94 +
  95 + public interface PlayMsgCallbackForStartSendRtpStream{
  96 + void handler(JSONObject jsonObject);
  97 + }
  98 +
  99 + public interface PlayMsgErrorCallback{
  100 + void handler(WVPResult wvpResult);
  101 + }
  102 +
  103 + @Override
  104 + public void onMessage(Message message, byte[] bytes) {
  105 + JSONObject msgJSON = JSON.parseObject(message.getBody(), JSONObject.class);
  106 + WvpRedisMsg wvpRedisMsg = JSON.toJavaObject(msgJSON, WvpRedisMsg.class);
  107 + if (!userSetting.getServerId().equals(wvpRedisMsg.getToId())) {
  108 + return;
  109 + }
  110 + if (WvpRedisMsg.isRequest(wvpRedisMsg)) {
  111 + logger.info("[收到REDIS通知] 请求: {}", new String(message.getBody()));
  112 +
  113 + switch (wvpRedisMsg.getCmd()){
  114 + case WvpRedisMsgCmd.GET_SEND_ITEM:
  115 + RequestSendItemMsg content = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), RequestSendItemMsg.class);
  116 + requestSendItemMsgHand(content, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
  117 + break;
  118 + case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
  119 + RequestPushStreamMsg param = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), RequestPushStreamMsg.class);;
  120 + requestPushStreamMsgHand(param, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
  121 + break;
  122 + default:
  123 + break;
  124 + }
  125 +
  126 + }else {
  127 + logger.info("[收到REDIS通知] 回复: {}", new String(message.getBody()));
  128 + switch (wvpRedisMsg.getCmd()){
  129 + case WvpRedisMsgCmd.GET_SEND_ITEM:
  130 +
  131 + WVPResult content = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), WVPResult.class);
  132 +
  133 + String key = wvpRedisMsg.getSerial();
  134 + switch (content.getCode()) {
  135 + case 0:
  136 + ResponseSendItemMsg responseSendItemMsg =JSON.toJavaObject((JSONObject)content.getData(), ResponseSendItemMsg.class);
  137 + PlayMsgCallback playMsgCallback = callbacks.get(key);
  138 + if (playMsgCallback != null) {
  139 + callbacksForError.remove(key);
  140 + playMsgCallback.handler(responseSendItemMsg);
  141 + }
  142 + break;
  143 + case ERROR_CODE_MEDIA_SERVER_NOT_FOUND:
  144 + case ERROR_CODE_OFFLINE:
  145 + case ERROR_CODE_TIMEOUT:
  146 + PlayMsgErrorCallback errorCallback = callbacksForError.get(key);
  147 + if (errorCallback != null) {
  148 + callbacks.remove(key);
  149 + errorCallback.handler(content);
  150 + }
  151 + break;
  152 + default:
  153 + break;
  154 + }
  155 + break;
  156 + case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
  157 + WVPResult wvpResult = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), WVPResult.class);
  158 + String serial = wvpRedisMsg.getSerial();
  159 + switch (wvpResult.getCode()) {
  160 + case 0:
  161 + JSONObject jsonObject = (JSONObject)wvpResult.getData();
  162 + PlayMsgCallbackForStartSendRtpStream playMsgCallback = callbacksForStartSendRtpStream.get(serial);
  163 + if (playMsgCallback != null) {
  164 + callbacksForError.remove(serial);
  165 + playMsgCallback.handler(jsonObject);
  166 + }
  167 + break;
  168 + case ERROR_CODE_MEDIA_SERVER_NOT_FOUND:
  169 + case ERROR_CODE_OFFLINE:
  170 + case ERROR_CODE_TIMEOUT:
  171 + PlayMsgErrorCallback errorCallback = callbacksForError.get(serial);
  172 + if (errorCallback != null) {
  173 + callbacks.remove(serial);
  174 + errorCallback.handler(wvpResult);
  175 + }
  176 + break;
  177 + default:
  178 + break;
  179 + }
  180 + break;
  181 + default:
  182 + break;
  183 + }
  184 + }
  185 +
  186 +
  187 +
  188 +
  189 + }
  190 +
  191 + /**
  192 + * 处理收到的请求推流的请求
  193 + */
  194 + private void requestPushStreamMsgHand(RequestPushStreamMsg requestPushStreamMsg, String fromId, String serial) {
  195 + MediaServerItem mediaInfo = mediaServerService.getOne(requestPushStreamMsg.getMediaServerId());
  196 + if (mediaInfo == null) {
  197 + // TODO 回复错误
  198 + return;
  199 + }
  200 + String is_Udp = requestPushStreamMsg.isTcp() ? "0" : "1";
  201 + Map<String, Object> param = new HashMap<>();
  202 + param.put("vhost","__defaultVhost__");
  203 + param.put("app",requestPushStreamMsg.getApp());
  204 + param.put("stream",requestPushStreamMsg.getStream());
  205 + param.put("ssrc", requestPushStreamMsg.getSsrc());
  206 + param.put("dst_url",requestPushStreamMsg.getIp());
  207 + param.put("dst_port", requestPushStreamMsg.getPort());
  208 + param.put("is_udp", is_Udp);
  209 + param.put("src_port", requestPushStreamMsg.getSrcPort());
  210 + param.put("pt", requestPushStreamMsg.getPt());
  211 + param.put("use_ps", requestPushStreamMsg.isPs() ? "1" : "0");
  212 + param.put("only_audio", requestPushStreamMsg.isOnlyAudio() ? "1" : "0");
  213 + JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
  214 + // 回复消息
  215 + responsePushStream(jsonObject, fromId, serial);
  216 + }
  217 +
  218 + private void responsePushStream(JSONObject content, String toId, String serial) {
  219 +
  220 + WVPResult<JSONObject> result = new WVPResult<>();
  221 + result.setCode(0);
  222 + result.setData(content);
  223 +
  224 + WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
  225 + WvpRedisMsgCmd.REQUEST_PUSH_STREAM, serial, result);
  226 + JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
  227 + redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
  228 + }
  229 +
  230 + /**
  231 + * 处理收到的请求sendItem的请求
  232 + */
  233 + private void requestSendItemMsgHand(RequestSendItemMsg content, String toId, String serial) {
  234 + MediaServerItem mediaServerItem = mediaServerService.getOne(content.getMediaServerId());
  235 + if (mediaServerItem == null) {
  236 + logger.info("[回复推流信息] 流媒体{}不存在 ", content.getMediaServerId());
  237 +
  238 + WVPResult<SendRtpItem> result = new WVPResult<>();
  239 + result.setCode(ERROR_CODE_MEDIA_SERVER_NOT_FOUND);
  240 + result.setMsg("流媒体不存在");
  241 +
  242 + WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
  243 + WvpRedisMsgCmd.GET_SEND_ITEM, serial, result);
  244 +
  245 + JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
  246 + redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
  247 + return;
  248 + }
  249 + // 确定流是否在线
  250 + boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream());
  251 + if (streamReady) {
  252 + logger.info("[回复推流信息] {}/{}", content.getApp(), content.getStream());
  253 + responseSendItem(mediaServerItem, content, toId, serial);
  254 + }else {
  255 + // 流已经离线
  256 + // 发送redis消息以使设备上线
  257 + logger.info("[ app={}, stream={} ]通道离线,发送redis信息控制设备开始推流",content.getApp(), content.getStream());
  258 +
  259 + String taskKey = UUID.randomUUID().toString();
  260 + // 设置超时
  261 + dynamicTask.startDelay(taskKey, ()->{
  262 + logger.info("[ app={}, stream={} ] 等待设备开始推流超时", content.getApp(), content.getStream());
  263 + WVPResult<SendRtpItem> result = new WVPResult<>();
  264 + result.setCode(ERROR_CODE_TIMEOUT);
  265 + WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
  266 + userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result
  267 + );
  268 + JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
  269 + redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
  270 + }, userSetting.getPlatformPlayTimeout());
  271 +
  272 + // 添加订阅
  273 + JSONObject subscribeKey = new JSONObject();
  274 + subscribeKey.put("app", content.getApp());
  275 + subscribeKey.put("stream", content.getStream());
  276 + subscribeKey.put("regist", true);
  277 + subscribeKey.put("schema", "rtmp");
  278 + subscribeKey.put("mediaServerId", mediaServerItem.getId());
  279 + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
  280 + (MediaServerItem mediaServerItemInUse, JSONObject json)->{
  281 + dynamicTask.stop(taskKey);
  282 + responseSendItem(mediaServerItem, content, toId, serial);
  283 + });
  284 +
  285 + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, content.getApp(), content.getStream(),
  286 + content.getChannelId(), content.getPlatformId(), content.getPlatformName(), content.getServerId(),
  287 + content.getMediaServerId());
  288 + redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
  289 +
  290 + }
  291 + }
  292 +
  293 + /**
  294 + * 将获取到的sendItem发送出去
  295 + */
  296 + private void responseSendItem(MediaServerItem mediaServerItem, RequestSendItemMsg content, String toId, String serial) {
  297 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(),
  298 + content.getPort(), content.getSsrc(), content.getPlatformId(),
  299 + content.getApp(), content.getStream(), content.getChannelId(),
  300 + content.getTcp());
  301 +
  302 + WVPResult<ResponseSendItemMsg> result = new WVPResult<>();
  303 + result.setCode(0);
  304 + ResponseSendItemMsg responseSendItemMsg = new ResponseSendItemMsg();
  305 + responseSendItemMsg.setSendRtpItem(sendRtpItem);
  306 + responseSendItemMsg.setMediaServerItem(mediaServerItem);
  307 + result.setData(responseSendItemMsg);
  308 +
  309 + WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
  310 + userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result
  311 + );
  312 + JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
  313 + redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
  314 + }
  315 +
  316 + /**
  317 + * 发送消息要求下级生成推流信息
  318 + * @param serverId 下级服务ID
  319 + * @param app 应用名
  320 + * @param stream 流ID
  321 + * @param ip 目标IP
  322 + * @param port 目标端口
  323 + * @param ssrc ssrc
  324 + * @param platformId 平台国标编号
  325 + * @param channelId 通道ID
  326 + * @param isTcp 是否使用TCP
  327 + * @param callback 得到信息的回调
  328 + */
  329 + public void sendMsg(String serverId, String mediaServerId, String app, String stream, String ip, int port, String ssrc,
  330 + String platformId, String channelId, boolean isTcp, String platformName, PlayMsgCallback callback, PlayMsgErrorCallback errorCallback) {
  331 + RequestSendItemMsg requestSendItemMsg = RequestSendItemMsg.getInstance(
  332 + serverId, mediaServerId, app, stream, ip, port, ssrc, platformId, channelId, isTcp, platformName);
  333 + requestSendItemMsg.setServerId(serverId);
  334 + String key = UUID.randomUUID().toString();
  335 + WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, WvpRedisMsgCmd.GET_SEND_ITEM,
  336 + key, requestSendItemMsg);
  337 +
  338 + JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
  339 + logger.info("[请求推流SendItem] {}: {}", serverId, jsonObject);
  340 + callbacks.put(key, callback);
  341 + callbacksForError.put(key, errorCallback);
  342 + dynamicTask.startDelay(key, ()->{
  343 + callbacks.remove(key);
  344 + callbacksForError.remove(key);
  345 + WVPResult<Object> wvpResult = new WVPResult<>();
  346 + wvpResult.setCode(ERROR_CODE_TIMEOUT);
  347 + wvpResult.setMsg("timeout");
  348 + errorCallback.handler(wvpResult);
  349 + }, userSetting.getPlatformPlayTimeout());
  350 + redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
  351 + }
  352 +
  353 + /**
  354 + * 发送请求推流的消息
  355 + * @param param 推流参数
  356 + * @param callback 回调
  357 + */
  358 + public void sendMsgForStartSendRtpStream(String serverId, RequestPushStreamMsg param, PlayMsgCallbackForStartSendRtpStream callback) {
  359 + String key = UUID.randomUUID().toString();
  360 + WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId,
  361 + WvpRedisMsgCmd.REQUEST_PUSH_STREAM, key, param);
  362 +
  363 + JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
  364 + logger.info("[REDIS 请求其他平台推流] {}: {}", serverId, jsonObject);
  365 + dynamicTask.startDelay(key, ()->{
  366 + callbacksForStartSendRtpStream.remove(key);
  367 + callbacksForError.remove(key);
  368 + }, userSetting.getPlatformPlayTimeout());
  369 + callbacksForStartSendRtpStream.put(key, callback);
  370 + callbacksForError.put(key, (wvpResult)->{
  371 + logger.info("[REDIS 请求其他平台推流] 失败: {}", wvpResult.getMsg());
  372 + callbacksForStartSendRtpStream.remove(key);
  373 + callbacksForError.remove(key);
  374 + });
  375 + redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
  376 + }
  377 +}
src/main/java/com/genersoft/iot/vmp/service/impl/RedisGPSMsgListener.java renamed to src/main/java/com/genersoft/iot/vmp/service/impl/RedisGpsMsgListener.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.fastjson.JSON; 3 import com.alibaba.fastjson.JSON;
4 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; 4 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
5 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 5 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  6 +import org.jetbrains.annotations.NotNull;
6 import org.slf4j.Logger; 7 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory; 8 import org.slf4j.LoggerFactory;
8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.beans.factory.annotation.Autowired;
@@ -10,17 +11,23 @@ import org.springframework.data.redis.connection.Message; @@ -10,17 +11,23 @@ import org.springframework.data.redis.connection.Message;
10 import org.springframework.data.redis.connection.MessageListener; 11 import org.springframework.data.redis.connection.MessageListener;
11 import org.springframework.stereotype.Component; 12 import org.springframework.stereotype.Component;
12 13
  14 +/**
  15 + * 接收来自redis的GPS更新通知
  16 + * @author lin
  17 + */
13 @Component 18 @Component
14 -public class RedisGPSMsgListener implements MessageListener { 19 +public class RedisGpsMsgListener implements MessageListener {
15 20
16 - private final static Logger logger = LoggerFactory.getLogger(RedisGPSMsgListener.class); 21 + private final static Logger logger = LoggerFactory.getLogger(RedisGpsMsgListener.class);
17 22
18 @Autowired 23 @Autowired
19 private IRedisCatchStorage redisCatchStorage; 24 private IRedisCatchStorage redisCatchStorage;
20 25
21 @Override 26 @Override
22 - public void onMessage(Message message, byte[] bytes) {  
23 - logger.info("收到来自REDIS的GPS通知: {}", new String(message.getBody())); 27 + public void onMessage(@NotNull Message message, byte[] bytes) {
  28 + if (logger.isDebugEnabled()) {
  29 + logger.debug("收到来自REDIS的GPS通知: {}", new String(message.getBody()));
  30 + }
24 GPSMsgInfo gpsMsgInfo = JSON.parseObject(message.getBody(), GPSMsgInfo.class); 31 GPSMsgInfo gpsMsgInfo = JSON.parseObject(message.getBody(), GPSMsgInfo.class);
25 redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo); 32 redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
26 } 33 }
src/main/java/com/genersoft/iot/vmp/service/impl/RedisStreamMsgListener.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.impl;
  2 +
  3 +import com.alibaba.fastjson.JSON;
  4 +import com.alibaba.fastjson.JSONObject;
  5 +import com.genersoft.iot.vmp.conf.UserSetting;
  6 +import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage;
  7 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  8 +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
  9 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  10 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
  11 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
  12 +import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
  13 +import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
  14 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  15 +import com.genersoft.iot.vmp.utils.DateUtil;
  16 +import org.slf4j.Logger;
  17 +import org.slf4j.LoggerFactory;
  18 +import org.springframework.beans.factory.annotation.Autowired;
  19 +import org.springframework.data.redis.connection.Message;
  20 +import org.springframework.data.redis.connection.MessageListener;
  21 +import org.springframework.stereotype.Component;
  22 +
  23 +
  24 +/**
  25 + * @author lin
  26 + */
  27 +@Component
  28 +public class RedisStreamMsgListener implements MessageListener {
  29 +
  30 + private final static Logger logger = LoggerFactory.getLogger(RedisStreamMsgListener.class);
  31 +
  32 + @Autowired
  33 + private ISIPCommander commander;
  34 +
  35 + @Autowired
  36 + private ISIPCommanderForPlatform commanderForPlatform;
  37 +
  38 + @Autowired
  39 + private IVideoManagerStorage storage;
  40 +
  41 + @Autowired
  42 + private UserSetting userSetting;
  43 +
  44 + @Autowired
  45 + private ZLMMediaListManager zlmMediaListManager;
  46 +
  47 + @Override
  48 + public void onMessage(Message message, byte[] bytes) {
  49 +
  50 + JSONObject steamMsgJson = JSON.parseObject(message.getBody(), JSONObject.class);
  51 + if (steamMsgJson == null) {
  52 + logger.warn("[REDIS的ALARM通知]消息解析失败");
  53 + return;
  54 + }
  55 + String serverId = steamMsgJson.getString("serverId");
  56 +
  57 + if (userSetting.getServerId().equals(serverId)) {
  58 + // 自己发送的消息忽略即可
  59 + return;
  60 + }
  61 + logger.info("[REDIS通知] 流变化: {}", new String(message.getBody()));
  62 + String app = steamMsgJson.getString("app");
  63 + String stream = steamMsgJson.getString("stream");
  64 + boolean register = steamMsgJson.getBoolean("register");
  65 + String mediaServerId = steamMsgJson.getString("mediaServerId");
  66 + MediaItem mediaItem = new MediaItem();
  67 + mediaItem.setSeverId(serverId);
  68 + mediaItem.setApp(app);
  69 + mediaItem.setStream(stream);
  70 + mediaItem.setRegist(register);
  71 + mediaItem.setMediaServerId(mediaServerId);
  72 + mediaItem.setCreateStamp(System.currentTimeMillis()/1000);
  73 + mediaItem.setAliveSecond(0L);
  74 + mediaItem.setTotalReaderCount("0");
  75 + mediaItem.setOriginType(0);
  76 + mediaItem.setOriginTypeStr("0");
  77 + mediaItem.setOriginTypeStr("unknown");
  78 +
  79 + zlmMediaListManager.addPush(mediaItem);
  80 +
  81 +
  82 + }
  83 +}
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
@@ -107,6 +107,7 @@ public class StreamPushServiceImpl implements IStreamPushService { @@ -107,6 +107,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
107 streamPushItem.setStatus(true); 107 streamPushItem.setStatus(true);
108 streamPushItem.setStreamType("push"); 108 streamPushItem.setStreamType("push");
109 streamPushItem.setVhost(item.getVhost()); 109 streamPushItem.setVhost(item.getVhost());
  110 + streamPushItem.setServerId(item.getSeverId());
110 return streamPushItem; 111 return streamPushItem;
111 } 112 }
112 113
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
@@ -357,6 +357,15 @@ public interface IVideoManagerStorage { @@ -357,6 +357,15 @@ public interface IVideoManagerStorage {
357 357
358 358
359 /** 359 /**
  360 + * 获取但个推流
  361 + * @param app
  362 + * @param stream
  363 + * @return
  364 + */
  365 + StreamPushItem getMedia(String app, String stream);
  366 +
  367 +
  368 + /**
360 * 清空推流列表 369 * 清空推流列表
361 */ 370 */
362 void clearMediaList(); 371 void clearMediaList();
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
@@ -17,10 +17,10 @@ public interface DeviceChannelMapper { @@ -17,10 +17,10 @@ public interface DeviceChannelMapper {
17 17
18 @Insert("INSERT INTO device_channel (channelId, deviceId, name, manufacture, model, owner, civilCode, block, " + 18 @Insert("INSERT INTO device_channel (channelId, deviceId, name, manufacture, model, owner, civilCode, block, " +
19 "address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " + 19 "address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " +
20 - "ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " + 20 + "ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, longitudeWgs84, latitudeWgs84, createTime, updateTime) " +
21 "VALUES ('${channelId}', '${deviceId}', '${name}', '${manufacture}', '${model}', '${owner}', '${civilCode}', '${block}'," + 21 "VALUES ('${channelId}', '${deviceId}', '${name}', '${manufacture}', '${model}', '${owner}', '${civilCode}', '${block}'," +
22 "'${address}', ${parental}, '${parentId}', ${safetyWay}, ${registerWay}, '${certNum}', ${certifiable}, ${errCode}, '${secrecy}', " + 22 "'${address}', ${parental}, '${parentId}', ${safetyWay}, ${registerWay}, '${certNum}', ${certifiable}, ${errCode}, '${secrecy}', " +
23 - "'${ipAddress}', ${port}, '${password}', ${PTZType}, ${status}, '${streamId}', ${longitude}, ${latitude},'${createTime}', '${updateTime}')") 23 + "'${ipAddress}', ${port}, '${password}', ${PTZType}, ${status}, '${streamId}', ${longitude}, ${latitude}, ${longitudeGcj02}, ${latitudeGcj02}, ${longitudeWgs84}, ${latitudeWgs84},'${createTime}', '${updateTime}')")
24 int add(DeviceChannel channel); 24 int add(DeviceChannel channel);
25 25
26 @Update(value = {" <script>" + 26 @Update(value = {" <script>" +
@@ -50,6 +50,10 @@ public interface DeviceChannelMapper { @@ -50,6 +50,10 @@ public interface DeviceChannelMapper {
50 "<if test='hasAudio != null'>, hasAudio=${hasAudio}</if>" + 50 "<if test='hasAudio != null'>, hasAudio=${hasAudio}</if>" +
51 "<if test='longitude != null'>, longitude=${longitude}</if>" + 51 "<if test='longitude != null'>, longitude=${longitude}</if>" +
52 "<if test='latitude != null'>, latitude=${latitude}</if>" + 52 "<if test='latitude != null'>, latitude=${latitude}</if>" +
  53 + "<if test='longitudeGcj02 != null'>, longitudeGcj02=${longitudeGcj02}</if>" +
  54 + "<if test='latitudeGcj02 != null'>, latitudeGcj02=${latitudeGcj02}</if>" +
  55 + "<if test='longitudeWgs84 != null'>, longitudeWgs84=${longitudeWgs84}</if>" +
  56 + "<if test='latitudeWgs84 != null'>, latitudeWgs84=${latitudeWgs84}</if>" +
53 "WHERE deviceId='${deviceId}' AND channelId='${channelId}'"+ 57 "WHERE deviceId='${deviceId}' AND channelId='${channelId}'"+
54 " </script>"}) 58 " </script>"})
55 int update(DeviceChannel channel); 59 int update(DeviceChannel channel);
@@ -67,7 +71,7 @@ public interface DeviceChannelMapper { @@ -67,7 +71,7 @@ public interface DeviceChannelMapper {
67 " <if test='online == false' > AND dc.status=0</if>" + 71 " <if test='online == false' > AND dc.status=0</if>" +
68 " <if test='hasSubChannel == true' > AND dc.subCount > 0 </if>" + 72 " <if test='hasSubChannel == true' > AND dc.subCount > 0 </if>" +
69 " <if test='hasSubChannel == false' > AND dc.subCount = 0 </if>" + 73 " <if test='hasSubChannel == false' > AND dc.subCount = 0 </if>" +
70 - "GROUP BY dc.channelId " + 74 + "ORDER BY dc.channelId " +
71 " </script>"}) 75 " </script>"})
72 List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online); 76 List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online);
73 77
@@ -138,7 +142,8 @@ public interface DeviceChannelMapper { @@ -138,7 +142,8 @@ public interface DeviceChannelMapper {
138 "insert into device_channel " + 142 "insert into device_channel " +
139 "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " + 143 "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " +
140 " address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " + 144 " address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " +
141 - " ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " + 145 + " ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, " +
  146 + " longitudeWgs84, latitudeWgs84, createTime, updateTime) " +
142 "values " + 147 "values " +
143 "<foreach collection='addChannels' index='index' item='item' separator=','> " + 148 "<foreach collection='addChannels' index='index' item='item' separator=','> " +
144 "('${item.channelId}', '${item.deviceId}', '${item.name}', '${item.manufacture}', '${item.model}', " + 149 "('${item.channelId}', '${item.deviceId}', '${item.name}', '${item.manufacture}', '${item.model}', " +
@@ -146,7 +151,8 @@ public interface DeviceChannelMapper { @@ -146,7 +151,8 @@ public interface DeviceChannelMapper {
146 "'${item.address}', ${item.parental}, '${item.parentId}', ${item.safetyWay}, ${item.registerWay}, " + 151 "'${item.address}', ${item.parental}, '${item.parentId}', ${item.safetyWay}, ${item.registerWay}, " +
147 "'${item.certNum}', ${item.certifiable}, ${item.errCode}, '${item.secrecy}', " + 152 "'${item.certNum}', ${item.certifiable}, ${item.errCode}, '${item.secrecy}', " +
148 "'${item.ipAddress}', ${item.port}, '${item.password}', ${item.PTZType}, ${item.status}, " + 153 "'${item.ipAddress}', ${item.port}, '${item.password}', ${item.PTZType}, ${item.status}, " +
149 - "'${item.streamId}', ${item.longitude}, ${item.latitude},'${item.createTime}', '${item.updateTime}')" + 154 + "'${item.streamId}', ${item.longitude}, ${item.latitude},${item.longitudeGcj02}, " +
  155 + "${item.latitudeGcj02},${item.longitudeWgs84}, ${item.latitudeWgs84},'${item.createTime}', '${item.updateTime}')" +
150 "</foreach> " + 156 "</foreach> " +
151 "ON DUPLICATE KEY UPDATE " + 157 "ON DUPLICATE KEY UPDATE " +
152 "updateTime=VALUES(updateTime), " + 158 "updateTime=VALUES(updateTime), " +
@@ -173,7 +179,11 @@ public interface DeviceChannelMapper { @@ -173,7 +179,11 @@ public interface DeviceChannelMapper {
173 "status=VALUES(status), " + 179 "status=VALUES(status), " +
174 "streamId=VALUES(streamId), " + 180 "streamId=VALUES(streamId), " +
175 "longitude=VALUES(longitude), " + 181 "longitude=VALUES(longitude), " +
176 - "latitude=VALUES(latitude)" + 182 + "latitude=VALUES(latitude), " +
  183 + "longitudeGcj02=VALUES(longitudeGcj02), " +
  184 + "latitudeGcj02=VALUES(latitudeGcj02), " +
  185 + "longitudeWgs84=VALUES(longitudeWgs84), " +
  186 + "latitudeWgs84=VALUES(latitudeWgs84) " +
177 "</script>") 187 "</script>")
178 int batchAdd(List<DeviceChannel> addChannels); 188 int batchAdd(List<DeviceChannel> addChannels);
179 189
@@ -207,7 +217,11 @@ public interface DeviceChannelMapper { @@ -207,7 +217,11 @@ public interface DeviceChannelMapper {
207 "<if test='item.hasAudio != null'>, hasAudio=${item.hasAudio}</if>" + 217 "<if test='item.hasAudio != null'>, hasAudio=${item.hasAudio}</if>" +
208 "<if test='item.longitude != null'>, longitude=${item.longitude}</if>" + 218 "<if test='item.longitude != null'>, longitude=${item.longitude}</if>" +
209 "<if test='item.latitude != null'>, latitude=${item.latitude}</if>" + 219 "<if test='item.latitude != null'>, latitude=${item.latitude}</if>" +
210 - "WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}"+ 220 + "<if test='item.longitudeGcj02 != null'>, longitudeGcj02=${item.longitudeGcj02}</if>" +
  221 + "<if test='item.latitudeGcj02 != null'>, latitudeGcj02=${item.latitudeGcj02}</if>" +
  222 + "<if test='item.longitudeWgs84 != null'>, longitudeWgs84=${item.longitudeWgs84}</if>" +
  223 + "<if test='item.latitudeWgs84 != null'>, latitudeWgs84=${item.latitudeWgs84}</if>" +
  224 + "WHERE deviceId='${item.deviceId}' AND channelId='${item.channelId}'"+
211 "</foreach>" + 225 "</foreach>" +
212 "</script>"}) 226 "</script>"})
213 int batchUpdate(List<DeviceChannel> updateChannels); 227 int batchUpdate(List<DeviceChannel> updateChannels);
@@ -261,4 +275,6 @@ public interface DeviceChannelMapper { @@ -261,4 +275,6 @@ public interface DeviceChannelMapper {
261 @Select("SELECT * FROM device_channel WHERE length(trim(streamId)) > 0") 275 @Select("SELECT * FROM device_channel WHERE length(trim(streamId)) > 0")
262 List<DeviceChannel> getAllChannelInPlay(); 276 List<DeviceChannel> getAllChannelInPlay();
263 277
  278 + @Select("select * from device_channel where longitude*latitude > 0 and deviceId = #{deviceId}")
  279 + List<DeviceChannel> getAllChannelWithCoordinate(String deviceId);
264 } 280 }
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
@@ -38,6 +38,7 @@ public interface DeviceMapper { @@ -38,6 +38,7 @@ public interface DeviceMapper {
38 "mobilePositionSubmissionInterval," + 38 "mobilePositionSubmissionInterval," +
39 "subscribeCycleForAlarm," + 39 "subscribeCycleForAlarm," +
40 "ssrcCheck," + 40 "ssrcCheck," +
  41 + "geoCoordSys," +
41 "online" + 42 "online" +
42 ") VALUES (" + 43 ") VALUES (" +
43 "#{deviceId}," + 44 "#{deviceId}," +
@@ -61,6 +62,7 @@ public interface DeviceMapper { @@ -61,6 +62,7 @@ public interface DeviceMapper {
61 "#{mobilePositionSubmissionInterval}," + 62 "#{mobilePositionSubmissionInterval}," +
62 "#{subscribeCycleForAlarm}," + 63 "#{subscribeCycleForAlarm}," +
63 "#{ssrcCheck}," + 64 "#{ssrcCheck}," +
  65 + "#{geoCoordSys}," +
64 "#{online}" + 66 "#{online}" +
65 ")") 67 ")")
66 int add(Device device); 68 int add(Device device);
@@ -87,6 +89,7 @@ public interface DeviceMapper { @@ -87,6 +89,7 @@ public interface DeviceMapper {
87 "<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=${mobilePositionSubmissionInterval}</if>" + 89 "<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=${mobilePositionSubmissionInterval}</if>" +
88 "<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=${subscribeCycleForAlarm}</if>" + 90 "<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=${subscribeCycleForAlarm}</if>" +
89 "<if test=\"ssrcCheck != null\">, ssrcCheck=${ssrcCheck}</if>" + 91 "<if test=\"ssrcCheck != null\">, ssrcCheck=${ssrcCheck}</if>" +
  92 + "<if test=\"geoCoordSys != null\">, geoCoordSys=#{geoCoordSys}</if>" +
90 "WHERE deviceId='${deviceId}'"+ 93 "WHERE deviceId='${deviceId}'"+
91 " </script>"}) 94 " </script>"})
92 int update(Device device); 95 int update(Device device);
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java
@@ -14,9 +14,9 @@ import java.util.List; @@ -14,9 +14,9 @@ import java.util.List;
14 public interface StreamPushMapper { 14 public interface StreamPushMapper {
15 15
16 @Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " + 16 @Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " +
17 - "createStamp, aliveSecond, mediaServerId) VALUES" + 17 + "createStamp, aliveSecond, mediaServerId, serverId) VALUES" +
18 "('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " + 18 "('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " +
19 - "'${createStamp}', '${aliveSecond}', '${mediaServerId}' )") 19 + "'${createStamp}', '${aliveSecond}', '${mediaServerId}' , '${serverId}' )")
20 int add(StreamPushItem streamPushItem); 20 int add(StreamPushItem streamPushItem);
21 21
22 @Update("UPDATE stream_push " + 22 @Update("UPDATE stream_push " +
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
@@ -587,11 +587,11 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -587,11 +587,11 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
587 String scanKey = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_*"; 587 String scanKey = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_*";
588 List<GPSMsgInfo> result = new ArrayList<>(); 588 List<GPSMsgInfo> result = new ArrayList<>();
589 List<Object> keys = redis.scan(scanKey); 589 List<Object> keys = redis.scan(scanKey);
590 - for (int i = 0; i < keys.size(); i++) {  
591 - String key = (String) keys.get(i); 590 + for (Object o : keys) {
  591 + String key = (String) o;
592 GPSMsgInfo gpsMsgInfo = (GPSMsgInfo) redis.get(key); 592 GPSMsgInfo gpsMsgInfo = (GPSMsgInfo) redis.get(key);
593 if (!gpsMsgInfo.isStored()) { // 只取没有存过得 593 if (!gpsMsgInfo.isStored()) { // 只取没有存过得
594 - result.add((GPSMsgInfo)redis.get(key)); 594 + result.add((GPSMsgInfo) redis.get(key));
595 } 595 }
596 } 596 }
597 597
@@ -667,7 +667,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -667,7 +667,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
667 @Override 667 @Override
668 public void sendStreamPushRequestedMsg(MessageForPushChannel msg) { 668 public void sendStreamPushRequestedMsg(MessageForPushChannel msg) {
669 String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED; 669 String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED;
670 - logger.info("[redis 推流被请求通知] {}: {}-{}", key, msg.getApp(), msg.getStream()); 670 + logger.info("[redis 推流被请求通知] {}: {}/{}", key, msg.getApp(), msg.getStream());
671 redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg)); 671 redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
672 } 672 }
673 673
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
@@ -25,12 +25,13 @@ import org.springframework.stereotype.Component; @@ -25,12 +25,13 @@ import org.springframework.stereotype.Component;
25 import org.springframework.transaction.TransactionDefinition; 25 import org.springframework.transaction.TransactionDefinition;
26 import org.springframework.transaction.TransactionStatus; 26 import org.springframework.transaction.TransactionStatus;
27 import org.springframework.transaction.annotation.Transactional; 27 import org.springframework.transaction.annotation.Transactional;
  28 +import org.springframework.util.CollectionUtils;
28 import org.springframework.util.StringUtils; 29 import org.springframework.util.StringUtils;
29 30
30 import java.util.*; 31 import java.util.*;
31 import java.util.concurrent.ConcurrentHashMap; 32 import java.util.concurrent.ConcurrentHashMap;
32 33
33 -/** 34 +/**
34 * 视频设备数据存储-jdbc实现 35 * 视频设备数据存储-jdbc实现
35 * swwheihei 36 * swwheihei
36 * 2020年5月6日 下午2:31:42 37 * 2020年5月6日 下午2:31:42
@@ -195,7 +196,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { @@ -195,7 +196,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
195 196
196 @Override 197 @Override
197 public boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList) { 198 public boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList) {
198 - if (deviceChannelList == null) { 199 + if (CollectionUtils.isEmpty(deviceChannelList)) {
199 return false; 200 return false;
200 } 201 }
201 List<DeviceChannel> allChannelInPlay = deviceChannelMapper.getAllChannelInPlay(); 202 List<DeviceChannel> allChannelInPlay = deviceChannelMapper.getAllChannelInPlay();
@@ -246,6 +247,10 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { @@ -246,6 +247,10 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
246 if (stringBuilder.length() > 0) { 247 if (stringBuilder.length() > 0) {
247 logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder); 248 logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder);
248 } 249 }
  250 + if(CollectionUtils.isEmpty(channels)){
  251 + logger.info("通道重设,数据为空={}" , deviceChannelList);
  252 + return false;
  253 + }
249 try { 254 try {
250 int cleanChannelsResult = deviceChannelMapper.cleanChannelsNotInList(deviceId, channels); 255 int cleanChannelsResult = deviceChannelMapper.cleanChannelsNotInList(deviceId, channels);
251 int limitCount = 300; 256 int limitCount = 300;
@@ -315,6 +320,9 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { @@ -315,6 +320,9 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
315 List<DeviceChannel> all; 320 List<DeviceChannel> all;
316 if (catalogUnderDevice != null && catalogUnderDevice) { 321 if (catalogUnderDevice != null && catalogUnderDevice) {
317 all = deviceChannelMapper.queryChannels(deviceId, deviceId, query, hasSubChannel, online); 322 all = deviceChannelMapper.queryChannels(deviceId, deviceId, query, hasSubChannel, online);
  323 + // 海康设备的parentId是SIP id
  324 + List<DeviceChannel> deviceChannels = deviceChannelMapper.queryChannels(deviceId, sipConfig.getId(), query, hasSubChannel, online);
  325 + all.addAll(deviceChannels);
318 }else { 326 }else {
319 all = deviceChannelMapper.queryChannels(deviceId, null, query, hasSubChannel, online); 327 all = deviceChannelMapper.queryChannels(deviceId, null, query, hasSubChannel, online);
320 } 328 }
@@ -877,6 +885,11 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { @@ -877,6 +885,11 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
877 } 885 }
878 886
879 @Override 887 @Override
  888 + public StreamPushItem getMedia(String app, String stream) {
  889 + return streamPushMapper.selectOne(app, stream);
  890 + }
  891 +
  892 + @Override
880 public void clearMediaList() { 893 public void clearMediaList() {
881 streamPushMapper.clear(); 894 streamPushMapper.clear();
882 } 895 }
src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java
1 package com.genersoft.iot.vmp.utils; 1 package com.genersoft.iot.vmp.utils;
2 2
3 3
4 -import java.text.SimpleDateFormat;  
5 import java.time.Instant; 4 import java.time.Instant;
6 import java.time.LocalDate; 5 import java.time.LocalDate;
7 import java.time.LocalDateTime; 6 import java.time.LocalDateTime;
@@ -18,35 +17,63 @@ import java.util.Locale; @@ -18,35 +17,63 @@ import java.util.Locale;
18 */ 17 */
19 public class DateUtil { 18 public class DateUtil {
20 19
21 - private static final String yyyy_MM_dd_T_HH_mm_ss_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss";  
22 - public static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss"; 20 + /**
  21 + * 兼容不规范的iso8601时间格式
  22 + */
  23 + private static final String ISO8601_COMPATIBLE_PATTERN = "yyyy-M-d'T'H:m:s";
23 24
24 - public static final SimpleDateFormat formatISO8601 = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault());  
25 - public static final SimpleDateFormat format = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()); 25 + /**
  26 + * 用以输出标准的iso8601时间格式
  27 + */
  28 + private static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
26 29
27 - public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()).withZone(ZoneId.systemDefault());  
28 - public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()).withZone(ZoneId.systemDefault()); 30 + /**
  31 + * wvp内部统一时间格式
  32 + */
  33 + public static final String PATTERN = "yyyy-MM-dd HH:mm:ss";
  34 +
  35 + public static final String zoneStr = "Asia/Shanghai";
  36 +
  37 + public static final DateTimeFormatter formatterCompatibleISO8601 = DateTimeFormatter.ofPattern(ISO8601_COMPATIBLE_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
  38 + public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(ISO8601_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
  39 + public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
29 40
30 public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) { 41 public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) {
  42 +
31 return formatterISO8601.format(formatter.parse(formatTime)); 43 return formatterISO8601.format(formatter.parse(formatTime));
32 } 44 }
33 45
34 public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) { 46 public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) {
35 - return formatter.format(formatterISO8601.parse(formatTime)); 47 + return formatter.format(formatterCompatibleISO8601.parse(formatTime));
36 48
37 } 49 }
38 - 50 +
  51 + /**
  52 + * yyyy_MM_dd_HH_mm_ss 转时间戳
  53 + * @param formatTime
  54 + * @return
  55 + */
39 public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) { 56 public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) {
40 TemporalAccessor temporalAccessor = formatter.parse(formatTime); 57 TemporalAccessor temporalAccessor = formatter.parse(formatTime);
41 Instant instant = Instant.from(temporalAccessor); 58 Instant instant = Instant.from(temporalAccessor);
42 return instant.getEpochSecond(); 59 return instant.getEpochSecond();
43 } 60 }
44 61
  62 + /**
  63 + * 获取当前时间
  64 + * @return
  65 + */
45 public static String getNow() { 66 public static String getNow() {
46 LocalDateTime nowDateTime = LocalDateTime.now(); 67 LocalDateTime nowDateTime = LocalDateTime.now();
47 return formatter.format(nowDateTime); 68 return formatter.format(nowDateTime);
48 } 69 }
49 70
  71 + /**
  72 + * 格式校验
  73 + * @param timeStr 时间字符串
  74 + * @param dateTimeFormatter 待校验的格式
  75 + * @return
  76 + */
50 public static boolean verification(String timeStr, DateTimeFormatter dateTimeFormatter) { 77 public static boolean verification(String timeStr, DateTimeFormatter dateTimeFormatter) {
51 try { 78 try {
52 LocalDate.parse(timeStr, dateTimeFormatter); 79 LocalDate.parse(timeStr, dateTimeFormatter);
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java
@@ -24,6 +24,7 @@ import org.springframework.util.StringUtils; @@ -24,6 +24,7 @@ import org.springframework.util.StringUtils;
24 import org.springframework.web.bind.annotation.*; 24 import org.springframework.web.bind.annotation.*;
25 25
26 import java.text.ParseException; 26 import java.text.ParseException;
  27 +import java.time.LocalDateTime;
27 import java.util.Arrays; 28 import java.util.Arrays;
28 import java.util.List; 29 import java.util.List;
29 30
@@ -68,8 +69,8 @@ public class AlarmController { @@ -68,8 +69,8 @@ public class AlarmController {
68 @ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class), 69 @ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class),
69 @ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class), 70 @ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class),
70 @ApiImplicitParam(name="alarmType", value = "查询内容" ,dataTypeClass = String.class), 71 @ApiImplicitParam(name="alarmType", value = "查询内容" ,dataTypeClass = String.class),
71 - @ApiImplicitParam(name="startTime", value = "查询内容" ,dataTypeClass = String.class),  
72 - @ApiImplicitParam(name="endTime", value = "查询内容" ,dataTypeClass = String.class), 72 + @ApiImplicitParam(name="startTime", value = "开始时间" ,dataTypeClass = String.class),
  73 + @ApiImplicitParam(name="endTime", value = "结束时间" ,dataTypeClass = String.class),
73 }) 74 })
74 public ResponseEntity<PageInfo<DeviceAlarm>> getAll( 75 public ResponseEntity<PageInfo<DeviceAlarm>> getAll(
75 @RequestParam int page, 76 @RequestParam int page,
@@ -98,14 +99,7 @@ public class AlarmController { @@ -98,14 +99,7 @@ public class AlarmController {
98 } 99 }
99 100
100 101
101 - try {  
102 - if (startTime != null) {  
103 - DateUtil.format.parse(startTime);  
104 - }  
105 - if (endTime != null) {  
106 - DateUtil.format.parse(endTime);  
107 - }  
108 - } catch (ParseException e) { 102 + if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
109 return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); 103 return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
110 } 104 }
111 105
@@ -144,11 +138,7 @@ public class AlarmController { @@ -144,11 +138,7 @@ public class AlarmController {
144 if (StringUtils.isEmpty(time)) { 138 if (StringUtils.isEmpty(time)) {
145 time = null; 139 time = null;
146 } 140 }
147 - try {  
148 - if (time != null) {  
149 - DateUtil.format.parse(time);  
150 - }  
151 - } catch (ParseException e) { 141 + if (!DateUtil.verification(time, DateUtil.formatter) ){
152 return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); 142 return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
153 } 143 }
154 List<String> deviceIdList = null; 144 List<String> deviceIdList = null;
@@ -189,7 +179,7 @@ public class AlarmController { @@ -189,7 +179,7 @@ public class AlarmController {
189 deviceAlarm.setAlarmDescription("test"); 179 deviceAlarm.setAlarmDescription("test");
190 deviceAlarm.setAlarmMethod("1"); 180 deviceAlarm.setAlarmMethod("1");
191 deviceAlarm.setAlarmPriority("1"); 181 deviceAlarm.setAlarmPriority("1");
192 - deviceAlarm.setAlarmTime(DateUtil.formatISO8601.format(System.currentTimeMillis())); 182 + deviceAlarm.setAlarmTime(DateUtil.formatterISO8601.format(LocalDateTime.now()));
193 deviceAlarm.setAlarmType("1"); 183 deviceAlarm.setAlarmType("1");
194 deviceAlarm.setLongitude(115.33333); 184 deviceAlarm.setLongitude(115.33333);
195 deviceAlarm.setLatitude(39.33333); 185 deviceAlarm.setLatitude(39.33333);
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
@@ -21,16 +21,22 @@ import io.swagger.annotations.Api; @@ -21,16 +21,22 @@ import io.swagger.annotations.Api;
21 import io.swagger.annotations.ApiImplicitParam; 21 import io.swagger.annotations.ApiImplicitParam;
22 import io.swagger.annotations.ApiImplicitParams; 22 import io.swagger.annotations.ApiImplicitParams;
23 import io.swagger.annotations.ApiOperation; 23 import io.swagger.annotations.ApiOperation;
  24 +import org.apache.commons.compress.utils.IOUtils;
  25 +import org.apache.http.HttpResponse;
24 import org.slf4j.Logger; 26 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory; 27 import org.slf4j.LoggerFactory;
26 import org.springframework.beans.factory.annotation.Autowired; 28 import org.springframework.beans.factory.annotation.Autowired;
27 import org.springframework.http.HttpStatus; 29 import org.springframework.http.HttpStatus;
  30 +import org.springframework.http.MediaType;
28 import org.springframework.http.ResponseEntity; 31 import org.springframework.http.ResponseEntity;
29 import org.springframework.util.StringUtils; 32 import org.springframework.util.StringUtils;
30 import org.springframework.web.bind.annotation.*; 33 import org.springframework.web.bind.annotation.*;
31 import org.springframework.web.context.request.async.DeferredResult; 34 import org.springframework.web.context.request.async.DeferredResult;
32 35
  36 +import javax.servlet.http.HttpServletResponse;
33 import javax.sip.DialogState; 37 import javax.sip.DialogState;
  38 +import java.io.*;
  39 +import java.nio.file.Files;
34 import java.util.*; 40 import java.util.*;
35 41
36 @Api(tags = "国标设备查询", value = "国标设备查询") 42 @Api(tags = "国标设备查询", value = "国标设备查询")
@@ -200,6 +206,11 @@ public class DeviceQuery { @@ -200,6 +206,11 @@ public class DeviceQuery {
200 Set<String> allKeys = dynamicTask.getAllKeys(); 206 Set<String> allKeys = dynamicTask.getAllKeys();
201 for (String key : allKeys) { 207 for (String key : allKeys) {
202 if (key.startsWith(deviceId)) { 208 if (key.startsWith(deviceId)) {
  209 + Runnable runnable = dynamicTask.get(key);
  210 + if (runnable instanceof ISubscribeTask) {
  211 + ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
  212 + subscribeTask.stop();
  213 + }
203 dynamicTask.stop(key); 214 dynamicTask.stop(key);
204 } 215 }
205 } 216 }
@@ -306,12 +317,7 @@ public class DeviceQuery { @@ -306,12 +317,7 @@ public class DeviceQuery {
306 public ResponseEntity<WVPResult<String>> updateDevice(Device device){ 317 public ResponseEntity<WVPResult<String>> updateDevice(Device device){
307 318
308 if (device != null && device.getDeviceId() != null) { 319 if (device != null && device.getDeviceId() != null) {
309 -  
310 -  
311 - // TODO 报警订阅相关的信息  
312 -  
313 deviceService.updateDevice(device); 320 deviceService.updateDevice(device);
314 -// cmder.deviceInfoQuery(device);  
315 } 321 }
316 WVPResult<String> result = new WVPResult<>(); 322 WVPResult<String> result = new WVPResult<>();
317 result.setCode(0); 323 result.setCode(0);
@@ -336,6 +342,11 @@ public class DeviceQuery { @@ -336,6 +342,11 @@ public class DeviceQuery {
336 Device device = storager.queryVideoDevice(deviceId); 342 Device device = storager.queryVideoDevice(deviceId);
337 String uuid = UUID.randomUUID().toString(); 343 String uuid = UUID.randomUUID().toString();
338 String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId; 344 String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId;
  345 + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L);
  346 + if(device == null) {
  347 + result.setResult(new ResponseEntity(String.format("设备%s不存在", deviceId),HttpStatus.OK));
  348 + return result;
  349 + }
339 cmder.deviceStatusQuery(device, event -> { 350 cmder.deviceStatusQuery(device, event -> {
340 RequestMessage msg = new RequestMessage(); 351 RequestMessage msg = new RequestMessage();
341 msg.setId(uuid); 352 msg.setId(uuid);
@@ -343,7 +354,6 @@ public class DeviceQuery { @@ -343,7 +354,6 @@ public class DeviceQuery {
343 msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg)); 354 msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg));
344 resultHolder.invokeResult(msg); 355 resultHolder.invokeResult(msg);
345 }); 356 });
346 - DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L);  
347 result.onTimeout(()->{ 357 result.onTimeout(()->{
348 logger.warn(String.format("获取设备状态超时")); 358 logger.warn(String.format("获取设备状态超时"));
349 // 释放rtpserver 359 // 释放rtpserver
@@ -456,4 +466,17 @@ public class DeviceQuery { @@ -456,4 +466,17 @@ public class DeviceQuery {
456 wvpResult.setData(dialogStateMap); 466 wvpResult.setData(dialogStateMap);
457 return wvpResult; 467 return wvpResult;
458 } 468 }
  469 +
  470 + @GetMapping("/snap/{deviceId}/{channelId}")
  471 + @ApiOperation(value = "请求截图", notes = "请求截图")
  472 + public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId) {
  473 +
  474 + try {
  475 + final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + ".jpg").toPath());
  476 + resp.setContentType(MediaType.IMAGE_PNG_VALUE);
  477 + IOUtils.copy(in, resp.getOutputStream());
  478 + } catch (IOException e) {
  479 + resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
  480 + }
  481 + }
459 } 482 }
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
@@ -72,7 +72,7 @@ public class GBRecordController { @@ -72,7 +72,7 @@ public class GBRecordController {
72 if (!DateUtil.verification(startTime, DateUtil.formatter)){ 72 if (!DateUtil.verification(startTime, DateUtil.formatter)){
73 WVPResult<RecordInfo> wvpResult = new WVPResult<>(); 73 WVPResult<RecordInfo> wvpResult = new WVPResult<>();
74 wvpResult.setCode(-1); 74 wvpResult.setCode(-1);
75 - wvpResult.setMsg("startTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss); 75 + wvpResult.setMsg("startTime error, format is " + DateUtil.PATTERN);
76 76
77 ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK); 77 ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK);
78 result.setResult(resultResponseEntity); 78 result.setResult(resultResponseEntity);
@@ -81,7 +81,7 @@ public class GBRecordController { @@ -81,7 +81,7 @@ public class GBRecordController {
81 if (!DateUtil.verification(endTime, DateUtil.formatter)){ 81 if (!DateUtil.verification(endTime, DateUtil.formatter)){
82 WVPResult<RecordInfo> wvpResult = new WVPResult<>(); 82 WVPResult<RecordInfo> wvpResult = new WVPResult<>();
83 wvpResult.setCode(-1); 83 wvpResult.setCode(-1);
84 - wvpResult.setMsg("endTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss); 84 + wvpResult.setMsg("endTime error, format is " + DateUtil.PATTERN);
85 ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK); 85 ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK);
86 result.setResult(resultResponseEntity); 86 result.setResult(resultResponseEntity);
87 return result; 87 return result;
src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java
@@ -76,14 +76,7 @@ public class LogController { @@ -76,14 +76,7 @@ public class LogController {
76 logger.warn("自动记录日志功能已关闭,查询结果可能不完整。"); 76 logger.warn("自动记录日志功能已关闭,查询结果可能不完整。");
77 } 77 }
78 78
79 - try {  
80 - if (startTime != null) {  
81 - DateUtil.format.parse(startTime);  
82 - }  
83 - if (endTime != null) {  
84 - DateUtil.format.parse(endTime);  
85 - }  
86 - } catch (ParseException e) { 79 + if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
87 return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); 80 return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
88 } 81 }
89 82
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java
@@ -146,8 +146,8 @@ public class ApiDeviceController { @@ -146,8 +146,8 @@ public class ApiDeviceController {
146 // 2-基于口令的双向认证, 146 // 2-基于口令的双向认证,
147 // 3-基于数字证书的双向认证 147 // 3-基于数字证书的双向认证
148 deviceJOSNChannel.put("Status", deviceChannel.getStatus()); 148 deviceJOSNChannel.put("Status", deviceChannel.getStatus());
149 - deviceJOSNChannel.put("Longitude", deviceChannel.getLongitude());  
150 - deviceJOSNChannel.put("Latitude", deviceChannel.getLatitude()); 149 + deviceJOSNChannel.put("Longitude", deviceChannel.getLongitudeWgs84());
  150 + deviceJOSNChannel.put("Latitude", deviceChannel.getLatitudeWgs84());
151 deviceJOSNChannel.put("PTZType ", deviceChannel.getPTZType()); // 云台类型, 0 - 未知, 1 - 球机, 2 - 半球, 151 deviceJOSNChannel.put("PTZType ", deviceChannel.getPTZType()); // 云台类型, 0 - 未知, 1 - 球机, 2 - 半球,
152 // 3 - 固定枪机, 4 - 遥控枪机 152 // 3 - 固定枪机, 4 - 遥控枪机
153 deviceJOSNChannel.put("CustomPTZType", ""); 153 deviceJOSNChannel.put("CustomPTZType", "");
src/main/resources/all-application.yml
@@ -32,7 +32,7 @@ spring: @@ -32,7 +32,7 @@ spring:
32 datasource: 32 datasource:
33 type: com.alibaba.druid.pool.DruidDataSource 33 type: com.alibaba.druid.pool.DruidDataSource
34 driver-class-name: com.mysql.cj.jdbc.Driver 34 driver-class-name: com.mysql.cj.jdbc.Driver
35 - url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false 35 + url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
36 username: root 36 username: root
37 password: root123 37 password: root123
38 druid: 38 druid:
src/main/resources/application-dev.yml
@@ -20,7 +20,7 @@ spring: @@ -20,7 +20,7 @@ spring:
20 datasource: 20 datasource:
21 type: com.alibaba.druid.pool.DruidDataSource 21 type: com.alibaba.druid.pool.DruidDataSource
22 driver-class-name: com.mysql.cj.jdbc.Driver 22 driver-class-name: com.mysql.cj.jdbc.Driver
23 - url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false 23 + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
24 username: root 24 username: root
25 password: 123456 25 password: 123456
26 druid: 26 druid:
src/main/resources/application-docker.yml
@@ -20,7 +20,7 @@ spring: @@ -20,7 +20,7 @@ spring:
20 datasource: 20 datasource:
21 # 使用mysql 打开23-28行注释, 删除29-36行 21 # 使用mysql 打开23-28行注释, 删除29-36行
22 name: wvp 22 name: wvp
23 - url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowMultiQueries=true&useSSL=false 23 + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowMultiQueries=true&useSSL=false&allowMultiQueries=true
24 username: root 24 username: root
25 password: root 25 password: root
26 type: com.alibaba.druid.pool.DruidDataSource 26 type: com.alibaba.druid.pool.DruidDataSource
src/test/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImplTest.java
@@ -8,6 +8,10 @@ import org.springframework.boot.test.context.SpringBootTest; @@ -8,6 +8,10 @@ import org.springframework.boot.test.context.SpringBootTest;
8 import org.springframework.test.context.junit4.SpringRunner; 8 import org.springframework.test.context.junit4.SpringRunner;
9 9
10 import javax.annotation.Resource; 10 import javax.annotation.Resource;
  11 +import java.time.Instant;
  12 +import java.time.LocalDateTime;
  13 +import java.time.ZoneOffset;
  14 +import java.time.temporal.TemporalAccessor;
11 import java.util.Date; 15 import java.util.Date;
12 16
13 17
@@ -64,8 +68,8 @@ class DeviceAlarmServiceImplTest { @@ -64,8 +68,8 @@ class DeviceAlarmServiceImplTest {
64 * * 7其他报警;可以为直接组合如12为电话报警或 设备报警- 68 * * 7其他报警;可以为直接组合如12为电话报警或 设备报警-
65 */ 69 */
66 deviceAlarm.setAlarmMethod((int)(Math.random()*7 + 1) + ""); 70 deviceAlarm.setAlarmMethod((int)(Math.random()*7 + 1) + "");
67 - Date date = randomDate("2021-01-01 00:00:00", "2021-06-01 00:00:00");  
68 - deviceAlarm.setAlarmTime(DateUtil.format.format(date)); 71 + Instant date = randomDate("2021-01-01 00:00:00", "2021-06-01 00:00:00");
  72 + deviceAlarm.setAlarmTime(DateUtil.formatter.format(date));
69 /** 73 /**
70 * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情- 74 * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情-
71 */ 75 */
@@ -85,17 +89,20 @@ class DeviceAlarmServiceImplTest { @@ -85,17 +89,20 @@ class DeviceAlarmServiceImplTest {
85 89
86 90
87 91
88 - private Date randomDate(String beginDate, String endDate) { 92 + private Instant randomDate(String beginDate, String endDate) {
89 try { 93 try {
90 94
91 - Date start = DateUtil.format.parse(beginDate);//构造开始日期  
92 - Date end = DateUtil.format.parse(endDate);//构造结束日期 95 + //构造开始日期
  96 + LocalDateTime start = LocalDateTime.parse(beginDate, DateUtil.formatter);
  97 +
  98 + //构造结束日期
  99 + LocalDateTime end = LocalDateTime.parse(endDate, DateUtil.formatter);
93 //getTime()表示返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。 100 //getTime()表示返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
94 - if (start.getTime() >= end.getTime()) { 101 + if (start.isAfter(end)) {
95 return null; 102 return null;
96 } 103 }
97 - long date = random(start.getTime(), end.getTime());  
98 - return new Date(date); 104 + long date = random(start.toInstant(ZoneOffset.of("+8")).toEpochMilli(), end.toInstant(ZoneOffset.of("+8")).toEpochMilli());
  105 + return Instant.ofEpochMilli(date);
99 } catch (Exception e) { 106 } catch (Exception e) {
100 e.printStackTrace(); 107 e.printStackTrace();
101 } 108 }
web_src/package.json
@@ -52,7 +52,7 @@ @@ -52,7 +52,7 @@
52 "postcss-url": "^7.2.1", 52 "postcss-url": "^7.2.1",
53 "rimraf": "^2.6.0", 53 "rimraf": "^2.6.0",
54 "semver": "^5.3.0", 54 "semver": "^5.3.0",
55 - "shelljs": "^0.7.6", 55 + "shelljs": "^0.8.5",
56 "uglifyjs-webpack-plugin": "^1.1.1", 56 "uglifyjs-webpack-plugin": "^1.1.1",
57 "url-loader": "^0.5.8", 57 "url-loader": "^0.5.8",
58 "vue-loader": "^13.3.0", 58 "vue-loader": "^13.3.0",
web_src/src/App.vue
@@ -76,7 +76,7 @@ body, @@ -76,7 +76,7 @@ body,
76 line-height: 60px; 76 line-height: 60px;
77 } 77 }
78 .el-main { 78 .el-main {
79 - background-color: #e9eef3; 79 + background-color: #f0f2f5;
80 color: #333; 80 color: #333;
81 text-align: center; 81 text-align: center;
82 padding-top: 0px !important; 82 padding-top: 0px !important;
@@ -101,4 +101,8 @@ body, @@ -101,4 +101,8 @@ body,
101 box-shadow: inset 0 0 6px rgba(0, 0, 0, .1); 101 box-shadow: inset 0 0 6px rgba(0, 0, 0, .1);
102 -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .1); 102 -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .1);
103 } 103 }
  104 +.table-header {
  105 + color: #727272;
  106 + font-weight: 600;
  107 +}
104 </style> 108 </style>
web_src/src/components/CloudRecord.vue
@@ -18,19 +18,17 @@ @@ -18,19 +18,17 @@
18 <div v-if="!recordDetail"> 18 <div v-if="!recordDetail">
19 19
20 <!--设备列表--> 20 <!--设备列表-->
21 - <el-table :data="recordList" border style="width: 100%" :height="winHeight">  
22 - <el-table-column prop="app" label="应用名" align="center"> 21 + <el-table :data="recordList" style="width: 100%" :height="winHeight">
  22 + <el-table-column prop="app" label="应用名" >
23 </el-table-column> 23 </el-table-column>
24 - <el-table-column prop="stream" label="流ID" align="center"> 24 + <el-table-column prop="stream" label="流ID" >
25 </el-table-column> 25 </el-table-column>
26 - <el-table-column prop="time" label="时间" align="center"> 26 + <el-table-column prop="time" label="时间" >
27 </el-table-column> 27 </el-table-column>
28 - <el-table-column label="操作" width="360" align="center" fixed="right"> 28 + <el-table-column label="操作" width="360" fixed="right">
29 <template slot-scope="scope"> 29 <template slot-scope="scope">
30 - <el-button-group>  
31 - <el-button size="mini" icon="el-icon-video-camera-solid" type="primary" @click="showRecordDetail(scope.row)">查看</el-button>  
32 - <!-- <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteRecord(scope.row)">删除</el-button>-->  
33 - </el-button-group> 30 + <el-button size="medium" icon="el-icon-folder-opened" type="text" @click="showRecordDetail(scope.row)">查看</el-button>
  31 + <!-- <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteRecord(scope.row)">删除</el-button>-->
34 </template> 32 </template>
35 </el-table-column> 33 </el-table-column>
36 </el-table> 34 </el-table>
web_src/src/components/DeviceList.vue
@@ -7,34 +7,33 @@ @@ -7,34 +7,33 @@
7 @click="getDeviceList()"></el-button> 7 @click="getDeviceList()"></el-button>
8 </div> 8 </div>
9 </div> 9 </div>
10 - <!-- <devicePlayer ref="devicePlayer"></devicePlayer> -->  
11 <!--设备列表--> 10 <!--设备列表-->
12 - <el-table :data="deviceList" border style="width: 100%;font-size: 12px;" :height="winHeight">  
13 - <el-table-column prop="name" label="名称" align="center"> 11 + <el-table :data="deviceList" style="width: 100%;font-size: 12px;" :height="winHeight" header-row-class-name="table-header">
  12 + <el-table-column prop="name" label="名称" min-width="160">
14 </el-table-column> 13 </el-table-column>
15 - <el-table-column prop="deviceId" label="设备编号" width="180" align="center"> 14 + <el-table-column prop="deviceId" label="设备编号" min-width="200" >
16 </el-table-column> 15 </el-table-column>
17 - <el-table-column label="地址" width="180" align="center"> 16 + <el-table-column label="地址" min-width="160" >
18 <template slot-scope="scope"> 17 <template slot-scope="scope">
19 <div slot="reference" class="name-wrapper"> 18 <div slot="reference" class="name-wrapper">
20 <el-tag size="medium">{{ scope.row.hostAddress }}</el-tag> 19 <el-tag size="medium">{{ scope.row.hostAddress }}</el-tag>
21 </div> 20 </div>
22 </template> 21 </template>
23 </el-table-column> 22 </el-table-column>
24 - <el-table-column prop="manufacturer" label="厂家" align="center"> 23 + <el-table-column prop="manufacturer" label="厂家" min-width="120" >
25 </el-table-column> 24 </el-table-column>
26 - <el-table-column label="流传输模式" align="center" width="120"> 25 + <el-table-column label="流传输模式" min-width="160" >
27 <template slot-scope="scope"> 26 <template slot-scope="scope">
28 - <el-select size="mini" @change="transportChange(scope.row)" v-model="scope.row.streamMode" placeholder="请选择"> 27 + <el-select size="mini" @change="transportChange(scope.row)" v-model="scope.row.streamMode" placeholder="请选择" style="width: 120px">
29 <el-option key="UDP" label="UDP" value="UDP"></el-option> 28 <el-option key="UDP" label="UDP" value="UDP"></el-option>
30 <el-option key="TCP-ACTIVE" label="TCP主动模式" :disabled="true" value="TCP-ACTIVE"></el-option> 29 <el-option key="TCP-ACTIVE" label="TCP主动模式" :disabled="true" value="TCP-ACTIVE"></el-option>
31 <el-option key="TCP-PASSIVE" label="TCP被动模式" value="TCP-PASSIVE"></el-option> 30 <el-option key="TCP-PASSIVE" label="TCP被动模式" value="TCP-PASSIVE"></el-option>
32 </el-select> 31 </el-select>
33 </template> 32 </template>
34 </el-table-column> 33 </el-table-column>
35 - <el-table-column prop="channelCount" label="通道数" align="center"> 34 + <el-table-column prop="channelCount" label="通道数" min-width="120" >
36 </el-table-column> 35 </el-table-column>
37 - <el-table-column label="状态" width="120" align="center"> 36 + <el-table-column label="状态" min-width="120">
38 <template slot-scope="scope"> 37 <template slot-scope="scope">
39 <div slot="reference" class="name-wrapper"> 38 <div slot="reference" class="name-wrapper">
40 <el-tag size="medium" v-if="scope.row.online == 1">在线</el-tag> 39 <el-tag size="medium" v-if="scope.row.online == 1">在线</el-tag>
@@ -42,30 +41,32 @@ @@ -42,30 +41,32 @@
42 </div> 41 </div>
43 </template> 42 </template>
44 </el-table-column> 43 </el-table-column>
45 - <el-table-column prop="keepaliveTime" label="最近心跳" align="center" width="140"> 44 + <el-table-column prop="keepaliveTime" label="最近心跳" min-width="160" >
46 </el-table-column> 45 </el-table-column>
47 - <el-table-column prop="registerTime" label="最近注册" align="center" width="140">  
48 - </el-table-column>  
49 - <el-table-column prop="updateTime" label="更新时间" align="center" width="140">  
50 - </el-table-column>  
51 - <el-table-column prop="createTime" label="创建时间" align="center" width="140"> 46 + <el-table-column prop="registerTime" label="最近注册" min-width="160">
52 </el-table-column> 47 </el-table-column>
  48 +<!-- <el-table-column prop="updateTime" label="更新时间" width="140">-->
  49 +<!-- </el-table-column>-->
  50 +<!-- <el-table-column prop="createTime" label="创建时间" width="140">-->
  51 +<!-- </el-table-column>-->
53 52
54 - <el-table-column label="操作" width="450" align="center" fixed="right"> 53 + <el-table-column label="操作" min-width="450" fixed="right">
55 <template slot-scope="scope"> 54 <template slot-scope="scope">
56 - <el-button size="mini" v-if="scope.row.online!=0" icon="el-icon-refresh" @click="refDevice(scope.row)" 55 + <el-button type="text" size="medium" v-bind:disabled="scope.row.online==0" icon="el-icon-refresh" @click="refDevice(scope.row)"
57 @mouseover="getTooltipContent(scope.row.deviceId)">刷新 56 @mouseover="getTooltipContent(scope.row.deviceId)">刷新
58 </el-button> 57 </el-button>
59 - <el-button-group>  
60 - <el-button size="mini" icon="el-icon-video-camera-solid" v-bind:disabled="scope.row.online==0"  
61 - type="primary" @click="showChannelList(scope.row)">通道  
62 - </el-button>  
63 - <el-button size="mini" icon="el-icon-location" v-bind:disabled="scope.row.online==0" type="primary"  
64 - @click="showDevicePosition(scope.row)">定位  
65 - </el-button>  
66 - <el-button size="mini" icon="el-icon-edit" type="primary" @click="edit(scope.row)">编辑</el-button>  
67 - <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteDevice(scope.row)">删除</el-button>  
68 - </el-button-group> 58 + <el-divider direction="vertical"></el-divider>
  59 + <el-button type="text" size="medium" icon="el-icon-video-camera" v-bind:disabled="scope.row.online==0"
  60 + @click="showChannelList(scope.row)">通道
  61 + </el-button>
  62 + <el-divider direction="vertical"></el-divider>
  63 + <el-button size="medium" icon="el-icon-location" v-bind:disabled="scope.row.online==0" type="text"
  64 + @click="showDevicePosition(scope.row)">定位
  65 + </el-button>
  66 + <el-divider direction="vertical"></el-divider>
  67 + <el-button size="medium" icon="el-icon-edit" type="text" @click="edit(scope.row)">编辑</el-button>
  68 + <el-divider direction="vertical"></el-divider>
  69 + <el-button size="medium" icon="el-icon-delete" type="text" @click="deleteDevice(scope.row)" style="color: #f56c6c">删除</el-button>
69 </template> 70 </template>
70 </el-table-column> 71 </el-table-column>
71 </el-table> 72 </el-table>
@@ -347,4 +348,5 @@ export default { @@ -347,4 +348,5 @@ export default {
347 padding: 0.3rem; 348 padding: 0.3rem;
348 width: 14.4rem; 349 width: 14.4rem;
349 } 350 }
  351 +
350 </style> 352 </style>
web_src/src/components/MediaServerManger.vue
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 <span style="font-size: 16px">{{item.id}}</span> 15 <span style="font-size: 16px">{{item.id}}</span>
16 <el-button v-if="!item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">编辑</el-button> 16 <el-button v-if="!item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">编辑</el-button>
17 <el-button v-if="item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">查看</el-button> 17 <el-button v-if="item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">查看</el-button>
18 - <el-button icon="el-icon-delete" style="margin-right: 10px;padding: 0;float: right;" type="text" @click="del(item)">移除</el-button> 18 + <el-button v-if="!item.defaultServer" icon="el-icon-delete" style="margin-right: 10px;padding: 0;float: right;" type="text" @click="del(item)">移除</el-button>
19 <div style="margin-top: 13px; line-height: 12px; "> 19 <div style="margin-top: 13px; line-height: 12px; ">
20 <span style="font-size: 14px; color: #999; margin-top: 5px; ">{{item.ip}}</span> 20 <span style="font-size: 14px; color: #999; margin-top: 5px; ">{{item.ip}}</span>
21 <span style="font-size: 14px; color: #999; margin-top: 5px; float: right;">{{item.createTime}}</span> 21 <span style="font-size: 14px; color: #999; margin-top: 5px; float: right;">{{item.createTime}}</span>
web_src/src/components/ParentPlatformList.vue
@@ -4,14 +4,15 @@ @@ -4,14 +4,15 @@
4 <div class="page-title">上级平台列表</div> 4 <div class="page-title">上级平台列表</div>
5 <div class="page-header-btn"> 5 <div class="page-header-btn">
6 <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addParentPlatform">添加</el-button> 6 <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addParentPlatform">添加</el-button>
  7 + <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
7 </div> 8 </div>
8 </div> 9 </div>
9 10
10 <!--设备列表--> 11 <!--设备列表-->
11 - <el-table :data="platformList" border style="width: 100%" :height="winHeight">  
12 - <el-table-column prop="name" label="名称" align="center"></el-table-column>  
13 - <el-table-column prop="serverGBId" label="平台编号" align="center"></el-table-column>  
14 - <el-table-column label="是否启用" width="120" align="center"> 12 + <el-table :data="platformList" style="width: 100%" :height="winHeight">
  13 + <el-table-column prop="name" label="名称" ></el-table-column>
  14 + <el-table-column prop="serverGBId" label="平台编号" min-width="200"></el-table-column>
  15 + <el-table-column label="是否启用" min-width="80" >
15 <template slot-scope="scope"> 16 <template slot-scope="scope">
16 <div slot="reference" class="name-wrapper"> 17 <div slot="reference" class="name-wrapper">
17 <el-tag size="medium" v-if="scope.row.enable">已启用</el-tag> 18 <el-tag size="medium" v-if="scope.row.enable">已启用</el-tag>
@@ -19,7 +20,7 @@ @@ -19,7 +20,7 @@
19 </div> 20 </div>
20 </template> 21 </template>
21 </el-table-column> 22 </el-table-column>
22 - <el-table-column label="状态" width="120" align="center"> 23 + <el-table-column label="状态" min-width="80" >
23 <template slot-scope="scope"> 24 <template slot-scope="scope">
24 <div slot="reference" class="name-wrapper"> 25 <div slot="reference" class="name-wrapper">
25 <el-tag size="medium" v-if="scope.row.status">在线</el-tag> 26 <el-tag size="medium" v-if="scope.row.status">在线</el-tag>
@@ -27,17 +28,17 @@ @@ -27,17 +28,17 @@
27 </div> 28 </div>
28 </template> 29 </template>
29 </el-table-column> 30 </el-table-column>
30 - <el-table-column label="地址" width="180" align="center"> 31 + <el-table-column label="地址" min-width="160" >
31 <template slot-scope="scope"> 32 <template slot-scope="scope">
32 <div slot="reference" class="name-wrapper"> 33 <div slot="reference" class="name-wrapper">
33 <el-tag size="medium">{{ scope.row.serverIP}}:{{scope.row.serverPort }}</el-tag> 34 <el-tag size="medium">{{ scope.row.serverIP}}:{{scope.row.serverPort }}</el-tag>
34 </div> 35 </div>
35 </template> 36 </template>
36 </el-table-column> 37 </el-table-column>
37 - <el-table-column prop="deviceGBId" label="设备国标编号" width="200" align="center"></el-table-column>  
38 - <el-table-column prop="transport" label="信令传输模式" width="120" align="center"></el-table-column>  
39 - <el-table-column prop="channelCount" label="通道数" width="120" align="center"></el-table-column>  
40 - <el-table-column label="订阅信息" width="240" align="center" fixed="right"> 38 + <el-table-column prop="deviceGBId" label="设备国标编号" min-width="200" ></el-table-column>
  39 + <el-table-column prop="transport" label="信令传输模式" min-width="120" ></el-table-column>
  40 + <el-table-column prop="channelCount" label="通道数" min-width="120" ></el-table-column>
  41 + <el-table-column label="订阅信息" min-width="120" fixed="right">
41 <template slot-scope="scope"> 42 <template slot-scope="scope">
42 <i v-if="scope.row.alarmSubscribe" style="font-size: 20px" title="报警订阅" class="iconfont icon-gbaojings subscribe-on " ></i> 43 <i v-if="scope.row.alarmSubscribe" style="font-size: 20px" title="报警订阅" class="iconfont icon-gbaojings subscribe-on " ></i>
43 <i v-if="!scope.row.alarmSubscribe" style="font-size: 20px" title="报警订阅" class="iconfont icon-gbaojings subscribe-off " ></i> 44 <i v-if="!scope.row.alarmSubscribe" style="font-size: 20px" title="报警订阅" class="iconfont icon-gbaojings subscribe-off " ></i>
@@ -48,11 +49,11 @@ @@ -48,11 +49,11 @@
48 </template> 49 </template>
49 </el-table-column> 50 </el-table-column>
50 51
51 - <el-table-column label="操作" width="300" align="center" fixed="right"> 52 + <el-table-column label="操作" min-width="240" fixed="right">
52 <template slot-scope="scope"> 53 <template slot-scope="scope">
53 - <el-button size="mini" icon="el-icon-edit" @click="editPlatform(scope.row)">编辑</el-button>  
54 - <el-button size="mini" icon="el-icon-share" type="primary" @click="chooseChannel(scope.row)">选择通道</el-button>  
55 - <el-button size="mini" icon="el-icon-delete" type="danger" @click="deletePlatform(scope.row)">删除</el-button> 54 + <el-button size="medium" icon="el-icon-edit" type="text" @click="editPlatform(scope.row)">编辑</el-button>
  55 + <el-button size="medium" icon="el-icon-share" type="text" @click="chooseChannel(scope.row)">选择通道</el-button>
  56 + <el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c" @click="deletePlatform(scope.row)">删除</el-button>
56 </template> 57 </template>
57 </el-table-column> 58 </el-table-column>
58 </el-table> 59 </el-table>
@@ -168,6 +169,9 @@ export default { @@ -168,6 +169,9 @@ export default {
168 console.log(error); 169 console.log(error);
169 }); 170 });
170 171
  172 + },
  173 + refresh: function (){
  174 + this.initData();
171 } 175 }
172 176
173 } 177 }
web_src/src/components/PushVideoList.vue
@@ -34,52 +34,54 @@ @@ -34,52 +34,54 @@
34 <el-button icon="el-icon-delete" size="mini" style="margin-right: 1rem;" 34 <el-button icon="el-icon-delete" size="mini" style="margin-right: 1rem;"
35 :disabled="multipleSelection.length === 0" type="danger" @click="batchDel">批量移除 35 :disabled="multipleSelection.length === 0" type="danger" @click="batchDel">批量移除
36 </el-button> 36 </el-button>
  37 + <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
37 </div> 38 </div>
38 </div> 39 </div>
39 <devicePlayer ref="devicePlayer"></devicePlayer> 40 <devicePlayer ref="devicePlayer"></devicePlayer>
40 <addStreamTOGB ref="addStreamTOGB"></addStreamTOGB> 41 <addStreamTOGB ref="addStreamTOGB"></addStreamTOGB>
41 - <el-table ref="pushListTable" :data="pushList" border style="width: 100%" :height="winHeight" 42 + <el-table ref="pushListTable" :data="pushList" style="width: 100%" :height="winHeight"
42 @selection-change="handleSelectionChange" :row-key="(row)=> row.app + row.stream"> 43 @selection-change="handleSelectionChange" :row-key="(row)=> row.app + row.stream">
43 - <el-table-column align="center" type="selection" :reserve-selection="true" width="55"> 44 + <el-table-column type="selection" :reserve-selection="true" min-width="55">
44 </el-table-column> 45 </el-table-column>
45 - <el-table-column prop="name" label="名称" align="center"> 46 + <el-table-column prop="name" label="名称" min-width="200">
46 </el-table-column> 47 </el-table-column>
47 - <el-table-column prop="app" label="APP" align="center"> 48 + <el-table-column prop="app" label="APP" min-width="200">
48 </el-table-column> 49 </el-table-column>
49 - <el-table-column prop="stream" label="流ID" align="center"> 50 + <el-table-column prop="stream" label="流ID" min-width="200">
50 </el-table-column> 51 </el-table-column>
51 - <el-table-column prop="gbId" label="国标编码" width="200" align="center"> 52 + <el-table-column prop="gbId" label="国标编码" min-width="200" >
52 </el-table-column> 53 </el-table-column>
53 - <el-table-column prop="mediaServerId" label="流媒体" width="200" align="center"> 54 + <el-table-column prop="mediaServerId" label="流媒体" min-width="200" >
54 </el-table-column> 55 </el-table-column>
55 - <el-table-column label="开始时间" align="center" width="200"> 56 + <el-table-column label="开始时间" min-width="200">
56 <template slot-scope="scope"> 57 <template slot-scope="scope">
57 <el-button-group> 58 <el-button-group>
58 {{ dateFormat(parseInt(scope.row.createStamp)) }} 59 {{ dateFormat(parseInt(scope.row.createStamp)) }}
59 </el-button-group> 60 </el-button-group>
60 </template> 61 </template>
61 </el-table-column> 62 </el-table-column>
62 - <el-table-column label="正在推流" align="center" width="100"> 63 + <el-table-column label="正在推流" min-width="100">
63 <template slot-scope="scope"> 64 <template slot-scope="scope">
64 {{ (scope.row.status == false && scope.row.gbId == null) || scope.row.status ? '是' : '否' }} 65 {{ (scope.row.status == false && scope.row.gbId == null) || scope.row.status ? '是' : '否' }}
65 </template> 66 </template>
66 </el-table-column> 67 </el-table-column>
67 68
68 - <el-table-column label="操作" width="360" align="center" fixed="right"> 69 + <el-table-column label="操作" min-width="360" fixed="right">
69 <template slot-scope="scope"> 70 <template slot-scope="scope">
70 - <el-button-group>  
71 - <el-button size="mini" icon="el-icon-video-play"  
72 - v-if="(scope.row.status == false && scope.row.gbId == null) || scope.row.status"  
73 - @click="playPush(scope.row)">播放  
74 - </el-button>  
75 - <el-button size="mini" icon="el-icon-delete" type="danger" @click="stopPush(scope.row)">移除</el-button>  
76 - <el-button size="mini" icon="el-icon-position" type="primary" v-if="!!!scope.row.gbId"  
77 - @click="addToGB(scope.row)">加入国标  
78 - </el-button>  
79 - <el-button size="mini" icon="el-icon-position" type="primary" v-if="!!scope.row.gbId"  
80 - @click="removeFromGB(scope.row)">移出国标  
81 - </el-button>  
82 - </el-button-group> 71 + <el-button size="medium" icon="el-icon-video-play"
  72 + v-if="(scope.row.status == false && scope.row.gbId == null) || scope.row.status"
  73 + @click="playPush(scope.row)" type="text">播放
  74 + </el-button>
  75 + <el-divider direction="vertical"></el-divider>
  76 + <el-button size="medium" icon="el-icon-delete" type="text" @click="stopPush(scope.row)" style="color: #f56c6c" >移除</el-button>
  77 + <el-divider direction="vertical"></el-divider>
  78 + <el-button size="medium" icon="el-icon-position" type="text" v-if="!!!scope.row.gbId"
  79 + @click="addToGB(scope.row)">加入国标
  80 + </el-button>
  81 + <el-divider v-if="!!!scope.row.gbId" direction="vertical"></el-divider>
  82 + <el-button size="medium" icon="el-icon-position" type="text" v-if="!!scope.row.gbId"
  83 + @click="removeFromGB(scope.row)">移出国标
  84 + </el-button>
83 </template> 85 </template>
84 </el-table-column> 86 </el-table-column>
85 </el-table> 87 </el-table>
@@ -284,6 +286,9 @@ export default { @@ -284,6 +286,9 @@ export default {
284 handleSelectionChange: function (val) { 286 handleSelectionChange: function (val) {
285 this.multipleSelection = val; 287 this.multipleSelection = val;
286 }, 288 },
  289 + refresh: function () {
  290 + this.initData();
  291 + },
287 } 292 }
288 }; 293 };
289 </script> 294 </script>
web_src/src/components/StreamProxyList.vue
@@ -5,14 +5,15 @@ @@ -5,14 +5,15 @@
5 <div class="page-header-btn"> 5 <div class="page-header-btn">
6 <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addStreamProxy">添加代理</el-button> 6 <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addStreamProxy">添加代理</el-button>
7 <el-button v-if="false" icon="el-icon-search" size="mini" style="margin-right: 1rem;" type="primary" @click="addOnvif">搜索ONVIF</el-button> 7 <el-button v-if="false" icon="el-icon-search" size="mini" style="margin-right: 1rem;" type="primary" @click="addOnvif">搜索ONVIF</el-button>
  8 + <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
8 </div> 9 </div>
9 </div> 10 </div>
10 <devicePlayer ref="devicePlayer"></devicePlayer> 11 <devicePlayer ref="devicePlayer"></devicePlayer>
11 - <el-table :data="streamProxyList" border style="width: 100%" :height="winHeight">  
12 - <el-table-column prop="name" label="名称" align="center" show-overflow-tooltip/>  
13 - <el-table-column prop="app" label="流应用名" align="center" show-overflow-tooltip/>  
14 - <el-table-column prop="stream" label="流ID" align="center" show-overflow-tooltip/>  
15 - <el-table-column label="流地址" width="400" align="center" show-overflow-tooltip > 12 + <el-table :data="streamProxyList" style="width: 100%" :height="winHeight">
  13 + <el-table-column prop="name" label="名称" min-width="120" show-overflow-tooltip/>
  14 + <el-table-column prop="app" label="流应用名" min-width="120" show-overflow-tooltip/>
  15 + <el-table-column prop="stream" label="流ID" min-width="120" show-overflow-tooltip/>
  16 + <el-table-column label="流地址" min-width="400" show-overflow-tooltip >
16 <template slot-scope="scope"> 17 <template slot-scope="scope">
17 <div slot="reference" class="name-wrapper"> 18 <div slot="reference" class="name-wrapper">
18 19
@@ -27,8 +28,8 @@ @@ -27,8 +28,8 @@
27 </div> 28 </div>
28 </template> 29 </template>
29 </el-table-column> 30 </el-table-column>
30 - <el-table-column prop="mediaServerId" label="流媒体" width="150" align="center"></el-table-column>  
31 - <el-table-column label="类型" width="100" align="center"> 31 + <el-table-column prop="mediaServerId" label="流媒体" min-width="180" ></el-table-column>
  32 + <el-table-column label="类型" width="100" >
32 <template slot-scope="scope"> 33 <template slot-scope="scope">
33 <div slot="reference" class="name-wrapper"> 34 <div slot="reference" class="name-wrapper">
34 <el-tag size="medium">{{scope.row.type}}</el-tag> 35 <el-tag size="medium">{{scope.row.type}}</el-tag>
@@ -36,8 +37,8 @@ @@ -36,8 +37,8 @@
36 </template> 37 </template>
37 </el-table-column> 38 </el-table-column>
38 39
39 - <el-table-column prop="gbId" label="国标编码" width="180" align="center" show-overflow-tooltip/>  
40 - <el-table-column label="状态" width="120" align="center"> 40 + <el-table-column prop="gbId" label="国标编码" min-width="180" show-overflow-tooltip/>
  41 + <el-table-column label="状态" min-width="120" >
41 <template slot-scope="scope"> 42 <template slot-scope="scope">
42 <div slot="reference" class="name-wrapper"> 43 <div slot="reference" class="name-wrapper">
43 <el-tag size="medium" v-if="scope.row.status">在线</el-tag> 44 <el-tag size="medium" v-if="scope.row.status">在线</el-tag>
@@ -45,7 +46,7 @@ @@ -45,7 +46,7 @@
45 </div> 46 </div>
46 </template> 47 </template>
47 </el-table-column> 48 </el-table-column>
48 - <el-table-column label="启用" width="120" align="center"> 49 + <el-table-column label="启用" min-width="120" >
49 <template slot-scope="scope"> 50 <template slot-scope="scope">
50 <div slot="reference" class="name-wrapper"> 51 <div slot="reference" class="name-wrapper">
51 <el-tag size="medium" v-if="scope.row.enable">已启用</el-tag> 52 <el-tag size="medium" v-if="scope.row.enable">已启用</el-tag>
@@ -53,8 +54,8 @@ @@ -53,8 +54,8 @@
53 </div> 54 </div>
54 </template> 55 </template>
55 </el-table-column> 56 </el-table-column>
56 - <el-table-column prop="createTime" label="创建时间" align="center" width="150" show-overflow-tooltip/>  
57 - <el-table-column label="转HLS" width="120" align="center"> 57 + <el-table-column prop="createTime" label="创建时间" min-width="150" show-overflow-tooltip/>
  58 + <el-table-column label="转HLS" min-width="120" >
58 <template slot-scope="scope"> 59 <template slot-scope="scope">
59 <div slot="reference" class="name-wrapper"> 60 <div slot="reference" class="name-wrapper">
60 <el-tag size="medium" v-if="scope.row.enable_hls">已启用</el-tag> 61 <el-tag size="medium" v-if="scope.row.enable_hls">已启用</el-tag>
@@ -62,7 +63,7 @@ @@ -62,7 +63,7 @@
62 </div> 63 </div>
63 </template> 64 </template>
64 </el-table-column> 65 </el-table-column>
65 - <el-table-column label="MP4录制" width="120" align="center"> 66 + <el-table-column label="MP4录制" min-width="120" >
66 <template slot-scope="scope"> 67 <template slot-scope="scope">
67 <div slot="reference" class="name-wrapper"> 68 <div slot="reference" class="name-wrapper">
68 <el-tag size="medium" v-if="scope.row.enable_mp4">已启用</el-tag> 69 <el-tag size="medium" v-if="scope.row.enable_mp4">已启用</el-tag>
@@ -70,7 +71,7 @@ @@ -70,7 +71,7 @@
70 </div> 71 </div>
71 </template> 72 </template>
72 </el-table-column> 73 </el-table-column>
73 - <el-table-column label="无人观看自动删除" width="160" align="center"> 74 + <el-table-column label="无人观看自动删除" min-width="160" >
74 <template slot-scope="scope"> 75 <template slot-scope="scope">
75 <div slot="reference" class="name-wrapper"> 76 <div slot="reference" class="name-wrapper">
76 <el-tag size="medium" v-if="scope.row.enable_remove_none_reader">已启用</el-tag> 77 <el-tag size="medium" v-if="scope.row.enable_remove_none_reader">已启用</el-tag>
@@ -80,14 +81,15 @@ @@ -80,14 +81,15 @@
80 </el-table-column> 81 </el-table-column>
81 82
82 83
83 - <el-table-column label="操作" width="360" align="center" fixed="right"> 84 + <el-table-column label="操作" width="360" fixed="right">
84 <template slot-scope="scope"> 85 <template slot-scope="scope">
85 - <el-button-group>  
86 - <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.enable" @click="play(scope.row)">播放</el-button>  
87 - <el-button size="mini" icon="el-icon-close" type="success" v-if="scope.row.enable" @click="stop(scope.row)">停用</el-button>  
88 - <el-button size="mini" icon="el-icon-check" type="primary" :loading="startBtnLaoding" v-if="!scope.row.enable" @click="start(scope.row)">启用</el-button>  
89 - <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteStreamProxy(scope.row)">删除</el-button>  
90 - </el-button-group> 86 + <el-button size="medium" icon="el-icon-video-play" type="text" v-if="scope.row.enable" @click="play(scope.row)">播放</el-button>
  87 + <el-divider direction="vertical"></el-divider>
  88 + <el-button size="medium" icon="el-icon-switch-button" type="text" v-if="scope.row.enable" @click="stop(scope.row)">停用</el-button>
  89 + <el-divider direction="vertical"></el-divider>
  90 + <el-button size="medium" icon="el-icon-check" type="text" :loading="startBtnLaoding" v-if="!scope.row.enable" @click="start(scope.row)">启用</el-button>
  91 + <el-divider v-if="!scope.row.enable" direction="vertical"></el-divider>
  92 + <el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c" @click="deleteStreamProxy(scope.row)">删除</el-button>
91 </template> 93 </template>
92 </el-table-column> 94 </el-table-column>
93 </el-table> 95 </el-table>
@@ -305,8 +307,10 @@ @@ -305,8 +307,10 @@
305 console.log(error); 307 console.log(error);
306 that.getListLoading = false; 308 that.getListLoading = false;
307 }); 309 });
308 - }  
309 - 310 + },
  311 + refresh: function (){
  312 + this.initData();
  313 + }
310 } 314 }
311 }; 315 };
312 </script> 316 </script>
web_src/src/components/channelList.vue
@@ -2,10 +2,10 @@ @@ -2,10 +2,10 @@
2 <div id="channelList" style="width: 100%"> 2 <div id="channelList" style="width: 100%">
3 <div class="page-header"> 3 <div class="page-header">
4 <div class="page-title"> 4 <div class="page-title">
5 - <el-button icon="el-icon-arrow-left" size="mini" style="margin-right: 1rem;" type="primary" @click="showDevice">  
6 - 返回  
7 - </el-button>  
8 - 通道列表({{ parentChannelId == 0 ? deviceId : parentChannelId }})</div> 5 + <el-button icon="el-icon-back" size="mini" style="font-size: 20px; color: #000;" type="text" @click="showDevice" ></el-button>
  6 + <el-divider direction="vertical"></el-divider>
  7 + 通道列表
  8 + </div>
9 <div class="page-header-btn"> 9 <div class="page-header-btn">
10 搜索: 10 搜索:
11 <el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字" 11 <el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
@@ -25,84 +25,85 @@ @@ -25,84 +25,85 @@
25 <el-option label="在线" value="true"></el-option> 25 <el-option label="在线" value="true"></el-option>
26 <el-option label="离线" value="false"></el-option> 26 <el-option label="离线" value="false"></el-option>
27 </el-select> 27 </el-select>
28 - <el-checkbox size="mini" v-model="autoList" @change="autoListChange">  
29 - 自动刷新  
30 - </el-checkbox> 28 + <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
31 </div> 29 </div>
32 </div> 30 </div>
33 <devicePlayer ref="devicePlayer" v-loading="isLoging"></devicePlayer> 31 <devicePlayer ref="devicePlayer" v-loading="isLoging"></devicePlayer>
34 <!--设备列表--> 32 <!--设备列表-->
35 - <el-table ref="channelListTable" :data="deviceChannelList" :height="winHeight" border style="width: 100%">  
36 - <el-table-column prop="channelId" label="通道编号" width="200"> 33 + <el-table ref="channelListTable" :data="deviceChannelList" :height="winHeight" style="width: 100%" header-row-class-name="table-header">
  34 + <el-table-column prop="channelId" label="通道编号" min-width="200">
37 </el-table-column> 35 </el-table-column>
38 - <el-table-column prop="name" label="通道名称"> 36 + <el-table-column prop="deviceId" label="设备编号" min-width="200">
39 </el-table-column> 37 </el-table-column>
40 - <el-table-column label="快照" width="80" align="center">  
41 - <template slot-scope="scope">  
42 - <img style="max-height: 3rem;max-width: 4rem;"  
43 - v-if="scope.row.subCount === 0 && scope.row.parental === 0"  
44 - :id="scope.row.deviceId + '_' + scope.row.channelId"  
45 - :src="getSnap(scope.row)"  
46 - @error="getSnapErrorEvent($event.target.id)"  
47 - alt="">  
48 - <!-- <el-image-->  
49 - <!-- :id="'snapImg_' + scope.row.deviceId + '_' + scope.row.channelId"-->  
50 - <!-- :src="getSnap(scope.row)"-->  
51 - <!-- @error="getSnapErrorEvent($event, scope.row)"-->  
52 - <!-- :fit="'contain'">-->  
53 - <!-- <div slot="error" class="image-slot">-->  
54 - <!-- <i class="el-icon-picture-outline"></i>-->  
55 - <!-- </div>-->  
56 - <!-- </el-image>--> 38 + <el-table-column prop="name" label="通道名称" min-width="200">
  39 + </el-table-column>
  40 + <el-table-column label="快照" min-width="120">
  41 + <template v-slot:default="scope">
  42 + <el-image
  43 + :src="getSnap(scope.row)"
  44 + :preview-src-list="getBigSnap(scope.row)"
  45 + @error="getSnapErrorEvent(scope.row.deviceId, scope.row.channelId)"
  46 + :fit="'contain'"
  47 + style="width: 60px">
  48 + <div slot="error" class="image-slot">
  49 + <i class="el-icon-picture-outline"></i>
  50 + </div>
  51 + </el-image>
57 </template> 52 </template>
58 </el-table-column> 53 </el-table-column>
59 - <el-table-column prop="subCount" label="子节点数"> 54 + <el-table-column prop="subCount" label="子节点数" min-width="120">
60 </el-table-column> 55 </el-table-column>
61 - <el-table-column prop="manufacture" label="厂家"> 56 + <el-table-column prop="manufacture" label="厂家" min-width="120">
62 </el-table-column> 57 </el-table-column>
63 - <el-table-column label="位置信息" align="center"> 58 + <el-table-column label="位置信息" min-width="200">
64 <template slot-scope="scope"> 59 <template slot-scope="scope">
65 - <span>{{ scope.row.longitude }},{{ scope.row.latitude }}</span> 60 + <span v-if="scope.row.longitude*scope.row.latitude > 0">{{ scope.row.longitude }},<br>{{ scope.row.latitude }}</span>
  61 + <span v-if="scope.row.longitude*scope.row.latitude === 0">无</span>
66 </template> 62 </template>
67 </el-table-column> 63 </el-table-column>
68 - <el-table-column prop="ptztypeText" label="云台类型"/>  
69 - <el-table-column label="开启音频" align="center"> 64 + <el-table-column prop="ptztypeText" label="云台类型" min-width="120"/>
  65 + <el-table-column label="开启音频" min-width="120">
70 <template slot-scope="scope"> 66 <template slot-scope="scope">
71 <el-switch @change="updateChannel(scope.row)" v-model="scope.row.hasAudio" active-color="#409EFF"> 67 <el-switch @change="updateChannel(scope.row)" v-model="scope.row.hasAudio" active-color="#409EFF">
72 </el-switch> 68 </el-switch>
73 </template> 69 </template>
74 </el-table-column> 70 </el-table-column>
75 - <el-table-column label="状态" width="180" align="center"> 71 + <el-table-column label="状态" min-width="120">
76 <template slot-scope="scope"> 72 <template slot-scope="scope">
77 <div slot="reference" class="name-wrapper"> 73 <div slot="reference" class="name-wrapper">
78 - <el-tag size="medium" v-if="scope.row.status == 1">开启</el-tag>  
79 - <el-tag size="medium" type="info" v-if="scope.row.status == 0">关闭</el-tag> 74 + <el-tag size="medium" v-if="scope.row.status === 1">在线</el-tag>
  75 + <el-tag size="medium" type="info" v-if="scope.row.status === 0">离线</el-tag>
80 </div> 76 </div>
81 </template> 77 </template>
82 </el-table-column> 78 </el-table-column>
83 79
84 80
85 - <el-table-column label="操作" width="280" align="center" fixed="right"> 81 + <el-table-column label="操作" min-width="280" fixed="right">
86 <template slot-scope="scope"> 82 <template slot-scope="scope">
87 - <el-button-group>  
88 - <!-- <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> -->  
89 - <el-button size="mini" icon="el-icon-video-play" @click="sendDevicePush(scope.row)">播放</el-button>  
90 - <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="!!scope.row.streamId"  
91 - @click="stopDevicePush(scope.row)">停止  
92 - </el-button>  
93 - <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.subCount > 0 || scope.row.parental === 1"  
94 - @click="changeSubchannel(scope.row)">查看  
95 - </el-button>  
96 - <el-button size="mini" icon="el-icon-video-camera" type="primary" @click="queryRecords(scope.row)">设备录像  
97 - </el-button>  
98 - <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> -->  
99 - </el-button-group> 83 + <!-- <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> -->
  84 + <el-button size="medium" icon="el-icon-video-play" type="text" @click="sendDevicePush(scope.row)">播放</el-button>
  85 + <el-button size="medium" icon="el-icon-switch-button" type="text" style="color: #f56c6c" v-if="!!scope.row.streamId"
  86 + @click="stopDevicePush(scope.row)">停止
  87 + </el-button>
  88 + <el-divider direction="vertical"></el-divider>
  89 + <el-button size="medium" icon="el-icon-s-open" type="text" v-if="scope.row.subCount > 0 || scope.row.parental === 1"
  90 + @click="changeSubchannel(scope.row)">查看
  91 + </el-button>
  92 + <el-divider v-if="scope.row.subCount > 0 || scope.row.parental === 1" direction="vertical"></el-divider>
  93 + <el-button size="medium" icon="el-icon-video-camera" type="text" @click="queryRecords(scope.row)">设备录像
  94 + </el-button>
100 </template> 95 </template>
101 </el-table-column> 96 </el-table-column>
102 </el-table> 97 </el-table>
103 - <el-pagination style="float: right" @size-change="handleSizeChange" @current-change="currentChange"  
104 - :current-page="currentPage" :page-size="count" :page-sizes="[15, 20, 30, 50]"  
105 - layout="total, sizes, prev, pager, next" :total="total"> 98 + <el-pagination
  99 + style="float: right"
  100 + @size-change="handleSizeChange"
  101 + @current-change="currentChange"
  102 + :current-page="currentPage"
  103 + :page-size="count"
  104 + :page-sizes="[15, 25, 35, 50]"
  105 + layout="total, sizes, prev, pager, next"
  106 + :total="total">
106 </el-pagination> 107 </el-pagination>
107 </div> 108 </div>
108 </template> 109 </template>
@@ -111,6 +112,8 @@ @@ -111,6 +112,8 @@
111 import devicePlayer from './dialog/devicePlayer.vue' 112 import devicePlayer from './dialog/devicePlayer.vue'
112 import uiHeader from '../layout/UiHeader.vue' 113 import uiHeader from '../layout/UiHeader.vue'
113 import moment from "moment"; 114 import moment from "moment";
  115 +import DviceService from "./service/DeviceService";
  116 +import DeviceService from "./service/DeviceService";
114 117
115 export default { 118 export default {
116 name: 'channelList', 119 name: 'channelList',
@@ -120,6 +123,8 @@ export default { @@ -120,6 +123,8 @@ export default {
120 }, 123 },
121 data() { 124 data() {
122 return { 125 return {
  126 + deviceService: new DeviceService(),
  127 + device: null,
123 deviceId: this.$route.params.deviceId, 128 deviceId: this.$route.params.deviceId,
124 parentChannelId: this.$route.params.parentChannelId, 129 parentChannelId: this.$route.params.parentChannelId,
125 deviceChannelList: [], 130 deviceChannelList: [],
@@ -135,16 +140,21 @@ export default { @@ -135,16 +140,21 @@ export default {
135 total: 0, 140 total: 0,
136 beforeUrl: "/deviceList", 141 beforeUrl: "/deviceList",
137 isLoging: false, 142 isLoging: false,
138 - autoList: true,  
139 loadSnap: {} 143 loadSnap: {}
140 }; 144 };
141 }, 145 },
142 146
143 mounted() { 147 mounted() {
144 - this.initData();  
145 - if (this.autoList) {  
146 - this.updateLooper = setInterval(this.initData, 5000); 148 + if (this.deviceId) {
  149 + this.deviceService.getDevice(this.deviceId, (result)=>{
  150 + this.device = result;
  151 +
  152 + }, (error)=>{
  153 + console.log("获取设备信息失败")
  154 + console.error(error)
  155 + })
147 } 156 }
  157 + this.initData();
148 158
149 }, 159 },
150 destroyed() { 160 destroyed() {
@@ -177,12 +187,8 @@ export default { @@ -177,12 +187,8 @@ export default {
177 }) 187 })
178 }, 188 },
179 handleSizeChange: function (val) { 189 handleSizeChange: function (val) {
180 - var url = `/${this.$router.currentRoute.name}/${this.$router.params.deviceId}/${this.$router.params.parentChannelId}/${val}/1`  
181 - this.$router.push(url).then(() => {  
182 - this.initParam();  
183 - this.initData();  
184 - })  
185 - 190 + this.count = val;
  191 + this.getDeviceChannelList();
186 }, 192 },
187 getDeviceChannelList: function () { 193 getDeviceChannelList: function () {
188 let that = this; 194 let that = this;
@@ -227,7 +233,7 @@ export default { @@ -227,7 +233,7 @@ export default {
227 setTimeout(() => { 233 setTimeout(() => {
228 234
229 let snapId = deviceId + "_" + channelId; 235 let snapId = deviceId + "_" + channelId;
230 - that.loadSnap[snapId] = 0; 236 + that.loadSnap[deviceId + channelId] = 0;
231 that.getSnapErrorEvent(snapId) 237 that.getSnapErrorEvent(snapId)
232 }, 5000) 238 }, 5000)
233 that.$refs.devicePlayer.openDialog("media", deviceId, channelId, { 239 that.$refs.devicePlayer.openDialog("media", deviceId, channelId, {
@@ -269,19 +275,24 @@ export default { @@ -269,19 +275,24 @@ export default {
269 }); 275 });
270 }, 276 },
271 getSnap: function (row) { 277 getSnap: function (row) {
272 - return '/static/snap/' + row.deviceId + '_' + row.channelId + '.jpg' 278 + let url = (process.env.NODE_ENV === 'development'? "debug": "") + '/api/device/query/snap/' + row.deviceId + '/' + row.channelId
  279 + return url
273 }, 280 },
274 - getSnapErrorEvent: function (id) { 281 + getBigSnap: function (row) {
  282 + return [this.getSnap(row)]
  283 + },
  284 + getSnapErrorEvent: function (deviceId, channelId) {
275 285
276 - if (typeof (this.loadSnap[id]) != "undefined") {  
277 - console.log("下载截图" + this.loadSnap[id])  
278 - if (this.loadSnap[id] > 5) {  
279 - delete this.loadSnap[id]; 286 + if (typeof (this.loadSnap[deviceId + channelId]) != "undefined") {
  287 + console.log("下载截图" + this.loadSnap[deviceId + channelId])
  288 + if (this.loadSnap[deviceId + channelId] > 5) {
  289 + delete this.loadSnap[deviceId + channelId];
280 return; 290 return;
281 } 291 }
282 setTimeout(() => { 292 setTimeout(() => {
283 - this.loadSnap[id]++  
284 - document.getElementById(id).setAttribute("src", '/static/snap/' + id + '.jpg?' + new Date().getTime()) 293 + let url = (process.env.NODE_ENV === 'development'? "debug": "") + '/api/device/query/snap/' + deviceId + '/' + channelId
  294 + this.loadSnap[deviceId + channelId]++
  295 + document.getElementById(deviceId + channelId).setAttribute("src", url + '?' + new Date().getTime())
285 }, 1000) 296 }, 1000)
286 297
287 } 298 }
@@ -342,12 +353,8 @@ export default { @@ -342,12 +353,8 @@ export default {
342 console.log(JSON.stringify(res)); 353 console.log(JSON.stringify(res));
343 }); 354 });
344 }, 355 },
345 - autoListChange: function () {  
346 - if (this.autoList) {  
347 - this.updateLooper = setInterval(this.initData, 1500);  
348 - } else {  
349 - window.clearInterval(this.updateLooper);  
350 - } 356 + refresh: function () {
  357 + this.initData();
351 } 358 }
352 359
353 } 360 }
web_src/src/components/common/DeviceTree.vue
@@ -84,22 +84,34 @@ export default { @@ -84,22 +84,34 @@ export default {
84 }else { 84 }else {
85 resolve([]) 85 resolve([])
86 } 86 }
  87 + }, (list)=>{
  88 + console.log("设备加载完成")
87 }, (error)=>{ 89 }, (error)=>{
88 90
89 }) 91 })
90 } 92 }
91 if (node.level === 1) { 93 if (node.level === 1) {
92 - this.deviceService.getAllChannel(true, true, node.data.id, (catalogData) => {  
93 - this.deviceService.getAllChannel(false, true, node.data.id, (channelData) => {  
94 - let data = catalogData.concat(channelData)  
95 - this.channelDataHandler(data, resolve) 94 + let channelArray = []
  95 + this.deviceService.getAllChannel(true, true, node.data.id, catalogData =>{
  96 + channelArray = channelArray.concat(catalogData)
  97 + this.channelDataHandler(channelArray, resolve)
  98 + },(endCatalogData) => {
  99 + this.deviceService.getAllChannel(false, true, node.data.id, channelData => {
  100 + channelArray = channelArray.concat(channelData)
  101 + this.channelDataHandler(channelArray, resolve)
  102 + }, endChannelList => {
  103 +
96 }) 104 })
97 }) 105 })
98 }else if (node.level > 1){ 106 }else if (node.level > 1){
  107 + let channelArray = []
99 this.deviceService.getAllSubChannel(true, node.data.deviceId, node.data.id, (catalogData)=>{ 108 this.deviceService.getAllSubChannel(true, node.data.deviceId, node.data.id, (catalogData)=>{
  109 + channelArray = channelArray.concat(catalogData)
  110 + this.channelDataHandler(channelArray, resolve)
  111 + }, (endCatalogData)=>{
100 this.deviceService.getAllSubChannel(false, node.data.deviceId, node.data.id, (channelData)=>{ 112 this.deviceService.getAllSubChannel(false, node.data.deviceId, node.data.id, (channelData)=>{
101 - let data = catalogData.concat(channelData)  
102 - this.channelDataHandler(data, resolve) 113 + channelArray = channelArray.concat(channelData)
  114 + this.channelDataHandler(channelArray, resolve)
103 }) 115 })
104 }) 116 })
105 } 117 }
web_src/src/components/common/jessibuca.vue
@@ -23,11 +23,11 @@ @@ -23,11 +23,11 @@
23 </template> 23 </template>
24 24
25 <script> 25 <script>
  26 +let jessibucaPlayer = {};
26 export default { 27 export default {
27 name: 'jessibuca', 28 name: 'jessibuca',
28 data() { 29 data() {
29 return { 30 return {
30 - jessibuca: null,  
31 playing: false, 31 playing: false,
32 isNotMute: false, 32 isNotMute: false,
33 quieting: false, 33 quieting: false,
@@ -49,6 +49,7 @@ export default { @@ -49,6 +49,7 @@ export default {
49 window.onerror = (msg) => { 49 window.onerror = (msg) => {
50 // console.error(msg) 50 // console.error(msg)
51 }; 51 };
  52 + console.log(this._uid)
52 let paramUrl = decodeURIComponent(this.$route.params.url) 53 let paramUrl = decodeURIComponent(this.$route.params.url)
53 this.$nextTick(() => { 54 this.$nextTick(() => {
54 this.updatePlayerDomSize() 55 this.updatePlayerDomSize()
@@ -88,7 +89,7 @@ export default { @@ -88,7 +89,7 @@ export default {
88 let options = {}; 89 let options = {};
89 console.log("hasAudio " + this.hasAudio) 90 console.log("hasAudio " + this.hasAudio)
90 91
91 - this.jessibuca = new window.Jessibuca(Object.assign( 92 + jessibucaPlayer[this._uid] = new window.Jessibuca(Object.assign(
92 { 93 {
93 container: this.$refs.container, 94 container: this.$refs.container,
94 videoBuffer: 0.2, // 最大缓冲时长,单位秒 95 videoBuffer: 0.2, // 最大缓冲时长,单位秒
@@ -117,70 +118,70 @@ export default { @@ -117,70 +118,70 @@ export default {
117 }, 118 },
118 options 119 options
119 )); 120 ));
120 - 121 + let jessibuca = jessibucaPlayer[this._uid];
121 let _this = this; 122 let _this = this;
122 - this.jessibuca.on("load", function () { 123 + jessibuca.on("load", function () {
123 console.log("on load init"); 124 console.log("on load init");
124 }); 125 });
125 126
126 - this.jessibuca.on("log", function (msg) { 127 + jessibuca.on("log", function (msg) {
127 console.log("on log", msg); 128 console.log("on log", msg);
128 }); 129 });
129 - this.jessibuca.on("record", function (msg) { 130 + jessibuca.on("record", function (msg) {
130 console.log("on record:", msg); 131 console.log("on record:", msg);
131 }); 132 });
132 - this.jessibuca.on("pause", function () { 133 + jessibuca.on("pause", function () {
133 _this.playing = false; 134 _this.playing = false;
134 }); 135 });
135 - this.jessibuca.on("play", function () { 136 + jessibuca.on("play", function () {
136 _this.playing = true; 137 _this.playing = true;
137 }); 138 });
138 - this.jessibuca.on("fullscreen", function (msg) { 139 + jessibuca.on("fullscreen", function (msg) {
139 console.log("on fullscreen", msg); 140 console.log("on fullscreen", msg);
140 _this.fullscreen = msg 141 _this.fullscreen = msg
141 }); 142 });
142 143
143 - this.jessibuca.on("mute", function (msg) { 144 + jessibuca.on("mute", function (msg) {
144 console.log("on mute", msg); 145 console.log("on mute", msg);
145 _this.isNotMute = !msg; 146 _this.isNotMute = !msg;
146 }); 147 });
147 - this.jessibuca.on("audioInfo", function (msg) { 148 + jessibuca.on("audioInfo", function (msg) {
148 // console.log("audioInfo", msg); 149 // console.log("audioInfo", msg);
149 }); 150 });
150 151
151 - this.jessibuca.on("videoInfo", function (msg) { 152 + jessibuca.on("videoInfo", function (msg) {
152 // this.videoInfo = msg; 153 // this.videoInfo = msg;
153 console.log("videoInfo", msg); 154 console.log("videoInfo", msg);
154 155
155 }); 156 });
156 157
157 - this.jessibuca.on("bps", function (bps) { 158 + jessibuca.on("bps", function (bps) {
158 // console.log('bps', bps); 159 // console.log('bps', bps);
159 160
160 }); 161 });
161 let _ts = 0; 162 let _ts = 0;
162 - this.jessibuca.on("timeUpdate", function (ts) { 163 + jessibuca.on("timeUpdate", function (ts) {
163 // console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts); 164 // console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts);
164 _ts = ts; 165 _ts = ts;
165 }); 166 });
166 167
167 - this.jessibuca.on("videoInfo", function (info) { 168 + jessibuca.on("videoInfo", function (info) {
168 console.log("videoInfo", info); 169 console.log("videoInfo", info);
169 }); 170 });
170 171
171 - this.jessibuca.on("error", function (error) { 172 + jessibuca.on("error", function (error) {
172 console.log("error", error); 173 console.log("error", error);
173 }); 174 });
174 175
175 - this.jessibuca.on("timeout", function () { 176 + jessibuca.on("timeout", function () {
176 console.log("timeout"); 177 console.log("timeout");
177 }); 178 });
178 179
179 - this.jessibuca.on('start', function () { 180 + jessibuca.on('start', function () {
180 console.log('start'); 181 console.log('start');
181 }) 182 })
182 183
183 - this.jessibuca.on("performance", function (performance) { 184 + jessibuca.on("performance", function (performance) {
184 let show = "卡顿"; 185 let show = "卡顿";
185 if (performance === 2) { 186 if (performance === 2) {
186 show = "非常流畅"; 187 show = "非常流畅";
@@ -189,25 +190,25 @@ export default { @@ -189,25 +190,25 @@ export default {
189 } 190 }
190 _this.performance = show; 191 _this.performance = show;
191 }); 192 });
192 - this.jessibuca.on('buffer', function (buffer) { 193 + jessibuca.on('buffer', function (buffer) {
193 // console.log('buffer', buffer); 194 // console.log('buffer', buffer);
194 }) 195 })
195 196
196 - this.jessibuca.on('stats', function (stats) { 197 + jessibuca.on('stats', function (stats) {
197 // console.log('stats', stats); 198 // console.log('stats', stats);
198 }) 199 })
199 200
200 - this.jessibuca.on('kBps', function (kBps) { 201 + jessibuca.on('kBps', function (kBps) {
201 _this.kBps = Math.round(kBps); 202 _this.kBps = Math.round(kBps);
202 }); 203 });
203 204
204 // 显示时间戳 PTS 205 // 显示时间戳 PTS
205 - this.jessibuca.on('videoFrame', function () { 206 + jessibuca.on('videoFrame', function () {
206 207
207 }) 208 })
208 209
209 // 210 //
210 - this.jessibuca.on('metadata', function () { 211 + jessibuca.on('metadata', function () {
211 212
212 }); 213 });
213 }, 214 },
@@ -216,40 +217,40 @@ export default { @@ -216,40 +217,40 @@ export default {
216 }, 217 },
217 play: function (url) { 218 play: function (url) {
218 console.log(url) 219 console.log(url)
219 - if (this.jessibuca) { 220 + if (jessibucaPlayer[this._uid]) {
220 this.destroy(); 221 this.destroy();
221 } 222 }
222 this.create(); 223 this.create();
223 - this.jessibuca.on("play", () => { 224 + jessibucaPlayer[this._uid].on("play", () => {
224 this.playing = true; 225 this.playing = true;
225 this.loaded = true; 226 this.loaded = true;
226 - this.quieting = this.jessibuca.quieting; 227 + this.quieting = jessibuca.quieting;
227 }); 228 });
228 - if (this.jessibuca.hasLoaded()) {  
229 - this.jessibuca.play(url); 229 + if (jessibucaPlayer[this._uid].hasLoaded()) {
  230 + jessibucaPlayer[this._uid].play(url);
230 } else { 231 } else {
231 - this.jessibuca.on("load", () => { 232 + jessibucaPlayer[this._uid].on("load", () => {
232 console.log("load 播放") 233 console.log("load 播放")
233 - this.jessibuca.play(url); 234 + jessibucaPlayer[this._uid].play(url);
234 }); 235 });
235 } 236 }
236 }, 237 },
237 pause: function () { 238 pause: function () {
238 - if (this.jessibuca) {  
239 - this.jessibuca.pause(); 239 + if (jessibucaPlayer[this._uid]) {
  240 + jessibucaPlayer[this._uid].pause();
240 } 241 }
241 this.playing = false; 242 this.playing = false;
242 this.err = ""; 243 this.err = "";
243 this.performance = ""; 244 this.performance = "";
244 }, 245 },
245 destroy: function () { 246 destroy: function () {
246 - if (this.jessibuca) {  
247 - this.jessibuca.destroy(); 247 + if (jessibucaPlayer[this._uid]) {
  248 + jessibucaPlayer[this._uid].destroy();
248 } 249 }
249 if (document.getElementById("buttonsBox") == null) { 250 if (document.getElementById("buttonsBox") == null) {
250 this.$refs.container.appendChild(this.btnDom) 251 this.$refs.container.appendChild(this.btnDom)
251 } 252 }
252 - this.jessibuca = null; 253 + jessibucaPlayer[this._uid] = null;
253 this.playing = false; 254 this.playing = false;
254 this.err = ""; 255 this.err = "";
255 this.performance = ""; 256 this.performance = "";
@@ -262,7 +263,7 @@ export default { @@ -262,7 +263,7 @@ export default {
262 }, 263 },
263 fullscreenSwich: function () { 264 fullscreenSwich: function () {
264 let isFull = this.isFullscreen() 265 let isFull = this.isFullscreen()
265 - this.jessibuca.setFullscreen(!isFull) 266 + jessibucaPlayer[this._uid].setFullscreen(!isFull)
266 this.fullscreen = !isFull; 267 this.fullscreen = !isFull;
267 }, 268 },
268 isFullscreen: function () { 269 isFullscreen: function () {
@@ -273,8 +274,8 @@ export default { @@ -273,8 +274,8 @@ export default {
273 } 274 }
274 }, 275 },
275 destroyed() { 276 destroyed() {
276 - if (this.jessibuca) {  
277 - this.jessibuca.destroy(); 277 + if (jessibucaPlayer[this._uid]) {
  278 + jessibucaPlayer[this._uid].destroy();
278 } 279 }
279 this.playing = false; 280 this.playing = false;
280 this.loaded = false; 281 this.loaded = false;
web_src/src/components/control.vue
@@ -235,10 +235,8 @@ @@ -235,10 +235,8 @@
235 <el-table-column prop="local_ip" label="本地"></el-table-column> 235 <el-table-column prop="local_ip" label="本地"></el-table-column>
236 <el-table-column prop="typeid" label="类型"></el-table-column> 236 <el-table-column prop="typeid" label="类型"></el-table-column>
237 <el-table-column align="right"> 237 <el-table-column align="right">
238 - <template slot="header" slot-scope="scope">  
239 - <el-button icon="el-icon-refresh-right" circle @click="getAllSession()"></el-button>  
240 - </template>  
241 - <template slot-scope="scope"> 238 + <template v-slot:default="scope">
  239 + <el-button size="mini" icon="el-icon-refresh-right" circle @click="getAllSession()"></el-button>
242 <el-button @click.native.prevent="deleteRow(scope.$index, allSessionData)" type="text" size="small">移除 240 <el-button @click.native.prevent="deleteRow(scope.$index, allSessionData)" type="text" size="small">移除
243 </el-button> 241 </el-button>
244 </template> 242 </template>
web_src/src/components/dialog/deviceEdit.vue
@@ -39,6 +39,12 @@ @@ -39,6 +39,12 @@
39 <el-form-item label="语音发送通道" prop="name"> 39 <el-form-item label="语音发送通道" prop="name">
40 <el-input v-model="form.audioChannelForReceive" clearable></el-input> 40 <el-input v-model="form.audioChannelForReceive" clearable></el-input>
41 </el-form-item> 41 </el-form-item>
  42 + <el-form-item label="地理坐标系" prop="geoCoordSys" >
  43 + <el-select v-model="form.geoCoordSys" style="float: left; width: 100%" >
  44 + <el-option key="WGS84" label="WGS84" value="WGS84"></el-option>
  45 + <el-option key="GCJ02" label="GCJ02" value="GCJ02"></el-option>
  46 + </el-select>
  47 + </el-form-item>
42 <el-form-item label="目录订阅" title="0为取消订阅" prop="subscribeCycleForCatalog" > 48 <el-form-item label="目录订阅" title="0为取消订阅" prop="subscribeCycleForCatalog" >
43 <el-input v-model="form.subscribeCycleForCatalog" clearable ></el-input> 49 <el-input v-model="form.subscribeCycleForCatalog" clearable ></el-input>
44 </el-form-item> 50 </el-form-item>
web_src/src/components/dialog/devicePlayer.vue
@@ -3,9 +3,23 @@ @@ -3,9 +3,23 @@
3 3
4 <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" @close="close()"> 4 <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" @close="close()">
5 <!-- <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> --> 5 <!-- <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> -->
6 - <player ref="videoPlayer" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" :height="false" :hasAudio="hasAudio" fluent autoplay live ></player> 6 + <div style="width: 100%; height: 100%">
  7 + <el-tabs type="card" :stretch="true" v-model="activePlayer" @tab-click="changePlayer" v-if="Object.keys(this.player).length > 1">
  8 + <el-tab-pane label="Jessibuca" name="jessibuca">
  9 + <jessibucaPlayer v-if="activePlayer === 'jessibuca'" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></jessibucaPlayer>
  10 + </el-tab-pane>
  11 + <el-tab-pane label="WebRTC" name="webRTC">
  12 + <rtc-player v-if="activePlayer === 'webRTC'" ref="webRTC" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></rtc-player>
  13 + </el-tab-pane>
  14 + <el-tab-pane label="h265web">h265web敬请期待</el-tab-pane>
  15 + <el-tab-pane label="wsPlayer">wsPlayer 敬请期待</el-tab-pane>
  16 + </el-tabs>
  17 + <jessibucaPlayer v-if="Object.keys(this.player).length == 1 && this.player.jessibuca" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></jessibucaPlayer>
  18 + <rtc-player v-if="Object.keys(this.player).length == 1 && this.player.webRTC" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></rtc-player>
  19 +
  20 + </div>
7 <div id="shared" style="text-align: right; margin-top: 1rem;"> 21 <div id="shared" style="text-align: right; margin-top: 1rem;">
8 - <el-tabs v-model="tabActiveName" @tab-click="tabHandleClick"> 22 + <el-tabs v-model="tabActiveName" @tab-click="tabHandleClick" >
9 <el-tab-pane label="实时视频" name="media"> 23 <el-tab-pane label="实时视频" name="media">
10 <div style="margin-bottom: 0.5rem;"> 24 <div style="margin-bottom: 0.5rem;">
11 <!-- <el-button type="primary" size="small" @click="playRecord(true, '')">播放</el-button>--> 25 <!-- <el-button type="primary" size="small" @click="playRecord(true, '')">播放</el-button>-->
@@ -31,10 +45,100 @@ @@ -31,10 +45,100 @@
31 <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;"> 45 <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
32 <span style="width: 5rem; line-height: 2.5rem; text-align: right;">资源地址:</span> 46 <span style="width: 5rem; line-height: 2.5rem; text-align: right;">资源地址:</span>
33 <el-input v-model="getPlayerShared.sharedRtmp" :disabled="true" > 47 <el-input v-model="getPlayerShared.sharedRtmp" :disabled="true" >
34 - <template slot="append">  
35 - <i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedRtmp" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>  
36 - </template> 48 + <el-button slot="append" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedRtmp" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button>
  49 + <el-dropdown slot="prepend" v-if="streamInfo" trigger="click" @command="copyUrl">
  50 + <el-button >
  51 + 更多地址<i class="el-icon-arrow-down el-icon--right"></i>
  52 + </el-button>
  53 + <el-dropdown-menu slot="dropdown" >
  54 + <el-dropdown-item :command="streamInfo.flv">
  55 + <el-tag >FLV:</el-tag>
  56 + <span>{{ streamInfo.flv }}</span>
  57 + </el-dropdown-item>
  58 + <el-dropdown-item :command="streamInfo.https_flv">
  59 + <el-tag >FLV(https):</el-tag>
  60 + <span>{{ streamInfo.https_flv }}</span>
  61 + </el-dropdown-item>
  62 + <el-dropdown-item :command="streamInfo.ws_flv">
  63 + <el-tag >FLV(ws):</el-tag>
  64 + <span >{{ streamInfo.ws_flv }}</span>
  65 + </el-dropdown-item>
  66 + <el-dropdown-item :command="streamInfo.wss_flv">
  67 + <el-tag >FLV(wss):</el-tag>
  68 + <span>{{ streamInfo.wss_flv }}</span>
  69 + </el-dropdown-item>
  70 + <el-dropdown-item :command="streamInfo.fmp4">
  71 + <el-tag >FMP4:</el-tag>
  72 + <span>{{ streamInfo.fmp4 }}</span>
  73 + </el-dropdown-item>
  74 + <el-dropdown-item :command="streamInfo.https_fmp4">
  75 + <el-tag >FMP4(https):</el-tag>
  76 + <span>{{ streamInfo.https_fmp4 }}</span>
  77 + </el-dropdown-item>
  78 + <el-dropdown-item :command="streamInfo.ws_fmp4">
  79 + <el-tag >FMP4(ws):</el-tag>
  80 + <span>{{ streamInfo.ws_fmp4 }}</span>
  81 + </el-dropdown-item>
  82 + <el-dropdown-item :command="streamInfo.wss_fmp4">
  83 + <el-tag >FMP4(wss):</el-tag>
  84 + <span>{{ streamInfo.wss_fmp4 }}</span>
  85 + </el-dropdown-item>
  86 + <el-dropdown-item :command="streamInfo.hls">
  87 + <el-tag>HLS:</el-tag>
  88 + <span>{{ streamInfo.hls }}</span>
  89 + </el-dropdown-item>
  90 + <el-dropdown-item :command="streamInfo.https_hls">
  91 + <el-tag >HLS(https):</el-tag>
  92 + <span>{{ streamInfo.https_hls }}</span>
  93 + </el-dropdown-item>
  94 + <el-dropdown-item :command="streamInfo.ws_hls">
  95 + <el-tag >HLS(ws):</el-tag>
  96 + <span>{{ streamInfo.ws_hls }}</span>
  97 + </el-dropdown-item>
  98 + <el-dropdown-item :command="streamInfo.wss_hls">
  99 + <el-tag >HLS(wss):</el-tag>
  100 + <span>{{ streamInfo.wss_hls }}</span>
  101 + </el-dropdown-item>
  102 + <el-dropdown-item :command="streamInfo.ts">
  103 + <el-tag>TS:</el-tag>
  104 + <span>{{ streamInfo.ts }}</span>
  105 + </el-dropdown-item>
  106 + <el-dropdown-item :command="streamInfo.https_ts">
  107 + <el-tag>TS(https):</el-tag>
  108 + <span>{{ streamInfo.https_ts }}</span>
  109 + </el-dropdown-item>
  110 + <el-dropdown-item :command="streamInfo.ws_ts">
  111 + <el-tag>TS(ws):</el-tag>
  112 + <span>{{ streamInfo.ws_ts }}</span>
  113 + </el-dropdown-item>
  114 + <el-dropdown-item :command="streamInfo.wss_ts">
  115 + <el-tag>TS(wss):</el-tag>
  116 + <span>{{ streamInfo.wss_ts }}</span>
  117 + </el-dropdown-item>
  118 + <el-dropdown-item :command="streamInfo.rtc">
  119 + <el-tag >RTC:</el-tag>
  120 + <span>{{ streamInfo.rtc }}</span>
  121 + </el-dropdown-item>
  122 + <el-dropdown-item :command="streamInfo.rtmp">
  123 + <el-tag >RTMP:</el-tag>
  124 + <span>{{ streamInfo.rtmp }}</span>
  125 + </el-dropdown-item>
  126 + <el-dropdown-item :command="streamInfo.rtmps">
  127 + <el-tag >RTMPS:</el-tag>
  128 + <span>{{ streamInfo.rtmps }}</span>
  129 + </el-dropdown-item>
  130 + <el-dropdown-item :command="streamInfo.rtsp">
  131 + <el-tag >RTSP:</el-tag>
  132 + <span>{{ streamInfo.rtsp }}</span>
  133 + </el-dropdown-item>
  134 + <el-dropdown-item :command="streamInfo.rtsps">
  135 + <el-tag >RTSPS:</el-tag>
  136 + <span>{{ streamInfo.rtsps }}</span>
  137 + </el-dropdown-item>
  138 + </el-dropdown-menu>
  139 + </el-dropdown>
37 </el-input> 140 </el-input>
  141 +
38 </div> 142 </div>
39 </el-tab-pane> 143 </el-tab-pane>
40 <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}--> 144 <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}-->
@@ -115,27 +219,27 @@ @@ -115,27 +219,27 @@
115 219
116 <div class="control-panel"> 220 <div class="control-panel">
117 <el-button-group> 221 <el-button-group>
118 - <el-tag style="position :absolute; left: 0rem; top: 0rem; width: 5rem; text-align: center" size="medium" type="info">预置位编号</el-tag> 222 + <el-tag style="position :absolute; left: 0rem; top: 0rem; width: 5rem; text-align: center" size="medium">预置位编号</el-tag>
119 <el-input-number style="position: absolute; left: 5rem; top: 0rem; width: 6rem" size="mini" v-model="presetPos" controls-position="right" :precision="0" :step="1" :min="1" :max="255"></el-input-number> 223 <el-input-number style="position: absolute; left: 5rem; top: 0rem; width: 6rem" size="mini" v-model="presetPos" controls-position="right" :precision="0" :step="1" :min="1" :max="255"></el-input-number>
120 <el-button style="position: absolute; left: 11rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="presetPosition(129, presetPos)">设置</el-button> 224 <el-button style="position: absolute; left: 11rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="presetPosition(129, presetPos)">设置</el-button>
121 <el-button style="position: absolute; left: 27rem; top: 0rem; width: 5rem" size="mini" type="primary" icon="el-icon-place" @click="presetPosition(130, presetPos)">调用</el-button> 225 <el-button style="position: absolute; left: 27rem; top: 0rem; width: 5rem" size="mini" type="primary" icon="el-icon-place" @click="presetPosition(130, presetPos)">调用</el-button>
122 <el-button style="position: absolute; left: 16rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="presetPosition(131, presetPos)">删除</el-button> 226 <el-button style="position: absolute; left: 16rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="presetPosition(131, presetPos)">删除</el-button>
123 - <el-tag style="position :absolute; left: 0rem; top: 2.5rem; width: 5rem; text-align: center" size="medium" type="info">巡航速度</el-tag> 227 + <el-tag style="position :absolute; left: 0rem; top: 2.5rem; width: 5rem; text-align: center" size="medium">巡航速度</el-tag>
124 <el-input-number style="position: absolute; left: 5rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number> 228 <el-input-number style="position: absolute; left: 5rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number>
125 <el-button style="position: absolute; left: 11rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(134, cruisingGroup, cruisingSpeed)">设置</el-button> 229 <el-button style="position: absolute; left: 11rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(134, cruisingGroup, cruisingSpeed)">设置</el-button>
126 - <el-tag style="position :absolute; left: 16rem; top: 2.5rem; width: 5rem; text-align: center" size="medium" type="info">停留时间</el-tag> 230 + <el-tag style="position :absolute; left: 16rem; top: 2.5rem; width: 5rem; text-align: center" size="medium">停留时间</el-tag>
127 <el-input-number style="position: absolute; left: 21rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingTime" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number> 231 <el-input-number style="position: absolute; left: 21rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingTime" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number>
128 <el-button style="position: absolute; left: 27rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-timer" @click="setSpeedOrTime(135, cruisingGroup, cruisingTime)">设置</el-button> 232 <el-button style="position: absolute; left: 27rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-timer" @click="setSpeedOrTime(135, cruisingGroup, cruisingTime)">设置</el-button>
129 - <el-tag style="position :absolute; left: 0rem; top: 4.5rem; width: 5rem; text-align: center" size="medium" type="info">巡航组编号</el-tag> 233 + <el-tag style="position :absolute; left: 0rem; top: 4.5rem; width: 5rem; text-align: center" size="medium">巡航组编号</el-tag>
130 <el-input-number style="position: absolute; left: 5rem; top: 4.5rem; width: 6rem" size="mini" v-model="cruisingGroup" controls-position="right" :precision="0" :min="0" :max="255"></el-input-number> 234 <el-input-number style="position: absolute; left: 5rem; top: 4.5rem; width: 6rem" size="mini" v-model="cruisingGroup" controls-position="right" :precision="0" :min="0" :max="255"></el-input-number>
131 <el-button style="position: absolute; left: 11rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="setCommand(132, cruisingGroup, presetPos)">添加点</el-button> 235 <el-button style="position: absolute; left: 11rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="setCommand(132, cruisingGroup, presetPos)">添加点</el-button>
132 <el-button style="position: absolute; left: 16rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="setCommand(133, cruisingGroup, presetPos)">删除点</el-button> 236 <el-button style="position: absolute; left: 16rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="setCommand(133, cruisingGroup, presetPos)">删除点</el-button>
133 <el-button style="position: absolute; left: 21rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete" @click="setCommand(133, cruisingGroup, 0)">删除组</el-button> 237 <el-button style="position: absolute; left: 21rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete" @click="setCommand(133, cruisingGroup, 0)">删除组</el-button>
134 <el-button style="position: absolute; left: 27rem; top: 5rem; width: 5rem" size="mini" type="primary" icon="el-icon-video-camera-solid" @click="setCommand(136, cruisingGroup, 0)">巡航</el-button> 238 <el-button style="position: absolute; left: 27rem; top: 5rem; width: 5rem" size="mini" type="primary" icon="el-icon-video-camera-solid" @click="setCommand(136, cruisingGroup, 0)">巡航</el-button>
135 - <el-tag style="position :absolute; left: 0rem; top: 7rem; width: 5rem; text-align: center" size="medium" type="info">扫描速度</el-tag> 239 + <el-tag style="position :absolute; left: 0rem; top: 7rem; width: 5rem; text-align: center" size="medium">扫描速度</el-tag>
136 <el-input-number style="position: absolute; left: 5rem; top: 7rem; width: 6rem" size="mini" v-model="scanSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number> 240 <el-input-number style="position: absolute; left: 5rem; top: 7rem; width: 6rem" size="mini" v-model="scanSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number>
137 <el-button style="position: absolute; left: 11rem; top: 7rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(138, scanGroup, scanSpeed)">设置</el-button> 241 <el-button style="position: absolute; left: 11rem; top: 7rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(138, scanGroup, scanSpeed)">设置</el-button>
138 - <el-tag style="position :absolute; left: 0rem; top: 9rem; width: 5rem; text-align: center" size="medium" type="info">扫描组编号</el-tag> 242 + <el-tag style="position :absolute; left: 0rem; top: 9rem; width: 5rem; text-align: center" size="medium">扫描组编号</el-tag>
139 <el-input-number style="position: absolute; left: 5rem; top: 9rem; width: 6rem" size="mini" v-model="scanGroup" controls-position="right" :precision="0" :step="1" :min="0" :max="255"></el-input-number> 243 <el-input-number style="position: absolute; left: 5rem; top: 9rem; width: 6rem" size="mini" v-model="scanGroup" controls-position="right" :precision="0" :step="1" :min="0" :max="255"></el-input-number>
140 <el-button style="position: absolute; left: 11rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-left" @click="setCommand(137, scanGroup, 1)">左边界</el-button> 244 <el-button style="position: absolute; left: 11rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-left" @click="setCommand(137, scanGroup, 1)">左边界</el-button>
141 <el-button style="position: absolute; left: 16rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-right" @click="setCommand(137, scanGroup, 2)">右边界</el-button> 245 <el-button style="position: absolute; left: 16rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-right" @click="setCommand(137, scanGroup, 2)">右边界</el-button>
@@ -172,26 +276,28 @@ @@ -172,26 +276,28 @@
172 </div> 276 </div>
173 277
174 </el-tab-pane> 278 </el-tab-pane>
  279 +
175 </el-tabs> 280 </el-tabs>
176 </div> 281 </div>
177 </el-dialog> 282 </el-dialog>
  283 + <recordDownload ref="recordDownload"></recordDownload>
178 </div> 284 </div>
179 </template> 285 </template>
180 286
181 <script> 287 <script>
182 -import player from '../dialog/rtcPlayer.vue' 288 +import rtcPlayer from '../dialog/rtcPlayer.vue'
183 // import LivePlayer from '@liveqing/liveplayer' 289 // import LivePlayer from '@liveqing/liveplayer'
184 // import player from '../dialog/easyPlayer.vue' 290 // import player from '../dialog/easyPlayer.vue'
185 -// import player from '../dialog/jessibuca.vue' 291 +import jessibucaPlayer from '../common/jessibuca.vue'
  292 +import recordDownload from '../dialog/recordDownload.vue'
186 export default { 293 export default {
187 name: 'devicePlayer', 294 name: 'devicePlayer',
188 props: {}, 295 props: {},
189 components: { 296 components: {
190 - player, 297 + jessibucaPlayer, rtcPlayer, recordDownload,
191 }, 298 },
192 computed: { 299 computed: {
193 getPlayerShared: function () { 300 getPlayerShared: function () {
194 -  
195 return { 301 return {
196 sharedUrl: window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl), 302 sharedUrl: window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl),
197 sharedIframe: '<iframe src="' + window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl) + '"></iframe>', 303 sharedIframe: '<iframe src="' + window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl) + '"></iframe>',
@@ -199,11 +305,22 @@ export default { @@ -199,11 +305,22 @@ export default {
199 }; 305 };
200 } 306 }
201 }, 307 },
202 - created() {}, 308 + created() {
  309 + console.log(this.player)
  310 + if (Object.keys(this.player).length === 1) {
  311 + this.activePlayer = Object.keys(this.player)[0]
  312 + }
  313 + },
203 data() { 314 data() {
204 return { 315 return {
205 video: 'http://lndxyj.iqilu.com/public/upload/2019/10/14/8c001ea0c09cdc59a57829dabc8010fa.mp4', 316 video: 'http://lndxyj.iqilu.com/public/upload/2019/10/14/8c001ea0c09cdc59a57829dabc8010fa.mp4',
206 videoUrl: '', 317 videoUrl: '',
  318 + activePlayer: "jessibuca",
  319 + // 如何你只是用一种播放器,直接注释掉不用的部分即可
  320 + player: {
  321 + jessibuca : ["ws_flv", "wss_flv"],
  322 + webRTC: ["rtc", "rtc"],
  323 + },
207 videoHistory: { 324 videoHistory: {
208 date: '', 325 date: '',
209 searchHistoryResult: [] //媒体流历史记录搜索结果 326 searchHistoryResult: [] //媒体流历史记录搜索结果
@@ -241,6 +358,7 @@ export default { @@ -241,6 +358,7 @@ export default {
241 seekTime: 0, 358 seekTime: 0,
242 recordStartTime: 0, 359 recordStartTime: 0,
243 showTimeText: "00:00:00", 360 showTimeText: "00:00:00",
  361 + streamInfo: null,
244 }; 362 };
245 }, 363 },
246 methods: { 364 methods: {
@@ -250,7 +368,7 @@ export default { @@ -250,7 +368,7 @@ export default {
250 that.tracks = []; 368 that.tracks = [];
251 that.tracksLoading = true; 369 that.tracksLoading = true;
252 that.tracksNotLoaded = false; 370 that.tracksNotLoaded = false;
253 - if (tab.name == "codec") { 371 + if (tab.name === "codec") {
254 this.$axios({ 372 this.$axios({
255 method: 'get', 373 method: 'get',
256 url: '/zlm/' +this.mediaServerId+ '/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app='+ this.app +'&stream='+ this.streamId 374 url: '/zlm/' +this.mediaServerId+ '/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app='+ this.app +'&stream='+ this.streamId
@@ -269,6 +387,12 @@ export default { @@ -269,6 +387,12 @@ export default {
269 }).catch(function (e) {}); 387 }).catch(function (e) {});
270 } 388 }
271 }, 389 },
  390 + changePlayer: function (tab) {
  391 + console.log(this.player[tab.name][0])
  392 + this.activePlayer = tab.name;
  393 + this.videoUrl = this.streamInfo[this.player[tab.name][0]]
  394 + console.log(this.videoUrl)
  395 + },
272 openDialog: function (tab, deviceId, channelId, param) { 396 openDialog: function (tab, deviceId, channelId, param) {
273 this.tabActiveName = tab; 397 this.tabActiveName = tab;
274 this.channelId = channelId; 398 this.channelId = channelId;
@@ -277,8 +401,8 @@ export default { @@ -277,8 +401,8 @@ export default {
277 this.mediaServerId = ""; 401 this.mediaServerId = "";
278 this.app = ""; 402 this.app = "";
279 this.videoUrl = "" 403 this.videoUrl = ""
280 - if (!!this.$refs.videoPlayer) {  
281 - this.$refs.videoPlayer.pause(); 404 + if (!!this.$refs[this.activePlayer]) {
  405 + this.$refs[this.activePlayer].pause();
282 } 406 }
283 switch (tab) { 407 switch (tab) {
284 case "media": 408 case "media":
@@ -303,44 +427,32 @@ export default { @@ -303,44 +427,32 @@ export default {
303 console.log(val) 427 console.log(val)
304 }, 428 },
305 play: function (streamInfo, hasAudio) { 429 play: function (streamInfo, hasAudio) {
  430 + this.streamInfo = streamInfo;
306 this.hasAudio = hasAudio; 431 this.hasAudio = hasAudio;
307 this.isLoging = false; 432 this.isLoging = false;
308 - this.videoUrl = streamInfo.rtc;  
309 - // this.videoUrl = this.getUrlByStreamInfo(streamInfo);  
310 - this.streamId = streamInfo.streamId; 433 + // this.videoUrl = streamInfo.rtc;
  434 + this.videoUrl = this.getUrlByStreamInfo();
  435 + this.streamId = streamInfo.stream;
311 this.app = streamInfo.app; 436 this.app = streamInfo.app;
312 this.mediaServerId = streamInfo.mediaServerId; 437 this.mediaServerId = streamInfo.mediaServerId;
313 this.playFromStreamInfo(false, streamInfo) 438 this.playFromStreamInfo(false, streamInfo)
314 }, 439 },
315 - getUrlByStreamInfo(streamInfo){  
316 - let baseZlmApi = process.env.NODE_ENV === 'development'?`${location.host}/debug/zlm`:`${location.host}/zlm`  
317 - // return `${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`;  
318 - // return `http://${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`; 440 + getUrlByStreamInfo(){
319 if (location.protocol === "https:") { 441 if (location.protocol === "https:") {
320 - if (streamInfo.wss_flv === null) {  
321 - console.error("媒体服务器未配置ssl端口, 使用http端口")  
322 - // this.$message({  
323 - // showClose: true,  
324 - // message: '媒体服务器未配置ssl端口, ',  
325 - // type: 'error'  
326 - // });  
327 - return streamInfo.ws_flv  
328 - }else {  
329 - return streamInfo.wss_flv;  
330 - }  
331 - 442 + this.videoUrl = this.streamInfo[this.player[this.activePlayer][1]]
332 }else { 443 }else {
333 - return streamInfo.ws_flv; 444 + this.videoUrl = this.streamInfo[this.player[this.activePlayer][0]]
334 } 445 }
  446 + return this.videoUrl;
335 447
336 }, 448 },
337 coverPlay: function () { 449 coverPlay: function () {
338 var that = this; 450 var that = this;
339 this.coverPlaying = true; 451 this.coverPlaying = true;
340 - this.$refs.videoPlayer.pause() 452 + this.$refs[this.activePlayer].pause()
341 that.$axios({ 453 that.$axios({
342 method: 'post', 454 method: 'post',
343 - url: '/api/play/convert/' + that.streamId 455 + url: '/api/gb_record/convert/' + that.streamId
344 }).then(function (res) { 456 }).then(function (res) {
345 if (res.data.code == 0) { 457 if (res.data.code == 0) {
346 that.convertKey = res.data.key; 458 that.convertKey = res.data.key;
@@ -369,7 +481,7 @@ export default { @@ -369,7 +481,7 @@ export default {
369 }, 481 },
370 convertStopClick: function() { 482 convertStopClick: function() {
371 this.convertStop(()=>{ 483 this.convertStop(()=>{
372 - this.$refs.videoPlayer.play(this.videoUrl) 484 + this.$refs[this.activePlayer].play(this.videoUrl)
373 }); 485 });
374 }, 486 },
375 convertStop: function(callback) { 487 convertStop: function(callback) {
@@ -394,12 +506,12 @@ export default { @@ -394,12 +506,12 @@ export default {
394 playFromStreamInfo: function (realHasAudio, streamInfo) { 506 playFromStreamInfo: function (realHasAudio, streamInfo) {
395 this.showVideoDialog = true; 507 this.showVideoDialog = true;
396 this.hasaudio = realHasAudio && this.hasaudio; 508 this.hasaudio = realHasAudio && this.hasaudio;
397 - this.$refs.videoPlayer.play(this.getUrlByStreamInfo(streamInfo)) 509 + this.$refs[this.activePlayer].play(this.getUrlByStreamInfo(streamInfo))
398 }, 510 },
399 close: function () { 511 close: function () {
400 console.log('关闭视频'); 512 console.log('关闭视频');
401 - if (!!this.$refs.videoPlayer){  
402 - this.$refs.videoPlayer.pause(); 513 + if (!!this.$refs[this.activePlayer]){
  514 + this.$refs[this.activePlayer].pause();
403 } 515 }
404 this.videoUrl = ''; 516 this.videoUrl = '';
405 this.coverPlaying = false; 517 this.coverPlaying = false;
@@ -450,9 +562,19 @@ export default { @@ -450,9 +562,19 @@ export default {
450 method: 'get', 562 method: 'get',
451 url: '/api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + startTime + '&endTime=' + endTime 563 url: '/api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + startTime + '&endTime=' + endTime
452 }).then(function (res) { 564 }).then(function (res) {
453 - // 处理时间信息  
454 - that.videoHistory.searchHistoryResult = res.data.recordList;  
455 - that.recordsLoading = false; 565 + console.log(res)
  566 + if(res.data.code === 0) {
  567 + // 处理时间信息
  568 + that.videoHistory.searchHistoryResult = res.data.data.recordList;
  569 + that.recordsLoading = false;
  570 + }else {
  571 + this.$message({
  572 + showClose: true,
  573 + message: res.data.msg,
  574 + type: "error",
  575 + });
  576 + }
  577 +
456 }).catch(function (e) { 578 }).catch(function (e) {
457 console.log(e.message); 579 console.log(e.message);
458 // that.videoHistory.searchHistoryResult = falsificationData.recordData; 580 // that.videoHistory.searchHistoryResult = falsificationData.recordData;
@@ -474,8 +596,8 @@ export default { @@ -474,8 +596,8 @@ export default {
474 console.log(this.seekTime) 596 console.log(this.seekTime)
475 if (that.streamId != "") { 597 if (that.streamId != "") {
476 that.stopPlayRecord(function () { 598 that.stopPlayRecord(function () {
477 - that.streamId = "",  
478 - that.playRecord(row); 599 + that.streamId = "";
  600 + that.playRecord(row);
479 }) 601 })
480 } else { 602 } else {
481 this.$axios({ 603 this.$axios({
@@ -483,21 +605,22 @@ export default { @@ -483,21 +605,22 @@ export default {
483 url: '/api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' + 605 url: '/api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
484 row.endTime 606 row.endTime
485 }).then(function (res) { 607 }).then(function (res) {
486 - var streamInfo = res.data;  
487 - that.app = streamInfo.app;  
488 - that.streamId = streamInfo.streamId;  
489 - that.mediaServerId = streamInfo.mediaServerId;  
490 - that.videoUrl = that.getUrlByStreamInfo(streamInfo); 608 + that.streamInfo = res.data;
  609 + that.app = that.streamInfo.app;
  610 + that.streamId = that.streamInfo.stream;
  611 + that.mediaServerId = that.streamInfo.mediaServerId;
  612 + that.ssrc = that.streamInfo.ssrc;
  613 + that.videoUrl = that.getUrlByStreamInfo();
491 that.recordPlay = true; 614 that.recordPlay = true;
492 }); 615 });
493 } 616 }
494 }, 617 },
495 stopPlayRecord: function (callback) { 618 stopPlayRecord: function (callback) {
496 - this.$refs.videoPlayer.pause(); 619 + this.$refs[this.activePlayer].pause();
497 this.videoUrl = ''; 620 this.videoUrl = '';
498 this.$axios({ 621 this.$axios({
499 method: 'get', 622 method: 'get',
500 - url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId 623 + url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId
501 }).then(function (res) { 624 }).then(function (res) {
502 if (callback) callback() 625 if (callback) callback()
503 }); 626 });
@@ -505,33 +628,47 @@ export default { @@ -505,33 +628,47 @@ export default {
505 downloadRecord: function (row) { 628 downloadRecord: function (row) {
506 let that = this; 629 let that = this;
507 if (that.streamId != "") { 630 if (that.streamId != "") {
508 - that.stopDownloadRecord(function () {  
509 - that.streamId = "",  
510 - that.downloadRecord(row); 631 + that.stopDownloadRecord(function (res) {
  632 + if (res.code == 0) {
  633 + that.streamId = "";
  634 + that.downloadRecord(row);
  635 + }else {
  636 + this.$message({
  637 + showClose: true,
  638 + message: res.data.msg,
  639 + type: "error",
  640 + });
  641 + }
  642 +
511 }) 643 })
512 } else { 644 } else {
513 this.$axios({ 645 this.$axios({
514 method: 'get', 646 method: 'get',
515 - url: '/api/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' + 647 + url: '/api/gb_record/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
516 row.endTime + '&downloadSpeed=4' 648 row.endTime + '&downloadSpeed=4'
517 }).then(function (res) { 649 }).then(function (res) {
518 - var streamInfo = res.data;  
519 - that.app = streamInfo.app;  
520 - that.streamId = streamInfo.streamId;  
521 - that.mediaServerId = streamInfo.mediaServerId;  
522 - that.videoUrl = that.getUrlByStreamInfo(streamInfo);  
523 - that.recordPlay = true; 650 + if (res.data.code == 0) {
  651 + let streamInfo = res.data.data;
  652 + that.recordPlay = false;
  653 + that.$refs.recordDownload.openDialog(that.deviceId, that.channelId, streamInfo.app, streamInfo.stream, streamInfo.mediaServerId);
  654 + }else {
  655 + that.$message({
  656 + showClose: true,
  657 + message: res.data.msg,
  658 + type: "error",
  659 + });
  660 + }
524 }); 661 });
525 } 662 }
526 }, 663 },
527 stopDownloadRecord: function (callback) { 664 stopDownloadRecord: function (callback) {
528 - this.$refs.videoPlayer.pause(); 665 + this.$refs[this.activePlayer].pause();
529 this.videoUrl = ''; 666 this.videoUrl = '';
530 this.$axios({ 667 this.$axios({
531 method: 'get', 668 method: 'get',
532 - url: '/api/download/stop/' + this.deviceId + "/" + this.channelId  
533 - }).then(function (res) {  
534 - if (callback) callback() 669 + url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.streamId
  670 + }).then((res)=> {
  671 + if (callback) callback(res)
535 }); 672 });
536 }, 673 },
537 ptzCamera: function (command) { 674 ptzCamera: function (command) {
@@ -539,8 +676,6 @@ export default { @@ -539,8 +676,6 @@ export default {
539 let that = this; 676 let that = this;
540 this.$axios({ 677 this.$axios({
541 method: 'post', 678 method: 'post',
542 - // url: '/api/ptz/' + this.deviceId + '/' + this.channelId + '?leftRight=' + leftRight + '&upDown=' + upDown +  
543 - // '&inOut=' + zoom + '&moveSpeed=50&zoomSpeed=50'  
544 url: '/api/ptz/control/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&horizonSpeed=' + this.controSpeed + '&verticalSpeed=' + this.controSpeed + '&zoomSpeed=' + this.controSpeed 679 url: '/api/ptz/control/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&horizonSpeed=' + this.controSpeed + '&verticalSpeed=' + this.controSpeed + '&zoomSpeed=' + this.controSpeed
545 }).then(function (res) {}); 680 }).then(function (res) {});
546 }, 681 },
@@ -620,13 +755,21 @@ export default { @@ -620,13 +755,21 @@ export default {
620 console.log(resultArray) 755 console.log(resultArray)
621 return resultArray; 756 return resultArray;
622 }, 757 },
  758 + copyUrl: function (dropdownItem){
  759 + console.log(dropdownItem)
  760 + this.$copyText(dropdownItem).then((e)=> {
  761 + this.$message.success("成功拷贝到粘贴板");
  762 + }, (e)=> {
  763 +
  764 + })
  765 + },
623 gbPlay(){ 766 gbPlay(){
624 console.log('前端控制:播放'); 767 console.log('前端控制:播放');
625 this.$axios({ 768 this.$axios({
626 method: 'get', 769 method: 'get',
627 url: '/api/playback/resume/' + this.streamId 770 url: '/api/playback/resume/' + this.streamId
628 }).then((res)=> { 771 }).then((res)=> {
629 - this.$refs.videoPlayer.play(this.videoUrl) 772 + this.$refs[this.activePlayer].play(this.videoUrl)
630 }); 773 });
631 }, 774 },
632 gbPause(){ 775 gbPause(){
@@ -655,8 +798,13 @@ export default { @@ -655,8 +798,13 @@ export default {
655 this.$axios({ 798 this.$axios({
656 method: 'get', 799 method: 'get',
657 url: `/api/playback/seek/${this.streamId }/` + Math.floor(this.seekTime * val / 100000) 800 url: `/api/playback/seek/${this.streamId }/` + Math.floor(this.seekTime * val / 100000)
658 - }).then(function (res) {});  
659 - } 801 + }).then( (res)=> {
  802 + setTimeout(()=>{
  803 + this.$refs[this.activePlayer].play(this.videoUrl)
  804 + }, 600)
  805 + });
  806 + },
  807 +
660 808
661 } 809 }
662 }; 810 };
web_src/src/components/dialog/rtcPlayer.vue
@@ -7,11 +7,11 @@ @@ -7,11 +7,11 @@
7 </template> 7 </template>
8 8
9 <script> 9 <script>
  10 +let webrtcPlayer = null;
10 export default { 11 export default {
11 name: 'rtcPlayer', 12 name: 'rtcPlayer',
12 data() { 13 data() {
13 return { 14 return {
14 - webrtcPlayer: null,  
15 timer: null 15 timer: null
16 }; 16 };
17 }, 17 },
@@ -35,7 +35,7 @@ export default { @@ -35,7 +35,7 @@ export default {
35 }, 35 },
36 methods: { 36 methods: {
37 play: function (url) { 37 play: function (url) {
38 - this.webrtcPlayer = new ZLMRTCClient.Endpoint({ 38 + webrtcPlayer = new ZLMRTCClient.Endpoint({
39 element: document.getElementById('webRtcPlayerBox'),// video 标签 39 element: document.getElementById('webRtcPlayerBox'),// video 标签
40 debug: true,// 是否打印日志 40 debug: true,// 是否打印日志
41 zlmsdpUrl: url,//流地址 41 zlmsdpUrl: url,//流地址
@@ -45,17 +45,17 @@ export default { @@ -45,17 +45,17 @@ export default {
45 videoEnable: false, 45 videoEnable: false,
46 recvOnly: true, 46 recvOnly: true,
47 }) 47 })
48 - this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,(e)=>{// ICE 协商出错 48 + webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,(e)=>{// ICE 协商出错
49 console.error('ICE 协商出错') 49 console.error('ICE 协商出错')
50 this.eventcallbacK("ICE ERROR", "ICE 协商出错") 50 this.eventcallbacK("ICE ERROR", "ICE 协商出错")
51 }); 51 });
52 52
53 - this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(e)=>{//获取到了远端流,可以播放 53 + webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(e)=>{//获取到了远端流,可以播放
54 console.error('播放成功',e.streams) 54 console.error('播放成功',e.streams)
55 this.eventcallbacK("playing", "播放成功") 55 this.eventcallbacK("playing", "播放成功")
56 }); 56 });
57 57
58 - this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e)=>{// offer anwser 交换失败 58 + webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e)=>{// offer anwser 交换失败
59 console.error('offer anwser 交换失败',e) 59 console.error('offer anwser 交换失败',e)
60 this.eventcallbacK("OFFER ANSWER ERROR ", "offer anwser 交换失败") 60 this.eventcallbacK("OFFER ANSWER ERROR ", "offer anwser 交换失败")
61 if (e.code ==-400 && e.msg=="流不存在"){ 61 if (e.code ==-400 && e.msg=="流不存在"){
@@ -68,7 +68,7 @@ export default { @@ -68,7 +68,7 @@ export default {
68 } 68 }
69 }); 69 });
70 70
71 - this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,(s)=>{// 获取到了本地流 71 + webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,(s)=>{// 获取到了本地流
72 72
73 // document.getElementById('selfVideo').srcObject=s; 73 // document.getElementById('selfVideo').srcObject=s;
74 this.eventcallbacK("LOCAL STREAM", "获取到了本地流") 74 this.eventcallbacK("LOCAL STREAM", "获取到了本地流")
@@ -76,9 +76,9 @@ export default { @@ -76,9 +76,9 @@ export default {
76 76
77 }, 77 },
78 pause: function () { 78 pause: function () {
79 - if (this.webrtcPlayer != null) {  
80 - this.webrtcPlayer.close();  
81 - this.webrtcPlayer = null; 79 + if (webrtcPlayer != null) {
  80 + webrtcPlayer.close();
  81 + webrtcPlayer = null;
82 } 82 }
83 83
84 }, 84 },
web_src/src/components/devicePosition.vue renamed to web_src/src/components/map.vue
@@ -49,7 +49,7 @@ import devicePlayer from &#39;./dialog/devicePlayer.vue&#39; @@ -49,7 +49,7 @@ import devicePlayer from &#39;./dialog/devicePlayer.vue&#39;
49 import queryTrace from './dialog/queryTrace.vue' 49 import queryTrace from './dialog/queryTrace.vue'
50 50
51 export default { 51 export default {
52 - name: "devicePosition", 52 + name: "map",
53 components: { 53 components: {
54 MapComponent, 54 MapComponent,
55 DeviceTree, 55 DeviceTree,
@@ -183,12 +183,27 @@ export default { @@ -183,12 +183,27 @@ export default {
183 this.clean() 183 this.clean()
184 this.closeInfoBox() 184 this.closeInfoBox()
185 let params = []; 185 let params = [];
  186 + let longitudeStr;
  187 + let latitudeStr;
  188 + if (window.mapParam.coordinateSystem == "GCJ-02") {
  189 + longitudeStr = "longitudeGcj02";
  190 + latitudeStr = "latitudeGcj02";
  191 + }else if (window.mapParam.coordinateSystem == "WGS84") {
  192 + longitudeStr = "longitudeWgs84";
  193 + latitudeStr = "latitudeWgs84";
  194 + }else {
  195 + longitudeStr = "longitude";
  196 + latitudeStr = "latitude";
  197 + }
  198 +
186 for (let i = 0; i < channels.length; i++) { 199 for (let i = 0; i < channels.length; i++) {
187 - if (channels[i].longitude * channels[i].latitude === 0) { 200 + let longitude = channels[i][longitudeStr];
  201 + let latitude = channels[i][latitudeStr];
  202 + if (longitude * latitude === 0) {
188 continue; 203 continue;
189 } 204 }
190 let item = { 205 let item = {
191 - position: [channels[i].longitude, channels[i].latitude], 206 + position: [longitude, latitude],
192 image: { 207 image: {
193 src: this.getImageByChannel(channels[i]), 208 src: this.getImageByChannel(channels[i]),
194 anchor: [0.5, 1] 209 anchor: [0.5, 1]
@@ -202,7 +217,7 @@ export default { @@ -202,7 +217,7 @@ export default {
202 this.layer = this.$refs.map.addLayer(params, this.featureClickEvent) 217 this.layer = this.$refs.map.addLayer(params, this.featureClickEvent)
203 console.log(4) 218 console.log(4)
204 if (params.length === 1) { 219 if (params.length === 1) {
205 - this.$refs.map.panTo([channels[0].longitude, channels[0].latitude], mapParam.maxZoom) 220 + this.$refs.map.panTo([channels[0][longitudeStr], channels[0][latitudeStr]], mapParam.maxZoom)
206 } else if (params.length > 1) { 221 } else if (params.length > 1) {
207 this.$refs.map.fit(this.layer) 222 this.$refs.map.fit(this.layer)
208 } else { 223 } else {
@@ -251,7 +266,20 @@ export default { @@ -251,7 +266,20 @@ export default {
251 this.channel = channels[0] 266 this.channel = channels[0]
252 } 267 }
253 this.$nextTick(() => { 268 this.$nextTick(() => {
254 - this.infoBoxId = this.$refs.map.openInfoBox([this.channel.longitude, this.channel.latitude], this.$refs.infobox, [0, -50]) 269 + let longitudeStr;
  270 + let latitudeStr;
  271 + if (window.mapParam.coordinateSystem == "GCJ-02") {
  272 + longitudeStr = "longitudeGcj02";
  273 + latitudeStr = "latitudeGcj02";
  274 + }else if (window.mapParam.coordinateSystem == "WGS84") {
  275 + longitudeStr = "longitudeWgs84";
  276 + latitudeStr = "latitudeWgs84";
  277 + }else {
  278 + longitudeStr = "longitude";
  279 + latitudeStr = "latitude";
  280 + }
  281 + let position = [this.channel[longitudeStr], this.channel[latitudeStr]];
  282 + this.infoBoxId = this.$refs.map.openInfoBox(position, this.$refs.infobox, [0, -50])
255 }) 283 })
256 }, 284 },
257 closeInfoBox: function () { 285 closeInfoBox: function () {
web_src/src/components/service/DeviceService.js
@@ -21,47 +21,60 @@ class DeviceService{ @@ -21,47 +21,60 @@ class DeviceService{
21 if (typeof (errorCallback) == "function") errorCallback(error) 21 if (typeof (errorCallback) == "function") errorCallback(error)
22 }); 22 });
23 } 23 }
24 - getAllDeviceList(callback, errorCallback) { 24 +
  25 + getDevice(deviceId, callback, errorCallback){
  26 + this.$axios({
  27 + method: 'get',
  28 + url:`/api/device/query/devices/${deviceId}`,
  29 + }).then((res) => {
  30 + if (typeof (callback) == "function") callback(res.data)
  31 + }).catch((error) => {
  32 + console.log(error);
  33 + if (typeof (errorCallback) == "function") errorCallback(error)
  34 + });
  35 + }
  36 +
  37 + getAllDeviceList(callback,endCallback, errorCallback) {
25 let currentPage = 1; 38 let currentPage = 1;
26 let count = 100; 39 let count = 100;
27 let deviceList = [] 40 let deviceList = []
28 - this.getAllDeviceListIteration(deviceList, currentPage, count, (data) => {  
29 - if (typeof (callback) == "function") callback(data)  
30 - }, errorCallback) 41 + this.getAllDeviceListIteration(deviceList, currentPage, count, callback, endCallback, errorCallback)
31 } 42 }
32 43
33 - getAllDeviceListIteration(deviceList, currentPage, count, callback, errorCallback) { 44 + getAllDeviceListIteration(deviceList, currentPage, count, callback, endCallback, errorCallback) {
34 this.getDeviceList(currentPage, count, (data) => { 45 this.getDeviceList(currentPage, count, (data) => {
35 if (data.list) { 46 if (data.list) {
  47 + if (typeof (callback) == "function") callback(data.list)
36 deviceList = deviceList.concat(data.list); 48 deviceList = deviceList.concat(data.list);
37 if (deviceList.length < data.total) { 49 if (deviceList.length < data.total) {
38 currentPage ++ 50 currentPage ++
39 - this.getAllDeviceListIteration(deviceList, currentPage, count, callback, errorCallback) 51 + this.getAllDeviceListIteration(deviceList, currentPage, count, callback, endCallback, errorCallback)
40 }else { 52 }else {
41 - if (typeof (callback) == "function") callback(deviceList) 53 + if (typeof (endCallback) == "function") endCallback(deviceList)
42 } 54 }
43 } 55 }
44 }, errorCallback) 56 }, errorCallback)
45 } 57 }
46 58
47 59
48 - getAllChannel(isCatalog, catalogUnderDevice, deviceId, callback, errorCallback) { 60 + getAllChannel(isCatalog, catalogUnderDevice, deviceId, callback, endCallback, errorCallback) {
49 let currentPage = 1; 61 let currentPage = 1;
50 let count = 100; 62 let count = 100;
51 let catalogList = [] 63 let catalogList = []
52 - this.getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, errorCallback) 64 + this.getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, endCallback, errorCallback)
53 } 65 }
54 66
55 - getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, errorCallback) { 67 + getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, endCallback, errorCallback) {
56 this.getChanel(isCatalog, catalogUnderDevice, deviceId, currentPage, count, (data) => { 68 this.getChanel(isCatalog, catalogUnderDevice, deviceId, currentPage, count, (data) => {
57 if (data.list) { 69 if (data.list) {
  70 + if (typeof (callback) == "function") callback(data.list)
58 catalogList = catalogList.concat(data.list); 71 catalogList = catalogList.concat(data.list);
59 if (catalogList.length < data.total) { 72 if (catalogList.length < data.total) {
60 currentPage ++ 73 currentPage ++
61 this.getAllChannelIteration(isCatalog,catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, errorCallback) 74 this.getAllChannelIteration(isCatalog,catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, errorCallback)
62 }else { 75 }else {
63 console.log(1) 76 console.log(1)
64 - if (typeof (callback) == "function") callback(catalogList) 77 + if (typeof (endCallback) == "function") endCallback(catalogList)
65 } 78 }
66 } 79 }
67 }, errorCallback) 80 }, errorCallback)
@@ -84,22 +97,23 @@ class DeviceService{ @@ -84,22 +97,23 @@ class DeviceService{
84 } 97 }
85 98
86 99
87 - getAllSubChannel(isCatalog, deviceId, channelId, callback, errorCallback) { 100 + getAllSubChannel(isCatalog, deviceId, channelId, callback, endCallback, errorCallback) {
88 let currentPage = 1; 101 let currentPage = 1;
89 let count = 100; 102 let count = 100;
90 let catalogList = [] 103 let catalogList = []
91 - this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, errorCallback) 104 + this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, endCallback, errorCallback)
92 } 105 }
93 106
94 - getAllSubChannelIteration(isCatalog, deviceId,channelId, catalogList, currentPage, count, callback, errorCallback) { 107 + getAllSubChannelIteration(isCatalog, deviceId,channelId, catalogList, currentPage, count, callback, endCallback, errorCallback) {
95 this.getSubChannel(isCatalog, deviceId, channelId, currentPage, count, (data) => { 108 this.getSubChannel(isCatalog, deviceId, channelId, currentPage, count, (data) => {
96 if (data.list) { 109 if (data.list) {
  110 + if (typeof (callback) == "function") callback(data.list)
97 catalogList = catalogList.concat(data.list); 111 catalogList = catalogList.concat(data.list);
98 if (catalogList.length < data.total) { 112 if (catalogList.length < data.total) {
99 currentPage ++ 113 currentPage ++
100 - this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, errorCallback) 114 + this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, endCallback, errorCallback)
101 }else { 115 }else {
102 - if (typeof (callback) == "function") callback(catalogList) 116 + if (typeof (endCallback) == "function") endCallback(catalogList)
103 } 117 }
104 } 118 }
105 }, errorCallback) 119 }, errorCallback)
web_src/src/layout/UiHeader.vue
1 <template> 1 <template>
2 <div id="UiHeader"> 2 <div id="UiHeader">
3 - <el-menu router :default-active="activeIndex" menu-trigger="click" background-color="#545c64" text-color="#fff"  
4 - active-text-color="#ffd04b" mode="horizontal"> 3 +
  4 + <el-menu router :default-active="activeIndex" menu-trigger="click" background-color="#001529" text-color="#fff"
  5 + active-text-color="#1890ff" mode="horizontal">
  6 +
5 <el-menu-item index="/control">控制台</el-menu-item> 7 <el-menu-item index="/control">控制台</el-menu-item>
6 - <el-menu-item index="/live">实时监控</el-menu-item> 8 + <el-menu-item index="/live">分屏监控</el-menu-item>
7 <el-menu-item index="/deviceList">国标设备</el-menu-item> 9 <el-menu-item index="/deviceList">国标设备</el-menu-item>
8 <el-menu-item index="/map">电子地图</el-menu-item> 10 <el-menu-item index="/map">电子地图</el-menu-item>
9 <el-menu-item index="/pushVideoList">推流列表</el-menu-item> 11 <el-menu-item index="/pushVideoList">推流列表</el-menu-item>
@@ -148,4 +150,8 @@ export default { @@ -148,4 +150,8 @@ export default {
148 #UiHeader .el-switch__label.is-active{ 150 #UiHeader .el-switch__label.is-active{
149 color: #409EFF; 151 color: #409EFF;
150 } 152 }
  153 +#UiHeader .el-menu-item.is-active {
  154 + color: #fff!important;
  155 + background-color: #1890ff!important;
  156 +}
151 </style> 157 </style>
web_src/src/router/index.js
@@ -7,7 +7,7 @@ import deviceList from &#39;../components/DeviceList.vue&#39; @@ -7,7 +7,7 @@ import deviceList from &#39;../components/DeviceList.vue&#39;
7 import channelList from '../components/channelList.vue' 7 import channelList from '../components/channelList.vue'
8 import pushVideoList from '../components/PushVideoList.vue' 8 import pushVideoList from '../components/PushVideoList.vue'
9 import streamProxyList from '../components/StreamProxyList.vue' 9 import streamProxyList from '../components/StreamProxyList.vue'
10 -import devicePosition from '../components/devicePosition.vue' 10 +import map from '../components/map.vue'
11 import login from '../components/Login.vue' 11 import login from '../components/Login.vue'
12 import parentPlatformList from '../components/ParentPlatformList.vue' 12 import parentPlatformList from '../components/ParentPlatformList.vue'
13 import cloudRecord from '../components/CloudRecord.vue' 13 import cloudRecord from '../components/CloudRecord.vue'
@@ -69,9 +69,9 @@ export default new VueRouter({ @@ -69,9 +69,9 @@ export default new VueRouter({
69 component: parentPlatformList, 69 component: parentPlatformList,
70 }, 70 },
71 { 71 {
72 - path: '/devicePosition/:deviceId/:parentChannelId/:count/:page',  
73 - name: 'devicePosition',  
74 - component: devicePosition, 72 + path: '/map/:deviceId/:parentChannelId/:count/:page',
  73 + name: 'map',
  74 + component: map,
75 }, 75 },
76 { 76 {
77 path: '/cloudRecord', 77 path: '/cloudRecord',
@@ -100,8 +100,8 @@ export default new VueRouter({ @@ -100,8 +100,8 @@ export default new VueRouter({
100 }, 100 },
101 { 101 {
102 path: '/map', 102 path: '/map',
103 - name: 'devicePosition',  
104 - component: devicePosition, 103 + name: 'map',
  104 + component: map,
105 }, 105 },
106 ] 106 ]
107 }, 107 },