Commit 26739237e2d93460eb869067a6004bfa63a1bdb8
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
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 |
pom.xml
| @@ -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<CatalogEvent> { | @@ -66,7 +66,7 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> { | ||
| 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<StreamPushIte | @@ -81,6 +81,11 @@ public class StreamPushItem extends GbStream implements Comparable<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<StreamPushIte | @@ -219,5 +224,13 @@ public class StreamPushItem extends GbStream implements Comparable<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
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)); |