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 66 changed files with 2399 additions and 998 deletions

Too many changes to show.

To preserve performance only 66 of 106 files are displayed.

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));