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
106 changed files
with
3655 additions
and
1463 deletions
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)); |
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
| @@ -21,6 +21,8 @@ import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; | @@ -21,6 +21,8 @@ import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; | ||
| 21 | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | 21 | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| 22 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | 22 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 23 | import com.genersoft.iot.vmp.service.IMediaServerService; | 23 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 24 | +import com.genersoft.iot.vmp.service.IMediaService; | ||
| 25 | +import com.genersoft.iot.vmp.service.IPlayService; | ||
| 24 | import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; | 26 | import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; |
| 25 | import com.genersoft.iot.vmp.service.bean.PlayBackCallback; | 27 | import com.genersoft.iot.vmp.service.bean.PlayBackCallback; |
| 26 | import com.genersoft.iot.vmp.service.bean.PlayBackResult; | 28 | import com.genersoft.iot.vmp.service.bean.PlayBackResult; |
| @@ -32,8 +34,6 @@ import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; | @@ -32,8 +34,6 @@ import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; | ||
| 32 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | 34 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| 33 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; | 35 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; |
| 34 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; | 36 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; |
| 35 | -import com.genersoft.iot.vmp.service.IMediaService; | ||
| 36 | -import com.genersoft.iot.vmp.service.IPlayService; | ||
| 37 | import gov.nist.javax.sip.stack.SIPDialog; | 37 | import gov.nist.javax.sip.stack.SIPDialog; |
| 38 | import org.slf4j.Logger; | 38 | import org.slf4j.Logger; |
| 39 | import org.slf4j.LoggerFactory; | 39 | import org.slf4j.LoggerFactory; |
| @@ -49,6 +49,7 @@ import javax.sip.SipException; | @@ -49,6 +49,7 @@ import javax.sip.SipException; | ||
| 49 | import java.io.FileNotFoundException; | 49 | import java.io.FileNotFoundException; |
| 50 | import java.math.BigDecimal; | 50 | import java.math.BigDecimal; |
| 51 | import java.text.ParseException; | 51 | import java.text.ParseException; |
| 52 | +import java.math.RoundingMode; | ||
| 52 | import java.util.*; | 53 | import java.util.*; |
| 53 | import java.util.stream.Collectors; | 54 | import java.util.stream.Collectors; |
| 54 | import java.util.stream.Stream; | 55 | import java.util.stream.Stream; |
| @@ -75,9 +76,6 @@ public class PlayServiceImpl implements IPlayService { | @@ -75,9 +76,6 @@ public class PlayServiceImpl implements IPlayService { | ||
| 75 | private IRedisCatchStorage redisCatchStorage; | 76 | private IRedisCatchStorage redisCatchStorage; |
| 76 | 77 | ||
| 77 | @Autowired | 78 | @Autowired |
| 78 | - private RedisUtil redis; | ||
| 79 | - | ||
| 80 | - @Autowired | ||
| 81 | private DeferredResultHolder resultHolder; | 79 | private DeferredResultHolder resultHolder; |
| 82 | 80 | ||
| 83 | @Autowired | 81 | @Autowired |
| @@ -140,36 +138,19 @@ public class PlayServiceImpl implements IPlayService { | @@ -140,36 +138,19 @@ public class PlayServiceImpl implements IPlayService { | ||
| 140 | result.onCompletion(()->{ | 138 | result.onCompletion(()->{ |
| 141 | // 点播结束时调用截图接口 | 139 | // 点播结束时调用截图接口 |
| 142 | // TODO 应该在上流时调用更好,结束也可能是错误结束 | 140 | // TODO 应该在上流时调用更好,结束也可能是错误结束 |
| 143 | - try { | ||
| 144 | - String classPath = ResourceUtils.getURL("classpath:").getPath(); | ||
| 145 | - // 兼容打包为jar的class路径 | ||
| 146 | - if(classPath.contains("jar")) { | ||
| 147 | - classPath = classPath.substring(0, classPath.lastIndexOf(".")); | ||
| 148 | - classPath = classPath.substring(0, classPath.lastIndexOf("/") + 1); | ||
| 149 | - } | ||
| 150 | - if (classPath.startsWith("file:")) { | ||
| 151 | - classPath = classPath.substring(classPath.indexOf(":") + 1); | 141 | + String path = "static/static/snap/"; |
| 142 | + String fileName = deviceId + "_" + channelId + ".jpg"; | ||
| 143 | + ResponseEntity responseEntity = (ResponseEntity)result.getResult(); | ||
| 144 | + if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) { | ||
| 145 | + WVPResult wvpResult = (WVPResult)responseEntity.getBody(); | ||
| 146 | + if (Objects.requireNonNull(wvpResult).getCode() == 0) { | ||
| 147 | + StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData(); | ||
| 148 | + MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId()); | ||
| 149 | + String streamUrl = streamInfoForSuccess.getFmp4(); | ||
| 150 | + // 请求截图 | ||
| 151 | + logger.info("[请求截图]: " + fileName); | ||
| 152 | + zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName); | ||
| 152 | } | 153 | } |
| 153 | - String path = classPath + "static/static/snap/"; | ||
| 154 | - // 兼容Windows系统路径(去除前面的“/”) | ||
| 155 | - if(System.getProperty("os.name").contains("indows")) { | ||
| 156 | - path = path.substring(1); | ||
| 157 | - } | ||
| 158 | - String fileName = deviceId + "_" + channelId + ".jpg"; | ||
| 159 | - ResponseEntity responseEntity = (ResponseEntity)result.getResult(); | ||
| 160 | - if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) { | ||
| 161 | - WVPResult wvpResult = (WVPResult)responseEntity.getBody(); | ||
| 162 | - if (Objects.requireNonNull(wvpResult).getCode() == 0) { | ||
| 163 | - StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData(); | ||
| 164 | - MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId()); | ||
| 165 | - String streamUrl = streamInfoForSuccess.getFmp4(); | ||
| 166 | - // 请求截图 | ||
| 167 | - logger.info("[请求截图]: " + fileName); | ||
| 168 | - zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName); | ||
| 169 | - } | ||
| 170 | - } | ||
| 171 | - } catch (FileNotFoundException e) { | ||
| 172 | - e.printStackTrace(); | ||
| 173 | } | 154 | } |
| 174 | }); | 155 | }); |
| 175 | if (streamInfo != null) { | 156 | if (streamInfo != null) { |
| @@ -186,24 +167,33 @@ public class PlayServiceImpl implements IPlayService { | @@ -186,24 +167,33 @@ public class PlayServiceImpl implements IPlayService { | ||
| 186 | MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); | 167 | MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); |
| 187 | 168 | ||
| 188 | JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); | 169 | JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); |
| 189 | - if (rtpInfo != null && rtpInfo.getBoolean("exist")) { | 170 | + if(rtpInfo.getInteger("code") == 0){ |
| 171 | + if (rtpInfo.getBoolean("exist")) { | ||
| 190 | 172 | ||
| 191 | - WVPResult wvpResult = new WVPResult(); | ||
| 192 | - wvpResult.setCode(0); | ||
| 193 | - wvpResult.setMsg("success"); | ||
| 194 | - wvpResult.setData(streamInfo); | ||
| 195 | - msg.setData(wvpResult); | 173 | + WVPResult wvpResult = new WVPResult(); |
| 174 | + wvpResult.setCode(0); | ||
| 175 | + wvpResult.setMsg("success"); | ||
| 176 | + wvpResult.setData(streamInfo); | ||
| 177 | + msg.setData(wvpResult); | ||
| 196 | 178 | ||
| 197 | - resultHolder.invokeAllResult(msg); | ||
| 198 | - if (hookEvent != null) { | ||
| 199 | - hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo))); | 179 | + resultHolder.invokeAllResult(msg); |
| 180 | + if (hookEvent != null) { | ||
| 181 | + hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo))); | ||
| 182 | + } | ||
| 183 | + }else { | ||
| 184 | + redisCatchStorage.stopPlay(streamInfo); | ||
| 185 | + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | ||
| 186 | + streamInfo = null; | ||
| 200 | } | 187 | } |
| 201 | }else { | 188 | }else { |
| 189 | + //zlm连接失败 | ||
| 202 | redisCatchStorage.stopPlay(streamInfo); | 190 | redisCatchStorage.stopPlay(streamInfo); |
| 203 | storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | 191 | storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); |
| 204 | streamInfo = null; | 192 | streamInfo = null; |
| 193 | + | ||
| 205 | } | 194 | } |
| 206 | 195 | ||
| 196 | + | ||
| 207 | } | 197 | } |
| 208 | if (streamInfo == null) { | 198 | if (streamInfo == null) { |
| 209 | String streamId = null; | 199 | String streamId = null; |
| @@ -256,33 +246,41 @@ public class PlayServiceImpl implements IPlayService { | @@ -256,33 +246,41 @@ public class PlayServiceImpl implements IPlayService { | ||
| 256 | if (ssrcInfo == null) { | 246 | if (ssrcInfo == null) { |
| 257 | ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); | 247 | ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); |
| 258 | } | 248 | } |
| 259 | - | 249 | + logger.info("[点播开始] deviceId: {}, channelId: {}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getSsrc() ); |
| 260 | // 超时处理 | 250 | // 超时处理 |
| 261 | String timeOutTaskKey = UUID.randomUUID().toString(); | 251 | String timeOutTaskKey = UUID.randomUUID().toString(); |
| 262 | SSRCInfo finalSsrcInfo = ssrcInfo; | 252 | SSRCInfo finalSsrcInfo = ssrcInfo; |
| 263 | dynamicTask.startDelay( timeOutTaskKey,()->{ | 253 | dynamicTask.startDelay( timeOutTaskKey,()->{ |
| 264 | - logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", device.getDeviceId(), channelId)); | ||
| 265 | 254 | ||
| 266 | SIPDialog dialog = streamSession.getDialogByStream(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); | 255 | SIPDialog dialog = streamSession.getDialogByStream(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); |
| 267 | if (dialog != null) { | 256 | if (dialog != null) { |
| 257 | + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {}", device.getDeviceId(), channelId); | ||
| 268 | timeoutCallback.run(1, "收流超时"); | 258 | timeoutCallback.run(1, "收流超时"); |
| 269 | // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 | 259 | // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 |
| 270 | cmder.streamByeCmd(device.getDeviceId(), channelId, finalSsrcInfo.getStream(), null); | 260 | cmder.streamByeCmd(device.getDeviceId(), channelId, finalSsrcInfo.getStream(), null); |
| 271 | }else { | 261 | }else { |
| 262 | + logger.info("[点播超时] 消息未响应 deviceId: {}, channelId: {}", device.getDeviceId(), channelId); | ||
| 272 | timeoutCallback.run(0, "点播超时"); | 263 | timeoutCallback.run(0, "点播超时"); |
| 273 | mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); | 264 | mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); |
| 274 | mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); | 265 | mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); |
| 275 | streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); | 266 | streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); |
| 276 | } | 267 | } |
| 277 | - }, userSetting.getPlayTimeout()*1000); | 268 | + }, userSetting.getPlayTimeout()); |
| 278 | final String ssrc = ssrcInfo.getSsrc(); | 269 | final String ssrc = ssrcInfo.getSsrc(); |
| 279 | final String stream = ssrcInfo.getStream(); | 270 | final String stream = ssrcInfo.getStream(); |
| 271 | + //端口获取失败的ssrcInfo 没有必要发送点播指令 | ||
| 272 | + if(ssrcInfo.getPort() <= 0){ | ||
| 273 | + logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo); | ||
| 274 | + return; | ||
| 275 | + } | ||
| 280 | cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { | 276 | cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { |
| 281 | logger.info("收到订阅消息: " + response.toJSONString()); | 277 | logger.info("收到订阅消息: " + response.toJSONString()); |
| 282 | dynamicTask.stop(timeOutTaskKey); | 278 | dynamicTask.stop(timeOutTaskKey); |
| 283 | // hook响应 | 279 | // hook响应 |
| 284 | onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid); | 280 | onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid); |
| 285 | hookEvent.response(mediaServerItemInuse, response); | 281 | hookEvent.response(mediaServerItemInuse, response); |
| 282 | + logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId); | ||
| 283 | + | ||
| 286 | }, (event) -> { | 284 | }, (event) -> { |
| 287 | ResponseEvent responseEvent = (ResponseEvent)event.event; | 285 | ResponseEvent responseEvent = (ResponseEvent)event.event; |
| 288 | String contentString = new String(responseEvent.getResponse().getRawContent()); | 286 | String contentString = new String(responseEvent.getResponse().getRawContent()); |
| @@ -296,8 +294,10 @@ public class PlayServiceImpl implements IPlayService { | @@ -296,8 +294,10 @@ public class PlayServiceImpl implements IPlayService { | ||
| 296 | if (ssrc.equals(ssrcInResponse)) { | 294 | if (ssrc.equals(ssrcInResponse)) { |
| 297 | return; | 295 | return; |
| 298 | } | 296 | } |
| 299 | - logger.info("[SIP 消息] 收到invite 200, 发现下级自定义了ssrc 开启修正"); | 297 | + logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse ); |
| 300 | if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { | 298 | if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { |
| 299 | + logger.info("[SIP 消息] SSRC修正 {}->{}", ssrc, ssrcInResponse); | ||
| 300 | + | ||
| 301 | if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) { | 301 | if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) { |
| 302 | // ssrc 不可用 | 302 | // ssrc 不可用 |
| 303 | // 释放ssrc | 303 | // 释放ssrc |
| @@ -450,7 +450,7 @@ public class PlayServiceImpl implements IPlayService { | @@ -450,7 +450,7 @@ public class PlayServiceImpl implements IPlayService { | ||
| 450 | cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null); | 450 | cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null); |
| 451 | // 回复之前所有的点播请求 | 451 | // 回复之前所有的点播请求 |
| 452 | playBackCallback.call(playBackResult); | 452 | playBackCallback.call(playBackResult); |
| 453 | - }, userSetting.getPlayTimeout()*1000); | 453 | + }, userSetting.getPlayTimeout()); |
| 454 | 454 | ||
| 455 | cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack, | 455 | cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack, |
| 456 | (InviteStreamInfo inviteStreamInfo) -> { | 456 | (InviteStreamInfo inviteStreamInfo) -> { |
| @@ -539,7 +539,7 @@ public class PlayServiceImpl implements IPlayService { | @@ -539,7 +539,7 @@ public class PlayServiceImpl implements IPlayService { | ||
| 539 | cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null); | 539 | cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null); |
| 540 | // 回复之前所有的点播请求 | 540 | // 回复之前所有的点播请求 |
| 541 | hookCallBack.call(downloadResult); | 541 | hookCallBack.call(downloadResult); |
| 542 | - }, userSetting.getPlayTimeout()*1000); | 542 | + }, userSetting.getPlayTimeout()); |
| 543 | cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack, | 543 | cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack, |
| 544 | inviteStreamInfo -> { | 544 | inviteStreamInfo -> { |
| 545 | logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString()); | 545 | logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString()); |
| @@ -605,7 +605,7 @@ public class PlayServiceImpl implements IPlayService { | @@ -605,7 +605,7 @@ public class PlayServiceImpl implements IPlayService { | ||
| 605 | 605 | ||
| 606 | BigDecimal currentCount = new BigDecimal(duration/1000); | 606 | BigDecimal currentCount = new BigDecimal(duration/1000); |
| 607 | BigDecimal totalCount = new BigDecimal(end-start); | 607 | BigDecimal totalCount = new BigDecimal(end-start); |
| 608 | - BigDecimal divide = currentCount.divide(totalCount,2, BigDecimal.ROUND_HALF_UP); | 608 | + BigDecimal divide = currentCount.divide(totalCount,2, RoundingMode.HALF_UP); |
| 609 | double process = divide.doubleValue(); | 609 | double process = divide.doubleValue(); |
| 610 | streamInfo.setProgress(process); | 610 | streamInfo.setProgress(process); |
| 611 | } | 611 | } |
| @@ -728,4 +728,9 @@ public class PlayServiceImpl implements IPlayService { | @@ -728,4 +728,9 @@ public class PlayServiceImpl implements IPlayService { | ||
| 728 | 728 | ||
| 729 | 729 | ||
| 730 | } | 730 | } |
| 731 | + | ||
| 732 | + @Override | ||
| 733 | + public void zlmServerOnline(String mediaServerId) { | ||
| 734 | + // 似乎没啥需要做的 | ||
| 735 | + } | ||
| 731 | } | 736 | } |
src/main/java/com/genersoft/iot/vmp/service/impl/RedisGbPlayMsgListener.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.service.impl; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSON; | ||
| 4 | +import com.alibaba.fastjson.JSONObject; | ||
| 5 | +import com.genersoft.iot.vmp.conf.DynamicTask; | ||
| 6 | +import com.genersoft.iot.vmp.conf.UserSetting; | ||
| 7 | +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; | ||
| 8 | +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; | ||
| 9 | +import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager; | ||
| 10 | +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; | ||
| 11 | +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | ||
| 12 | +import com.genersoft.iot.vmp.service.IMediaServerService; | ||
| 13 | +import com.genersoft.iot.vmp.service.bean.*; | ||
| 14 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | ||
| 15 | +import com.genersoft.iot.vmp.utils.redis.RedisUtil; | ||
| 16 | +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | ||
| 17 | +import org.slf4j.Logger; | ||
| 18 | +import org.slf4j.LoggerFactory; | ||
| 19 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 20 | +import org.springframework.data.redis.connection.Message; | ||
| 21 | +import org.springframework.data.redis.connection.MessageListener; | ||
| 22 | +import org.springframework.stereotype.Component; | ||
| 23 | + | ||
| 24 | +import javax.sip.InvalidArgumentException; | ||
| 25 | +import javax.sip.SipException; | ||
| 26 | +import java.text.ParseException; | ||
| 27 | +import java.util.HashMap; | ||
| 28 | +import java.util.Map; | ||
| 29 | +import java.util.UUID; | ||
| 30 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 31 | + | ||
| 32 | + | ||
| 33 | +/** | ||
| 34 | + * 监听下级发送推送信息,并发送国标推流消息上级 | ||
| 35 | + * @author lin | ||
| 36 | + */ | ||
| 37 | +@Component | ||
| 38 | +public class RedisGbPlayMsgListener implements MessageListener { | ||
| 39 | + | ||
| 40 | + private final static Logger logger = LoggerFactory.getLogger(RedisGbPlayMsgListener.class); | ||
| 41 | + | ||
| 42 | + public static final String WVP_PUSH_STREAM_KEY = "WVP_PUSH_STREAM"; | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 流媒体不存在的错误玛 | ||
| 46 | + */ | ||
| 47 | + public static final int ERROR_CODE_MEDIA_SERVER_NOT_FOUND = -1; | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * 离线的错误玛 | ||
| 51 | + */ | ||
| 52 | + public static final int ERROR_CODE_OFFLINE = -2; | ||
| 53 | + | ||
| 54 | + /** | ||
| 55 | + * 超时的错误玛 | ||
| 56 | + */ | ||
| 57 | + public static final int ERROR_CODE_TIMEOUT = -3; | ||
| 58 | + | ||
| 59 | + private Map<String, PlayMsgCallback> callbacks = new ConcurrentHashMap<>(); | ||
| 60 | + private Map<String, PlayMsgCallbackForStartSendRtpStream> callbacksForStartSendRtpStream = new ConcurrentHashMap<>(); | ||
| 61 | + private Map<String, PlayMsgErrorCallback> callbacksForError = new ConcurrentHashMap<>(); | ||
| 62 | + | ||
| 63 | + @Autowired | ||
| 64 | + private UserSetting userSetting; | ||
| 65 | + | ||
| 66 | + @Autowired | ||
| 67 | + private RedisUtil redis; | ||
| 68 | + | ||
| 69 | + @Autowired | ||
| 70 | + private ZLMMediaListManager zlmMediaListManager; | ||
| 71 | + | ||
| 72 | + @Autowired | ||
| 73 | + private ZLMRTPServerFactory zlmrtpServerFactory; | ||
| 74 | + | ||
| 75 | + @Autowired | ||
| 76 | + private IMediaServerService mediaServerService; | ||
| 77 | + | ||
| 78 | + @Autowired | ||
| 79 | + private IRedisCatchStorage redisCatchStorage; | ||
| 80 | + | ||
| 81 | + @Autowired | ||
| 82 | + private DynamicTask dynamicTask; | ||
| 83 | + | ||
| 84 | + @Autowired | ||
| 85 | + private ZLMMediaListManager mediaListManager; | ||
| 86 | + | ||
| 87 | + @Autowired | ||
| 88 | + private ZLMHttpHookSubscribe subscribe; | ||
| 89 | + | ||
| 90 | + | ||
| 91 | + public interface PlayMsgCallback{ | ||
| 92 | + void handler(ResponseSendItemMsg responseSendItemMsg); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + public interface PlayMsgCallbackForStartSendRtpStream{ | ||
| 96 | + void handler(JSONObject jsonObject); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + public interface PlayMsgErrorCallback{ | ||
| 100 | + void handler(WVPResult wvpResult); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + @Override | ||
| 104 | + public void onMessage(Message message, byte[] bytes) { | ||
| 105 | + JSONObject msgJSON = JSON.parseObject(message.getBody(), JSONObject.class); | ||
| 106 | + WvpRedisMsg wvpRedisMsg = JSON.toJavaObject(msgJSON, WvpRedisMsg.class); | ||
| 107 | + if (!userSetting.getServerId().equals(wvpRedisMsg.getToId())) { | ||
| 108 | + return; | ||
| 109 | + } | ||
| 110 | + if (WvpRedisMsg.isRequest(wvpRedisMsg)) { | ||
| 111 | + logger.info("[收到REDIS通知] 请求: {}", new String(message.getBody())); | ||
| 112 | + | ||
| 113 | + switch (wvpRedisMsg.getCmd()){ | ||
| 114 | + case WvpRedisMsgCmd.GET_SEND_ITEM: | ||
| 115 | + RequestSendItemMsg content = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), RequestSendItemMsg.class); | ||
| 116 | + requestSendItemMsgHand(content, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial()); | ||
| 117 | + break; | ||
| 118 | + case WvpRedisMsgCmd.REQUEST_PUSH_STREAM: | ||
| 119 | + RequestPushStreamMsg param = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), RequestPushStreamMsg.class);; | ||
| 120 | + requestPushStreamMsgHand(param, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial()); | ||
| 121 | + break; | ||
| 122 | + default: | ||
| 123 | + break; | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + }else { | ||
| 127 | + logger.info("[收到REDIS通知] 回复: {}", new String(message.getBody())); | ||
| 128 | + switch (wvpRedisMsg.getCmd()){ | ||
| 129 | + case WvpRedisMsgCmd.GET_SEND_ITEM: | ||
| 130 | + | ||
| 131 | + WVPResult content = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), WVPResult.class); | ||
| 132 | + | ||
| 133 | + String key = wvpRedisMsg.getSerial(); | ||
| 134 | + switch (content.getCode()) { | ||
| 135 | + case 0: | ||
| 136 | + ResponseSendItemMsg responseSendItemMsg =JSON.toJavaObject((JSONObject)content.getData(), ResponseSendItemMsg.class); | ||
| 137 | + PlayMsgCallback playMsgCallback = callbacks.get(key); | ||
| 138 | + if (playMsgCallback != null) { | ||
| 139 | + callbacksForError.remove(key); | ||
| 140 | + playMsgCallback.handler(responseSendItemMsg); | ||
| 141 | + } | ||
| 142 | + break; | ||
| 143 | + case ERROR_CODE_MEDIA_SERVER_NOT_FOUND: | ||
| 144 | + case ERROR_CODE_OFFLINE: | ||
| 145 | + case ERROR_CODE_TIMEOUT: | ||
| 146 | + PlayMsgErrorCallback errorCallback = callbacksForError.get(key); | ||
| 147 | + if (errorCallback != null) { | ||
| 148 | + callbacks.remove(key); | ||
| 149 | + errorCallback.handler(content); | ||
| 150 | + } | ||
| 151 | + break; | ||
| 152 | + default: | ||
| 153 | + break; | ||
| 154 | + } | ||
| 155 | + break; | ||
| 156 | + case WvpRedisMsgCmd.REQUEST_PUSH_STREAM: | ||
| 157 | + WVPResult wvpResult = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), WVPResult.class); | ||
| 158 | + String serial = wvpRedisMsg.getSerial(); | ||
| 159 | + switch (wvpResult.getCode()) { | ||
| 160 | + case 0: | ||
| 161 | + JSONObject jsonObject = (JSONObject)wvpResult.getData(); | ||
| 162 | + PlayMsgCallbackForStartSendRtpStream playMsgCallback = callbacksForStartSendRtpStream.get(serial); | ||
| 163 | + if (playMsgCallback != null) { | ||
| 164 | + callbacksForError.remove(serial); | ||
| 165 | + playMsgCallback.handler(jsonObject); | ||
| 166 | + } | ||
| 167 | + break; | ||
| 168 | + case ERROR_CODE_MEDIA_SERVER_NOT_FOUND: | ||
| 169 | + case ERROR_CODE_OFFLINE: | ||
| 170 | + case ERROR_CODE_TIMEOUT: | ||
| 171 | + PlayMsgErrorCallback errorCallback = callbacksForError.get(serial); | ||
| 172 | + if (errorCallback != null) { | ||
| 173 | + callbacks.remove(serial); | ||
| 174 | + errorCallback.handler(wvpResult); | ||
| 175 | + } | ||
| 176 | + break; | ||
| 177 | + default: | ||
| 178 | + break; | ||
| 179 | + } | ||
| 180 | + break; | ||
| 181 | + default: | ||
| 182 | + break; | ||
| 183 | + } | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + | ||
| 187 | + | ||
| 188 | + | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + /** | ||
| 192 | + * 处理收到的请求推流的请求 | ||
| 193 | + */ | ||
| 194 | + private void requestPushStreamMsgHand(RequestPushStreamMsg requestPushStreamMsg, String fromId, String serial) { | ||
| 195 | + MediaServerItem mediaInfo = mediaServerService.getOne(requestPushStreamMsg.getMediaServerId()); | ||
| 196 | + if (mediaInfo == null) { | ||
| 197 | + // TODO 回复错误 | ||
| 198 | + return; | ||
| 199 | + } | ||
| 200 | + String is_Udp = requestPushStreamMsg.isTcp() ? "0" : "1"; | ||
| 201 | + Map<String, Object> param = new HashMap<>(); | ||
| 202 | + param.put("vhost","__defaultVhost__"); | ||
| 203 | + param.put("app",requestPushStreamMsg.getApp()); | ||
| 204 | + param.put("stream",requestPushStreamMsg.getStream()); | ||
| 205 | + param.put("ssrc", requestPushStreamMsg.getSsrc()); | ||
| 206 | + param.put("dst_url",requestPushStreamMsg.getIp()); | ||
| 207 | + param.put("dst_port", requestPushStreamMsg.getPort()); | ||
| 208 | + param.put("is_udp", is_Udp); | ||
| 209 | + param.put("src_port", requestPushStreamMsg.getSrcPort()); | ||
| 210 | + param.put("pt", requestPushStreamMsg.getPt()); | ||
| 211 | + param.put("use_ps", requestPushStreamMsg.isPs() ? "1" : "0"); | ||
| 212 | + param.put("only_audio", requestPushStreamMsg.isOnlyAudio() ? "1" : "0"); | ||
| 213 | + JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); | ||
| 214 | + // 回复消息 | ||
| 215 | + responsePushStream(jsonObject, fromId, serial); | ||
| 216 | + } | ||
| 217 | + | ||
| 218 | + private void responsePushStream(JSONObject content, String toId, String serial) { | ||
| 219 | + | ||
| 220 | + WVPResult<JSONObject> result = new WVPResult<>(); | ||
| 221 | + result.setCode(0); | ||
| 222 | + result.setData(content); | ||
| 223 | + | ||
| 224 | + WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId, | ||
| 225 | + WvpRedisMsgCmd.REQUEST_PUSH_STREAM, serial, result); | ||
| 226 | + JSONObject jsonObject = (JSONObject)JSON.toJSON(response); | ||
| 227 | + redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); | ||
| 228 | + } | ||
| 229 | + | ||
| 230 | + /** | ||
| 231 | + * 处理收到的请求sendItem的请求 | ||
| 232 | + */ | ||
| 233 | + private void requestSendItemMsgHand(RequestSendItemMsg content, String toId, String serial) { | ||
| 234 | + MediaServerItem mediaServerItem = mediaServerService.getOne(content.getMediaServerId()); | ||
| 235 | + if (mediaServerItem == null) { | ||
| 236 | + logger.info("[回复推流信息] 流媒体{}不存在 ", content.getMediaServerId()); | ||
| 237 | + | ||
| 238 | + WVPResult<SendRtpItem> result = new WVPResult<>(); | ||
| 239 | + result.setCode(ERROR_CODE_MEDIA_SERVER_NOT_FOUND); | ||
| 240 | + result.setMsg("流媒体不存在"); | ||
| 241 | + | ||
| 242 | + WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId, | ||
| 243 | + WvpRedisMsgCmd.GET_SEND_ITEM, serial, result); | ||
| 244 | + | ||
| 245 | + JSONObject jsonObject = (JSONObject)JSON.toJSON(response); | ||
| 246 | + redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); | ||
| 247 | + return; | ||
| 248 | + } | ||
| 249 | + // 确定流是否在线 | ||
| 250 | + boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream()); | ||
| 251 | + if (streamReady) { | ||
| 252 | + logger.info("[回复推流信息] {}/{}", content.getApp(), content.getStream()); | ||
| 253 | + responseSendItem(mediaServerItem, content, toId, serial); | ||
| 254 | + }else { | ||
| 255 | + // 流已经离线 | ||
| 256 | + // 发送redis消息以使设备上线 | ||
| 257 | + logger.info("[ app={}, stream={} ]通道离线,发送redis信息控制设备开始推流",content.getApp(), content.getStream()); | ||
| 258 | + | ||
| 259 | + String taskKey = UUID.randomUUID().toString(); | ||
| 260 | + // 设置超时 | ||
| 261 | + dynamicTask.startDelay(taskKey, ()->{ | ||
| 262 | + logger.info("[ app={}, stream={} ] 等待设备开始推流超时", content.getApp(), content.getStream()); | ||
| 263 | + WVPResult<SendRtpItem> result = new WVPResult<>(); | ||
| 264 | + result.setCode(ERROR_CODE_TIMEOUT); | ||
| 265 | + WvpRedisMsg response = WvpRedisMsg.getResponseInstance( | ||
| 266 | + userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result | ||
| 267 | + ); | ||
| 268 | + JSONObject jsonObject = (JSONObject)JSON.toJSON(response); | ||
| 269 | + redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); | ||
| 270 | + }, userSetting.getPlatformPlayTimeout()); | ||
| 271 | + | ||
| 272 | + // 添加订阅 | ||
| 273 | + JSONObject subscribeKey = new JSONObject(); | ||
| 274 | + subscribeKey.put("app", content.getApp()); | ||
| 275 | + subscribeKey.put("stream", content.getStream()); | ||
| 276 | + subscribeKey.put("regist", true); | ||
| 277 | + subscribeKey.put("schema", "rtmp"); | ||
| 278 | + subscribeKey.put("mediaServerId", mediaServerItem.getId()); | ||
| 279 | + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, | ||
| 280 | + (MediaServerItem mediaServerItemInUse, JSONObject json)->{ | ||
| 281 | + dynamicTask.stop(taskKey); | ||
| 282 | + responseSendItem(mediaServerItem, content, toId, serial); | ||
| 283 | + }); | ||
| 284 | + | ||
| 285 | + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, content.getApp(), content.getStream(), | ||
| 286 | + content.getChannelId(), content.getPlatformId(), content.getPlatformName(), content.getServerId(), | ||
| 287 | + content.getMediaServerId()); | ||
| 288 | + redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); | ||
| 289 | + | ||
| 290 | + } | ||
| 291 | + } | ||
| 292 | + | ||
| 293 | + /** | ||
| 294 | + * 将获取到的sendItem发送出去 | ||
| 295 | + */ | ||
| 296 | + private void responseSendItem(MediaServerItem mediaServerItem, RequestSendItemMsg content, String toId, String serial) { | ||
| 297 | + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(), | ||
| 298 | + content.getPort(), content.getSsrc(), content.getPlatformId(), | ||
| 299 | + content.getApp(), content.getStream(), content.getChannelId(), | ||
| 300 | + content.getTcp()); | ||
| 301 | + | ||
| 302 | + WVPResult<ResponseSendItemMsg> result = new WVPResult<>(); | ||
| 303 | + result.setCode(0); | ||
| 304 | + ResponseSendItemMsg responseSendItemMsg = new ResponseSendItemMsg(); | ||
| 305 | + responseSendItemMsg.setSendRtpItem(sendRtpItem); | ||
| 306 | + responseSendItemMsg.setMediaServerItem(mediaServerItem); | ||
| 307 | + result.setData(responseSendItemMsg); | ||
| 308 | + | ||
| 309 | + WvpRedisMsg response = WvpRedisMsg.getResponseInstance( | ||
| 310 | + userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result | ||
| 311 | + ); | ||
| 312 | + JSONObject jsonObject = (JSONObject)JSON.toJSON(response); | ||
| 313 | + redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); | ||
| 314 | + } | ||
| 315 | + | ||
| 316 | + /** | ||
| 317 | + * 发送消息要求下级生成推流信息 | ||
| 318 | + * @param serverId 下级服务ID | ||
| 319 | + * @param app 应用名 | ||
| 320 | + * @param stream 流ID | ||
| 321 | + * @param ip 目标IP | ||
| 322 | + * @param port 目标端口 | ||
| 323 | + * @param ssrc ssrc | ||
| 324 | + * @param platformId 平台国标编号 | ||
| 325 | + * @param channelId 通道ID | ||
| 326 | + * @param isTcp 是否使用TCP | ||
| 327 | + * @param callback 得到信息的回调 | ||
| 328 | + */ | ||
| 329 | + public void sendMsg(String serverId, String mediaServerId, String app, String stream, String ip, int port, String ssrc, | ||
| 330 | + String platformId, String channelId, boolean isTcp, String platformName, PlayMsgCallback callback, PlayMsgErrorCallback errorCallback) { | ||
| 331 | + RequestSendItemMsg requestSendItemMsg = RequestSendItemMsg.getInstance( | ||
| 332 | + serverId, mediaServerId, app, stream, ip, port, ssrc, platformId, channelId, isTcp, platformName); | ||
| 333 | + requestSendItemMsg.setServerId(serverId); | ||
| 334 | + String key = UUID.randomUUID().toString(); | ||
| 335 | + WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, WvpRedisMsgCmd.GET_SEND_ITEM, | ||
| 336 | + key, requestSendItemMsg); | ||
| 337 | + | ||
| 338 | + JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg); | ||
| 339 | + logger.info("[请求推流SendItem] {}: {}", serverId, jsonObject); | ||
| 340 | + callbacks.put(key, callback); | ||
| 341 | + callbacksForError.put(key, errorCallback); | ||
| 342 | + dynamicTask.startDelay(key, ()->{ | ||
| 343 | + callbacks.remove(key); | ||
| 344 | + callbacksForError.remove(key); | ||
| 345 | + WVPResult<Object> wvpResult = new WVPResult<>(); | ||
| 346 | + wvpResult.setCode(ERROR_CODE_TIMEOUT); | ||
| 347 | + wvpResult.setMsg("timeout"); | ||
| 348 | + errorCallback.handler(wvpResult); | ||
| 349 | + }, userSetting.getPlatformPlayTimeout()); | ||
| 350 | + redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); | ||
| 351 | + } | ||
| 352 | + | ||
| 353 | + /** | ||
| 354 | + * 发送请求推流的消息 | ||
| 355 | + * @param param 推流参数 | ||
| 356 | + * @param callback 回调 | ||
| 357 | + */ | ||
| 358 | + public void sendMsgForStartSendRtpStream(String serverId, RequestPushStreamMsg param, PlayMsgCallbackForStartSendRtpStream callback) { | ||
| 359 | + String key = UUID.randomUUID().toString(); | ||
| 360 | + WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, | ||
| 361 | + WvpRedisMsgCmd.REQUEST_PUSH_STREAM, key, param); | ||
| 362 | + | ||
| 363 | + JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg); | ||
| 364 | + logger.info("[REDIS 请求其他平台推流] {}: {}", serverId, jsonObject); | ||
| 365 | + dynamicTask.startDelay(key, ()->{ | ||
| 366 | + callbacksForStartSendRtpStream.remove(key); | ||
| 367 | + callbacksForError.remove(key); | ||
| 368 | + }, userSetting.getPlatformPlayTimeout()); | ||
| 369 | + callbacksForStartSendRtpStream.put(key, callback); | ||
| 370 | + callbacksForError.put(key, (wvpResult)->{ | ||
| 371 | + logger.info("[REDIS 请求其他平台推流] 失败: {}", wvpResult.getMsg()); | ||
| 372 | + callbacksForStartSendRtpStream.remove(key); | ||
| 373 | + callbacksForError.remove(key); | ||
| 374 | + }); | ||
| 375 | + redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); | ||
| 376 | + } | ||
| 377 | +} |
src/main/java/com/genersoft/iot/vmp/service/impl/RedisGPSMsgListener.java renamed to src/main/java/com/genersoft/iot/vmp/service/impl/RedisGpsMsgListener.java
| @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.service.impl; | @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.service.impl; | ||
| 3 | import com.alibaba.fastjson.JSON; | 3 | import com.alibaba.fastjson.JSON; |
| 4 | import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; | 4 | import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; |
| 5 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 5 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 6 | +import org.jetbrains.annotations.NotNull; | ||
| 6 | import org.slf4j.Logger; | 7 | import org.slf4j.Logger; |
| 7 | import org.slf4j.LoggerFactory; | 8 | import org.slf4j.LoggerFactory; |
| 8 | import org.springframework.beans.factory.annotation.Autowired; | 9 | import org.springframework.beans.factory.annotation.Autowired; |
| @@ -10,17 +11,23 @@ import org.springframework.data.redis.connection.Message; | @@ -10,17 +11,23 @@ import org.springframework.data.redis.connection.Message; | ||
| 10 | import org.springframework.data.redis.connection.MessageListener; | 11 | import org.springframework.data.redis.connection.MessageListener; |
| 11 | import org.springframework.stereotype.Component; | 12 | import org.springframework.stereotype.Component; |
| 12 | 13 | ||
| 14 | +/** | ||
| 15 | + * 接收来自redis的GPS更新通知 | ||
| 16 | + * @author lin | ||
| 17 | + */ | ||
| 13 | @Component | 18 | @Component |
| 14 | -public class RedisGPSMsgListener implements MessageListener { | 19 | +public class RedisGpsMsgListener implements MessageListener { |
| 15 | 20 | ||
| 16 | - private final static Logger logger = LoggerFactory.getLogger(RedisGPSMsgListener.class); | 21 | + private final static Logger logger = LoggerFactory.getLogger(RedisGpsMsgListener.class); |
| 17 | 22 | ||
| 18 | @Autowired | 23 | @Autowired |
| 19 | private IRedisCatchStorage redisCatchStorage; | 24 | private IRedisCatchStorage redisCatchStorage; |
| 20 | 25 | ||
| 21 | @Override | 26 | @Override |
| 22 | - public void onMessage(Message message, byte[] bytes) { | ||
| 23 | - logger.info("收到来自REDIS的GPS通知: {}", new String(message.getBody())); | 27 | + public void onMessage(@NotNull Message message, byte[] bytes) { |
| 28 | + if (logger.isDebugEnabled()) { | ||
| 29 | + logger.debug("收到来自REDIS的GPS通知: {}", new String(message.getBody())); | ||
| 30 | + } | ||
| 24 | GPSMsgInfo gpsMsgInfo = JSON.parseObject(message.getBody(), GPSMsgInfo.class); | 31 | GPSMsgInfo gpsMsgInfo = JSON.parseObject(message.getBody(), GPSMsgInfo.class); |
| 25 | redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo); | 32 | redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo); |
| 26 | } | 33 | } |
src/main/java/com/genersoft/iot/vmp/service/impl/RedisStreamMsgListener.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.service.impl; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSON; | ||
| 4 | +import com.alibaba.fastjson.JSONObject; | ||
| 5 | +import com.genersoft.iot.vmp.conf.UserSetting; | ||
| 6 | +import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage; | ||
| 7 | +import com.genersoft.iot.vmp.gb28181.bean.Device; | ||
| 8 | +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; | ||
| 9 | +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | ||
| 10 | +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; | ||
| 11 | +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; | ||
| 12 | +import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager; | ||
| 13 | +import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; | ||
| 14 | +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; | ||
| 15 | +import com.genersoft.iot.vmp.utils.DateUtil; | ||
| 16 | +import org.slf4j.Logger; | ||
| 17 | +import org.slf4j.LoggerFactory; | ||
| 18 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 19 | +import org.springframework.data.redis.connection.Message; | ||
| 20 | +import org.springframework.data.redis.connection.MessageListener; | ||
| 21 | +import org.springframework.stereotype.Component; | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + * @author lin | ||
| 26 | + */ | ||
| 27 | +@Component | ||
| 28 | +public class RedisStreamMsgListener implements MessageListener { | ||
| 29 | + | ||
| 30 | + private final static Logger logger = LoggerFactory.getLogger(RedisStreamMsgListener.class); | ||
| 31 | + | ||
| 32 | + @Autowired | ||
| 33 | + private ISIPCommander commander; | ||
| 34 | + | ||
| 35 | + @Autowired | ||
| 36 | + private ISIPCommanderForPlatform commanderForPlatform; | ||
| 37 | + | ||
| 38 | + @Autowired | ||
| 39 | + private IVideoManagerStorage storage; | ||
| 40 | + | ||
| 41 | + @Autowired | ||
| 42 | + private UserSetting userSetting; | ||
| 43 | + | ||
| 44 | + @Autowired | ||
| 45 | + private ZLMMediaListManager zlmMediaListManager; | ||
| 46 | + | ||
| 47 | + @Override | ||
| 48 | + public void onMessage(Message message, byte[] bytes) { | ||
| 49 | + | ||
| 50 | + JSONObject steamMsgJson = JSON.parseObject(message.getBody(), JSONObject.class); | ||
| 51 | + if (steamMsgJson == null) { | ||
| 52 | + logger.warn("[REDIS的ALARM通知]消息解析失败"); | ||
| 53 | + return; | ||
| 54 | + } | ||
| 55 | + String serverId = steamMsgJson.getString("serverId"); | ||
| 56 | + | ||
| 57 | + if (userSetting.getServerId().equals(serverId)) { | ||
| 58 | + // 自己发送的消息忽略即可 | ||
| 59 | + return; | ||
| 60 | + } | ||
| 61 | + logger.info("[REDIS通知] 流变化: {}", new String(message.getBody())); | ||
| 62 | + String app = steamMsgJson.getString("app"); | ||
| 63 | + String stream = steamMsgJson.getString("stream"); | ||
| 64 | + boolean register = steamMsgJson.getBoolean("register"); | ||
| 65 | + String mediaServerId = steamMsgJson.getString("mediaServerId"); | ||
| 66 | + MediaItem mediaItem = new MediaItem(); | ||
| 67 | + mediaItem.setSeverId(serverId); | ||
| 68 | + mediaItem.setApp(app); | ||
| 69 | + mediaItem.setStream(stream); | ||
| 70 | + mediaItem.setRegist(register); | ||
| 71 | + mediaItem.setMediaServerId(mediaServerId); | ||
| 72 | + mediaItem.setCreateStamp(System.currentTimeMillis()/1000); | ||
| 73 | + mediaItem.setAliveSecond(0L); | ||
| 74 | + mediaItem.setTotalReaderCount("0"); | ||
| 75 | + mediaItem.setOriginType(0); | ||
| 76 | + mediaItem.setOriginTypeStr("0"); | ||
| 77 | + mediaItem.setOriginTypeStr("unknown"); | ||
| 78 | + | ||
| 79 | + zlmMediaListManager.addPush(mediaItem); | ||
| 80 | + | ||
| 81 | + | ||
| 82 | + } | ||
| 83 | +} |
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
| @@ -107,6 +107,7 @@ public class StreamPushServiceImpl implements IStreamPushService { | @@ -107,6 +107,7 @@ public class StreamPushServiceImpl implements IStreamPushService { | ||
| 107 | streamPushItem.setStatus(true); | 107 | streamPushItem.setStatus(true); |
| 108 | streamPushItem.setStreamType("push"); | 108 | streamPushItem.setStreamType("push"); |
| 109 | streamPushItem.setVhost(item.getVhost()); | 109 | streamPushItem.setVhost(item.getVhost()); |
| 110 | + streamPushItem.setServerId(item.getSeverId()); | ||
| 110 | return streamPushItem; | 111 | return streamPushItem; |
| 111 | } | 112 | } |
| 112 | 113 |
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
| @@ -357,6 +357,15 @@ public interface IVideoManagerStorage { | @@ -357,6 +357,15 @@ public interface IVideoManagerStorage { | ||
| 357 | 357 | ||
| 358 | 358 | ||
| 359 | /** | 359 | /** |
| 360 | + * 获取但个推流 | ||
| 361 | + * @param app | ||
| 362 | + * @param stream | ||
| 363 | + * @return | ||
| 364 | + */ | ||
| 365 | + StreamPushItem getMedia(String app, String stream); | ||
| 366 | + | ||
| 367 | + | ||
| 368 | + /** | ||
| 360 | * 清空推流列表 | 369 | * 清空推流列表 |
| 361 | */ | 370 | */ |
| 362 | void clearMediaList(); | 371 | void clearMediaList(); |
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
| @@ -17,10 +17,10 @@ public interface DeviceChannelMapper { | @@ -17,10 +17,10 @@ public interface DeviceChannelMapper { | ||
| 17 | 17 | ||
| 18 | @Insert("INSERT INTO device_channel (channelId, deviceId, name, manufacture, model, owner, civilCode, block, " + | 18 | @Insert("INSERT INTO device_channel (channelId, deviceId, name, manufacture, model, owner, civilCode, block, " + |
| 19 | "address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " + | 19 | "address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " + |
| 20 | - "ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " + | 20 | + "ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, longitudeWgs84, latitudeWgs84, createTime, updateTime) " + |
| 21 | "VALUES ('${channelId}', '${deviceId}', '${name}', '${manufacture}', '${model}', '${owner}', '${civilCode}', '${block}'," + | 21 | "VALUES ('${channelId}', '${deviceId}', '${name}', '${manufacture}', '${model}', '${owner}', '${civilCode}', '${block}'," + |
| 22 | "'${address}', ${parental}, '${parentId}', ${safetyWay}, ${registerWay}, '${certNum}', ${certifiable}, ${errCode}, '${secrecy}', " + | 22 | "'${address}', ${parental}, '${parentId}', ${safetyWay}, ${registerWay}, '${certNum}', ${certifiable}, ${errCode}, '${secrecy}', " + |
| 23 | - "'${ipAddress}', ${port}, '${password}', ${PTZType}, ${status}, '${streamId}', ${longitude}, ${latitude},'${createTime}', '${updateTime}')") | 23 | + "'${ipAddress}', ${port}, '${password}', ${PTZType}, ${status}, '${streamId}', ${longitude}, ${latitude}, ${longitudeGcj02}, ${latitudeGcj02}, ${longitudeWgs84}, ${latitudeWgs84},'${createTime}', '${updateTime}')") |
| 24 | int add(DeviceChannel channel); | 24 | int add(DeviceChannel channel); |
| 25 | 25 | ||
| 26 | @Update(value = {" <script>" + | 26 | @Update(value = {" <script>" + |
| @@ -50,6 +50,10 @@ public interface DeviceChannelMapper { | @@ -50,6 +50,10 @@ public interface DeviceChannelMapper { | ||
| 50 | "<if test='hasAudio != null'>, hasAudio=${hasAudio}</if>" + | 50 | "<if test='hasAudio != null'>, hasAudio=${hasAudio}</if>" + |
| 51 | "<if test='longitude != null'>, longitude=${longitude}</if>" + | 51 | "<if test='longitude != null'>, longitude=${longitude}</if>" + |
| 52 | "<if test='latitude != null'>, latitude=${latitude}</if>" + | 52 | "<if test='latitude != null'>, latitude=${latitude}</if>" + |
| 53 | + "<if test='longitudeGcj02 != null'>, longitudeGcj02=${longitudeGcj02}</if>" + | ||
| 54 | + "<if test='latitudeGcj02 != null'>, latitudeGcj02=${latitudeGcj02}</if>" + | ||
| 55 | + "<if test='longitudeWgs84 != null'>, longitudeWgs84=${longitudeWgs84}</if>" + | ||
| 56 | + "<if test='latitudeWgs84 != null'>, latitudeWgs84=${latitudeWgs84}</if>" + | ||
| 53 | "WHERE deviceId='${deviceId}' AND channelId='${channelId}'"+ | 57 | "WHERE deviceId='${deviceId}' AND channelId='${channelId}'"+ |
| 54 | " </script>"}) | 58 | " </script>"}) |
| 55 | int update(DeviceChannel channel); | 59 | int update(DeviceChannel channel); |
| @@ -67,7 +71,7 @@ public interface DeviceChannelMapper { | @@ -67,7 +71,7 @@ public interface DeviceChannelMapper { | ||
| 67 | " <if test='online == false' > AND dc.status=0</if>" + | 71 | " <if test='online == false' > AND dc.status=0</if>" + |
| 68 | " <if test='hasSubChannel == true' > AND dc.subCount > 0 </if>" + | 72 | " <if test='hasSubChannel == true' > AND dc.subCount > 0 </if>" + |
| 69 | " <if test='hasSubChannel == false' > AND dc.subCount = 0 </if>" + | 73 | " <if test='hasSubChannel == false' > AND dc.subCount = 0 </if>" + |
| 70 | - "GROUP BY dc.channelId " + | 74 | + "ORDER BY dc.channelId " + |
| 71 | " </script>"}) | 75 | " </script>"}) |
| 72 | List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online); | 76 | List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online); |
| 73 | 77 | ||
| @@ -138,7 +142,8 @@ public interface DeviceChannelMapper { | @@ -138,7 +142,8 @@ public interface DeviceChannelMapper { | ||
| 138 | "insert into device_channel " + | 142 | "insert into device_channel " + |
| 139 | "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " + | 143 | "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " + |
| 140 | " address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " + | 144 | " address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " + |
| 141 | - " ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " + | 145 | + " ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, " + |
| 146 | + " longitudeWgs84, latitudeWgs84, createTime, updateTime) " + | ||
| 142 | "values " + | 147 | "values " + |
| 143 | "<foreach collection='addChannels' index='index' item='item' separator=','> " + | 148 | "<foreach collection='addChannels' index='index' item='item' separator=','> " + |
| 144 | "('${item.channelId}', '${item.deviceId}', '${item.name}', '${item.manufacture}', '${item.model}', " + | 149 | "('${item.channelId}', '${item.deviceId}', '${item.name}', '${item.manufacture}', '${item.model}', " + |
| @@ -146,7 +151,8 @@ public interface DeviceChannelMapper { | @@ -146,7 +151,8 @@ public interface DeviceChannelMapper { | ||
| 146 | "'${item.address}', ${item.parental}, '${item.parentId}', ${item.safetyWay}, ${item.registerWay}, " + | 151 | "'${item.address}', ${item.parental}, '${item.parentId}', ${item.safetyWay}, ${item.registerWay}, " + |
| 147 | "'${item.certNum}', ${item.certifiable}, ${item.errCode}, '${item.secrecy}', " + | 152 | "'${item.certNum}', ${item.certifiable}, ${item.errCode}, '${item.secrecy}', " + |
| 148 | "'${item.ipAddress}', ${item.port}, '${item.password}', ${item.PTZType}, ${item.status}, " + | 153 | "'${item.ipAddress}', ${item.port}, '${item.password}', ${item.PTZType}, ${item.status}, " + |
| 149 | - "'${item.streamId}', ${item.longitude}, ${item.latitude},'${item.createTime}', '${item.updateTime}')" + | 154 | + "'${item.streamId}', ${item.longitude}, ${item.latitude},${item.longitudeGcj02}, " + |
| 155 | + "${item.latitudeGcj02},${item.longitudeWgs84}, ${item.latitudeWgs84},'${item.createTime}', '${item.updateTime}')" + | ||
| 150 | "</foreach> " + | 156 | "</foreach> " + |
| 151 | "ON DUPLICATE KEY UPDATE " + | 157 | "ON DUPLICATE KEY UPDATE " + |
| 152 | "updateTime=VALUES(updateTime), " + | 158 | "updateTime=VALUES(updateTime), " + |
| @@ -173,7 +179,11 @@ public interface DeviceChannelMapper { | @@ -173,7 +179,11 @@ public interface DeviceChannelMapper { | ||
| 173 | "status=VALUES(status), " + | 179 | "status=VALUES(status), " + |
| 174 | "streamId=VALUES(streamId), " + | 180 | "streamId=VALUES(streamId), " + |
| 175 | "longitude=VALUES(longitude), " + | 181 | "longitude=VALUES(longitude), " + |
| 176 | - "latitude=VALUES(latitude)" + | 182 | + "latitude=VALUES(latitude), " + |
| 183 | + "longitudeGcj02=VALUES(longitudeGcj02), " + | ||
| 184 | + "latitudeGcj02=VALUES(latitudeGcj02), " + | ||
| 185 | + "longitudeWgs84=VALUES(longitudeWgs84), " + | ||
| 186 | + "latitudeWgs84=VALUES(latitudeWgs84) " + | ||
| 177 | "</script>") | 187 | "</script>") |
| 178 | int batchAdd(List<DeviceChannel> addChannels); | 188 | int batchAdd(List<DeviceChannel> addChannels); |
| 179 | 189 | ||
| @@ -207,7 +217,11 @@ public interface DeviceChannelMapper { | @@ -207,7 +217,11 @@ public interface DeviceChannelMapper { | ||
| 207 | "<if test='item.hasAudio != null'>, hasAudio=${item.hasAudio}</if>" + | 217 | "<if test='item.hasAudio != null'>, hasAudio=${item.hasAudio}</if>" + |
| 208 | "<if test='item.longitude != null'>, longitude=${item.longitude}</if>" + | 218 | "<if test='item.longitude != null'>, longitude=${item.longitude}</if>" + |
| 209 | "<if test='item.latitude != null'>, latitude=${item.latitude}</if>" + | 219 | "<if test='item.latitude != null'>, latitude=${item.latitude}</if>" + |
| 210 | - "WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}"+ | 220 | + "<if test='item.longitudeGcj02 != null'>, longitudeGcj02=${item.longitudeGcj02}</if>" + |
| 221 | + "<if test='item.latitudeGcj02 != null'>, latitudeGcj02=${item.latitudeGcj02}</if>" + | ||
| 222 | + "<if test='item.longitudeWgs84 != null'>, longitudeWgs84=${item.longitudeWgs84}</if>" + | ||
| 223 | + "<if test='item.latitudeWgs84 != null'>, latitudeWgs84=${item.latitudeWgs84}</if>" + | ||
| 224 | + "WHERE deviceId='${item.deviceId}' AND channelId='${item.channelId}'"+ | ||
| 211 | "</foreach>" + | 225 | "</foreach>" + |
| 212 | "</script>"}) | 226 | "</script>"}) |
| 213 | int batchUpdate(List<DeviceChannel> updateChannels); | 227 | int batchUpdate(List<DeviceChannel> updateChannels); |
| @@ -261,4 +275,6 @@ public interface DeviceChannelMapper { | @@ -261,4 +275,6 @@ public interface DeviceChannelMapper { | ||
| 261 | @Select("SELECT * FROM device_channel WHERE length(trim(streamId)) > 0") | 275 | @Select("SELECT * FROM device_channel WHERE length(trim(streamId)) > 0") |
| 262 | List<DeviceChannel> getAllChannelInPlay(); | 276 | List<DeviceChannel> getAllChannelInPlay(); |
| 263 | 277 | ||
| 278 | + @Select("select * from device_channel where longitude*latitude > 0 and deviceId = #{deviceId}") | ||
| 279 | + List<DeviceChannel> getAllChannelWithCoordinate(String deviceId); | ||
| 264 | } | 280 | } |
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
| @@ -38,6 +38,7 @@ public interface DeviceMapper { | @@ -38,6 +38,7 @@ public interface DeviceMapper { | ||
| 38 | "mobilePositionSubmissionInterval," + | 38 | "mobilePositionSubmissionInterval," + |
| 39 | "subscribeCycleForAlarm," + | 39 | "subscribeCycleForAlarm," + |
| 40 | "ssrcCheck," + | 40 | "ssrcCheck," + |
| 41 | + "geoCoordSys," + | ||
| 41 | "online" + | 42 | "online" + |
| 42 | ") VALUES (" + | 43 | ") VALUES (" + |
| 43 | "#{deviceId}," + | 44 | "#{deviceId}," + |
| @@ -61,6 +62,7 @@ public interface DeviceMapper { | @@ -61,6 +62,7 @@ public interface DeviceMapper { | ||
| 61 | "#{mobilePositionSubmissionInterval}," + | 62 | "#{mobilePositionSubmissionInterval}," + |
| 62 | "#{subscribeCycleForAlarm}," + | 63 | "#{subscribeCycleForAlarm}," + |
| 63 | "#{ssrcCheck}," + | 64 | "#{ssrcCheck}," + |
| 65 | + "#{geoCoordSys}," + | ||
| 64 | "#{online}" + | 66 | "#{online}" + |
| 65 | ")") | 67 | ")") |
| 66 | int add(Device device); | 68 | int add(Device device); |
| @@ -87,6 +89,7 @@ public interface DeviceMapper { | @@ -87,6 +89,7 @@ public interface DeviceMapper { | ||
| 87 | "<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=${mobilePositionSubmissionInterval}</if>" + | 89 | "<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=${mobilePositionSubmissionInterval}</if>" + |
| 88 | "<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=${subscribeCycleForAlarm}</if>" + | 90 | "<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=${subscribeCycleForAlarm}</if>" + |
| 89 | "<if test=\"ssrcCheck != null\">, ssrcCheck=${ssrcCheck}</if>" + | 91 | "<if test=\"ssrcCheck != null\">, ssrcCheck=${ssrcCheck}</if>" + |
| 92 | + "<if test=\"geoCoordSys != null\">, geoCoordSys=#{geoCoordSys}</if>" + | ||
| 90 | "WHERE deviceId='${deviceId}'"+ | 93 | "WHERE deviceId='${deviceId}'"+ |
| 91 | " </script>"}) | 94 | " </script>"}) |
| 92 | int update(Device device); | 95 | int update(Device device); |
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java
| @@ -14,9 +14,9 @@ import java.util.List; | @@ -14,9 +14,9 @@ import java.util.List; | ||
| 14 | public interface StreamPushMapper { | 14 | public interface StreamPushMapper { |
| 15 | 15 | ||
| 16 | @Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " + | 16 | @Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " + |
| 17 | - "createStamp, aliveSecond, mediaServerId) VALUES" + | 17 | + "createStamp, aliveSecond, mediaServerId, serverId) VALUES" + |
| 18 | "('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " + | 18 | "('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " + |
| 19 | - "'${createStamp}', '${aliveSecond}', '${mediaServerId}' )") | 19 | + "'${createStamp}', '${aliveSecond}', '${mediaServerId}' , '${serverId}' )") |
| 20 | int add(StreamPushItem streamPushItem); | 20 | int add(StreamPushItem streamPushItem); |
| 21 | 21 | ||
| 22 | @Update("UPDATE stream_push " + | 22 | @Update("UPDATE stream_push " + |
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
| @@ -587,11 +587,11 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { | @@ -587,11 +587,11 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { | ||
| 587 | String scanKey = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_*"; | 587 | String scanKey = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_*"; |
| 588 | List<GPSMsgInfo> result = new ArrayList<>(); | 588 | List<GPSMsgInfo> result = new ArrayList<>(); |
| 589 | List<Object> keys = redis.scan(scanKey); | 589 | List<Object> keys = redis.scan(scanKey); |
| 590 | - for (int i = 0; i < keys.size(); i++) { | ||
| 591 | - String key = (String) keys.get(i); | 590 | + for (Object o : keys) { |
| 591 | + String key = (String) o; | ||
| 592 | GPSMsgInfo gpsMsgInfo = (GPSMsgInfo) redis.get(key); | 592 | GPSMsgInfo gpsMsgInfo = (GPSMsgInfo) redis.get(key); |
| 593 | if (!gpsMsgInfo.isStored()) { // 只取没有存过得 | 593 | if (!gpsMsgInfo.isStored()) { // 只取没有存过得 |
| 594 | - result.add((GPSMsgInfo)redis.get(key)); | 594 | + result.add((GPSMsgInfo) redis.get(key)); |
| 595 | } | 595 | } |
| 596 | } | 596 | } |
| 597 | 597 | ||
| @@ -667,7 +667,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { | @@ -667,7 +667,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { | ||
| 667 | @Override | 667 | @Override |
| 668 | public void sendStreamPushRequestedMsg(MessageForPushChannel msg) { | 668 | public void sendStreamPushRequestedMsg(MessageForPushChannel msg) { |
| 669 | String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED; | 669 | String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED; |
| 670 | - logger.info("[redis 推流被请求通知] {}: {}-{}", key, msg.getApp(), msg.getStream()); | 670 | + logger.info("[redis 推流被请求通知] {}: {}/{}", key, msg.getApp(), msg.getStream()); |
| 671 | redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg)); | 671 | redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg)); |
| 672 | } | 672 | } |
| 673 | 673 |
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
| @@ -25,12 +25,13 @@ import org.springframework.stereotype.Component; | @@ -25,12 +25,13 @@ import org.springframework.stereotype.Component; | ||
| 25 | import org.springframework.transaction.TransactionDefinition; | 25 | import org.springframework.transaction.TransactionDefinition; |
| 26 | import org.springframework.transaction.TransactionStatus; | 26 | import org.springframework.transaction.TransactionStatus; |
| 27 | import org.springframework.transaction.annotation.Transactional; | 27 | import org.springframework.transaction.annotation.Transactional; |
| 28 | +import org.springframework.util.CollectionUtils; | ||
| 28 | import org.springframework.util.StringUtils; | 29 | import org.springframework.util.StringUtils; |
| 29 | 30 | ||
| 30 | import java.util.*; | 31 | import java.util.*; |
| 31 | import java.util.concurrent.ConcurrentHashMap; | 32 | import java.util.concurrent.ConcurrentHashMap; |
| 32 | 33 | ||
| 33 | -/** | 34 | +/** |
| 34 | * 视频设备数据存储-jdbc实现 | 35 | * 视频设备数据存储-jdbc实现 |
| 35 | * swwheihei | 36 | * swwheihei |
| 36 | * 2020年5月6日 下午2:31:42 | 37 | * 2020年5月6日 下午2:31:42 |
| @@ -195,7 +196,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { | @@ -195,7 +196,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { | ||
| 195 | 196 | ||
| 196 | @Override | 197 | @Override |
| 197 | public boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList) { | 198 | public boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList) { |
| 198 | - if (deviceChannelList == null) { | 199 | + if (CollectionUtils.isEmpty(deviceChannelList)) { |
| 199 | return false; | 200 | return false; |
| 200 | } | 201 | } |
| 201 | List<DeviceChannel> allChannelInPlay = deviceChannelMapper.getAllChannelInPlay(); | 202 | List<DeviceChannel> allChannelInPlay = deviceChannelMapper.getAllChannelInPlay(); |
| @@ -246,6 +247,10 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { | @@ -246,6 +247,10 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { | ||
| 246 | if (stringBuilder.length() > 0) { | 247 | if (stringBuilder.length() > 0) { |
| 247 | logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder); | 248 | logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder); |
| 248 | } | 249 | } |
| 250 | + if(CollectionUtils.isEmpty(channels)){ | ||
| 251 | + logger.info("通道重设,数据为空={}" , deviceChannelList); | ||
| 252 | + return false; | ||
| 253 | + } | ||
| 249 | try { | 254 | try { |
| 250 | int cleanChannelsResult = deviceChannelMapper.cleanChannelsNotInList(deviceId, channels); | 255 | int cleanChannelsResult = deviceChannelMapper.cleanChannelsNotInList(deviceId, channels); |
| 251 | int limitCount = 300; | 256 | int limitCount = 300; |
| @@ -315,6 +320,9 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { | @@ -315,6 +320,9 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { | ||
| 315 | List<DeviceChannel> all; | 320 | List<DeviceChannel> all; |
| 316 | if (catalogUnderDevice != null && catalogUnderDevice) { | 321 | if (catalogUnderDevice != null && catalogUnderDevice) { |
| 317 | all = deviceChannelMapper.queryChannels(deviceId, deviceId, query, hasSubChannel, online); | 322 | all = deviceChannelMapper.queryChannels(deviceId, deviceId, query, hasSubChannel, online); |
| 323 | + // 海康设备的parentId是SIP id | ||
| 324 | + List<DeviceChannel> deviceChannels = deviceChannelMapper.queryChannels(deviceId, sipConfig.getId(), query, hasSubChannel, online); | ||
| 325 | + all.addAll(deviceChannels); | ||
| 318 | }else { | 326 | }else { |
| 319 | all = deviceChannelMapper.queryChannels(deviceId, null, query, hasSubChannel, online); | 327 | all = deviceChannelMapper.queryChannels(deviceId, null, query, hasSubChannel, online); |
| 320 | } | 328 | } |
| @@ -877,6 +885,11 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { | @@ -877,6 +885,11 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { | ||
| 877 | } | 885 | } |
| 878 | 886 | ||
| 879 | @Override | 887 | @Override |
| 888 | + public StreamPushItem getMedia(String app, String stream) { | ||
| 889 | + return streamPushMapper.selectOne(app, stream); | ||
| 890 | + } | ||
| 891 | + | ||
| 892 | + @Override | ||
| 880 | public void clearMediaList() { | 893 | public void clearMediaList() { |
| 881 | streamPushMapper.clear(); | 894 | streamPushMapper.clear(); |
| 882 | } | 895 | } |
src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java
| 1 | package com.genersoft.iot.vmp.utils; | 1 | package com.genersoft.iot.vmp.utils; |
| 2 | 2 | ||
| 3 | 3 | ||
| 4 | -import java.text.SimpleDateFormat; | ||
| 5 | import java.time.Instant; | 4 | import java.time.Instant; |
| 6 | import java.time.LocalDate; | 5 | import java.time.LocalDate; |
| 7 | import java.time.LocalDateTime; | 6 | import java.time.LocalDateTime; |
| @@ -18,35 +17,63 @@ import java.util.Locale; | @@ -18,35 +17,63 @@ import java.util.Locale; | ||
| 18 | */ | 17 | */ |
| 19 | public class DateUtil { | 18 | public class DateUtil { |
| 20 | 19 | ||
| 21 | - private static final String yyyy_MM_dd_T_HH_mm_ss_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss"; | ||
| 22 | - public static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss"; | 20 | + /** |
| 21 | + * 兼容不规范的iso8601时间格式 | ||
| 22 | + */ | ||
| 23 | + private static final String ISO8601_COMPATIBLE_PATTERN = "yyyy-M-d'T'H:m:s"; | ||
| 23 | 24 | ||
| 24 | - public static final SimpleDateFormat formatISO8601 = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()); | ||
| 25 | - public static final SimpleDateFormat format = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()); | 25 | + /** |
| 26 | + * 用以输出标准的iso8601时间格式 | ||
| 27 | + */ | ||
| 28 | + private static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"; | ||
| 26 | 29 | ||
| 27 | - public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()).withZone(ZoneId.systemDefault()); | ||
| 28 | - public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()).withZone(ZoneId.systemDefault()); | 30 | + /** |
| 31 | + * wvp内部统一时间格式 | ||
| 32 | + */ | ||
| 33 | + public static final String PATTERN = "yyyy-MM-dd HH:mm:ss"; | ||
| 34 | + | ||
| 35 | + public static final String zoneStr = "Asia/Shanghai"; | ||
| 36 | + | ||
| 37 | + public static final DateTimeFormatter formatterCompatibleISO8601 = DateTimeFormatter.ofPattern(ISO8601_COMPATIBLE_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); | ||
| 38 | + public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(ISO8601_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); | ||
| 39 | + public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); | ||
| 29 | 40 | ||
| 30 | public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) { | 41 | public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) { |
| 42 | + | ||
| 31 | return formatterISO8601.format(formatter.parse(formatTime)); | 43 | return formatterISO8601.format(formatter.parse(formatTime)); |
| 32 | } | 44 | } |
| 33 | 45 | ||
| 34 | public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) { | 46 | public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) { |
| 35 | - return formatter.format(formatterISO8601.parse(formatTime)); | 47 | + return formatter.format(formatterCompatibleISO8601.parse(formatTime)); |
| 36 | 48 | ||
| 37 | } | 49 | } |
| 38 | - | 50 | + |
| 51 | + /** | ||
| 52 | + * yyyy_MM_dd_HH_mm_ss 转时间戳 | ||
| 53 | + * @param formatTime | ||
| 54 | + * @return | ||
| 55 | + */ | ||
| 39 | public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) { | 56 | public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) { |
| 40 | TemporalAccessor temporalAccessor = formatter.parse(formatTime); | 57 | TemporalAccessor temporalAccessor = formatter.parse(formatTime); |
| 41 | Instant instant = Instant.from(temporalAccessor); | 58 | Instant instant = Instant.from(temporalAccessor); |
| 42 | return instant.getEpochSecond(); | 59 | return instant.getEpochSecond(); |
| 43 | } | 60 | } |
| 44 | 61 | ||
| 62 | + /** | ||
| 63 | + * 获取当前时间 | ||
| 64 | + * @return | ||
| 65 | + */ | ||
| 45 | public static String getNow() { | 66 | public static String getNow() { |
| 46 | LocalDateTime nowDateTime = LocalDateTime.now(); | 67 | LocalDateTime nowDateTime = LocalDateTime.now(); |
| 47 | return formatter.format(nowDateTime); | 68 | return formatter.format(nowDateTime); |
| 48 | } | 69 | } |
| 49 | 70 | ||
| 71 | + /** | ||
| 72 | + * 格式校验 | ||
| 73 | + * @param timeStr 时间字符串 | ||
| 74 | + * @param dateTimeFormatter 待校验的格式 | ||
| 75 | + * @return | ||
| 76 | + */ | ||
| 50 | public static boolean verification(String timeStr, DateTimeFormatter dateTimeFormatter) { | 77 | public static boolean verification(String timeStr, DateTimeFormatter dateTimeFormatter) { |
| 51 | try { | 78 | try { |
| 52 | LocalDate.parse(timeStr, dateTimeFormatter); | 79 | LocalDate.parse(timeStr, dateTimeFormatter); |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java
| @@ -24,6 +24,7 @@ import org.springframework.util.StringUtils; | @@ -24,6 +24,7 @@ import org.springframework.util.StringUtils; | ||
| 24 | import org.springframework.web.bind.annotation.*; | 24 | import org.springframework.web.bind.annotation.*; |
| 25 | 25 | ||
| 26 | import java.text.ParseException; | 26 | import java.text.ParseException; |
| 27 | +import java.time.LocalDateTime; | ||
| 27 | import java.util.Arrays; | 28 | import java.util.Arrays; |
| 28 | import java.util.List; | 29 | import java.util.List; |
| 29 | 30 | ||
| @@ -68,8 +69,8 @@ public class AlarmController { | @@ -68,8 +69,8 @@ public class AlarmController { | ||
| 68 | @ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class), | 69 | @ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class), |
| 69 | @ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class), | 70 | @ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class), |
| 70 | @ApiImplicitParam(name="alarmType", value = "查询内容" ,dataTypeClass = String.class), | 71 | @ApiImplicitParam(name="alarmType", value = "查询内容" ,dataTypeClass = String.class), |
| 71 | - @ApiImplicitParam(name="startTime", value = "查询内容" ,dataTypeClass = String.class), | ||
| 72 | - @ApiImplicitParam(name="endTime", value = "查询内容" ,dataTypeClass = String.class), | 72 | + @ApiImplicitParam(name="startTime", value = "开始时间" ,dataTypeClass = String.class), |
| 73 | + @ApiImplicitParam(name="endTime", value = "结束时间" ,dataTypeClass = String.class), | ||
| 73 | }) | 74 | }) |
| 74 | public ResponseEntity<PageInfo<DeviceAlarm>> getAll( | 75 | public ResponseEntity<PageInfo<DeviceAlarm>> getAll( |
| 75 | @RequestParam int page, | 76 | @RequestParam int page, |
| @@ -98,14 +99,7 @@ public class AlarmController { | @@ -98,14 +99,7 @@ public class AlarmController { | ||
| 98 | } | 99 | } |
| 99 | 100 | ||
| 100 | 101 | ||
| 101 | - try { | ||
| 102 | - if (startTime != null) { | ||
| 103 | - DateUtil.format.parse(startTime); | ||
| 104 | - } | ||
| 105 | - if (endTime != null) { | ||
| 106 | - DateUtil.format.parse(endTime); | ||
| 107 | - } | ||
| 108 | - } catch (ParseException e) { | 102 | + if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){ |
| 109 | return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); | 103 | return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); |
| 110 | } | 104 | } |
| 111 | 105 | ||
| @@ -144,11 +138,7 @@ public class AlarmController { | @@ -144,11 +138,7 @@ public class AlarmController { | ||
| 144 | if (StringUtils.isEmpty(time)) { | 138 | if (StringUtils.isEmpty(time)) { |
| 145 | time = null; | 139 | time = null; |
| 146 | } | 140 | } |
| 147 | - try { | ||
| 148 | - if (time != null) { | ||
| 149 | - DateUtil.format.parse(time); | ||
| 150 | - } | ||
| 151 | - } catch (ParseException e) { | 141 | + if (!DateUtil.verification(time, DateUtil.formatter) ){ |
| 152 | return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); | 142 | return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); |
| 153 | } | 143 | } |
| 154 | List<String> deviceIdList = null; | 144 | List<String> deviceIdList = null; |
| @@ -189,7 +179,7 @@ public class AlarmController { | @@ -189,7 +179,7 @@ public class AlarmController { | ||
| 189 | deviceAlarm.setAlarmDescription("test"); | 179 | deviceAlarm.setAlarmDescription("test"); |
| 190 | deviceAlarm.setAlarmMethod("1"); | 180 | deviceAlarm.setAlarmMethod("1"); |
| 191 | deviceAlarm.setAlarmPriority("1"); | 181 | deviceAlarm.setAlarmPriority("1"); |
| 192 | - deviceAlarm.setAlarmTime(DateUtil.formatISO8601.format(System.currentTimeMillis())); | 182 | + deviceAlarm.setAlarmTime(DateUtil.formatterISO8601.format(LocalDateTime.now())); |
| 193 | deviceAlarm.setAlarmType("1"); | 183 | deviceAlarm.setAlarmType("1"); |
| 194 | deviceAlarm.setLongitude(115.33333); | 184 | deviceAlarm.setLongitude(115.33333); |
| 195 | deviceAlarm.setLatitude(39.33333); | 185 | deviceAlarm.setLatitude(39.33333); |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
| @@ -21,16 +21,22 @@ import io.swagger.annotations.Api; | @@ -21,16 +21,22 @@ import io.swagger.annotations.Api; | ||
| 21 | import io.swagger.annotations.ApiImplicitParam; | 21 | import io.swagger.annotations.ApiImplicitParam; |
| 22 | import io.swagger.annotations.ApiImplicitParams; | 22 | import io.swagger.annotations.ApiImplicitParams; |
| 23 | import io.swagger.annotations.ApiOperation; | 23 | import io.swagger.annotations.ApiOperation; |
| 24 | +import org.apache.commons.compress.utils.IOUtils; | ||
| 25 | +import org.apache.http.HttpResponse; | ||
| 24 | import org.slf4j.Logger; | 26 | import org.slf4j.Logger; |
| 25 | import org.slf4j.LoggerFactory; | 27 | import org.slf4j.LoggerFactory; |
| 26 | import org.springframework.beans.factory.annotation.Autowired; | 28 | import org.springframework.beans.factory.annotation.Autowired; |
| 27 | import org.springframework.http.HttpStatus; | 29 | import org.springframework.http.HttpStatus; |
| 30 | +import org.springframework.http.MediaType; | ||
| 28 | import org.springframework.http.ResponseEntity; | 31 | import org.springframework.http.ResponseEntity; |
| 29 | import org.springframework.util.StringUtils; | 32 | import org.springframework.util.StringUtils; |
| 30 | import org.springframework.web.bind.annotation.*; | 33 | import org.springframework.web.bind.annotation.*; |
| 31 | import org.springframework.web.context.request.async.DeferredResult; | 34 | import org.springframework.web.context.request.async.DeferredResult; |
| 32 | 35 | ||
| 36 | +import javax.servlet.http.HttpServletResponse; | ||
| 33 | import javax.sip.DialogState; | 37 | import javax.sip.DialogState; |
| 38 | +import java.io.*; | ||
| 39 | +import java.nio.file.Files; | ||
| 34 | import java.util.*; | 40 | import java.util.*; |
| 35 | 41 | ||
| 36 | @Api(tags = "国标设备查询", value = "国标设备查询") | 42 | @Api(tags = "国标设备查询", value = "国标设备查询") |
| @@ -200,6 +206,11 @@ public class DeviceQuery { | @@ -200,6 +206,11 @@ public class DeviceQuery { | ||
| 200 | Set<String> allKeys = dynamicTask.getAllKeys(); | 206 | Set<String> allKeys = dynamicTask.getAllKeys(); |
| 201 | for (String key : allKeys) { | 207 | for (String key : allKeys) { |
| 202 | if (key.startsWith(deviceId)) { | 208 | if (key.startsWith(deviceId)) { |
| 209 | + Runnable runnable = dynamicTask.get(key); | ||
| 210 | + if (runnable instanceof ISubscribeTask) { | ||
| 211 | + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; | ||
| 212 | + subscribeTask.stop(); | ||
| 213 | + } | ||
| 203 | dynamicTask.stop(key); | 214 | dynamicTask.stop(key); |
| 204 | } | 215 | } |
| 205 | } | 216 | } |
| @@ -306,12 +317,7 @@ public class DeviceQuery { | @@ -306,12 +317,7 @@ public class DeviceQuery { | ||
| 306 | public ResponseEntity<WVPResult<String>> updateDevice(Device device){ | 317 | public ResponseEntity<WVPResult<String>> updateDevice(Device device){ |
| 307 | 318 | ||
| 308 | if (device != null && device.getDeviceId() != null) { | 319 | if (device != null && device.getDeviceId() != null) { |
| 309 | - | ||
| 310 | - | ||
| 311 | - // TODO 报警订阅相关的信息 | ||
| 312 | - | ||
| 313 | deviceService.updateDevice(device); | 320 | deviceService.updateDevice(device); |
| 314 | -// cmder.deviceInfoQuery(device); | ||
| 315 | } | 321 | } |
| 316 | WVPResult<String> result = new WVPResult<>(); | 322 | WVPResult<String> result = new WVPResult<>(); |
| 317 | result.setCode(0); | 323 | result.setCode(0); |
| @@ -336,6 +342,11 @@ public class DeviceQuery { | @@ -336,6 +342,11 @@ public class DeviceQuery { | ||
| 336 | Device device = storager.queryVideoDevice(deviceId); | 342 | Device device = storager.queryVideoDevice(deviceId); |
| 337 | String uuid = UUID.randomUUID().toString(); | 343 | String uuid = UUID.randomUUID().toString(); |
| 338 | String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId; | 344 | String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId; |
| 345 | + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L); | ||
| 346 | + if(device == null) { | ||
| 347 | + result.setResult(new ResponseEntity(String.format("设备%s不存在", deviceId),HttpStatus.OK)); | ||
| 348 | + return result; | ||
| 349 | + } | ||
| 339 | cmder.deviceStatusQuery(device, event -> { | 350 | cmder.deviceStatusQuery(device, event -> { |
| 340 | RequestMessage msg = new RequestMessage(); | 351 | RequestMessage msg = new RequestMessage(); |
| 341 | msg.setId(uuid); | 352 | msg.setId(uuid); |
| @@ -343,7 +354,6 @@ public class DeviceQuery { | @@ -343,7 +354,6 @@ public class DeviceQuery { | ||
| 343 | msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg)); | 354 | msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg)); |
| 344 | resultHolder.invokeResult(msg); | 355 | resultHolder.invokeResult(msg); |
| 345 | }); | 356 | }); |
| 346 | - DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L); | ||
| 347 | result.onTimeout(()->{ | 357 | result.onTimeout(()->{ |
| 348 | logger.warn(String.format("获取设备状态超时")); | 358 | logger.warn(String.format("获取设备状态超时")); |
| 349 | // 释放rtpserver | 359 | // 释放rtpserver |
| @@ -456,4 +466,17 @@ public class DeviceQuery { | @@ -456,4 +466,17 @@ public class DeviceQuery { | ||
| 456 | wvpResult.setData(dialogStateMap); | 466 | wvpResult.setData(dialogStateMap); |
| 457 | return wvpResult; | 467 | return wvpResult; |
| 458 | } | 468 | } |
| 469 | + | ||
| 470 | + @GetMapping("/snap/{deviceId}/{channelId}") | ||
| 471 | + @ApiOperation(value = "请求截图", notes = "请求截图") | ||
| 472 | + public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId) { | ||
| 473 | + | ||
| 474 | + try { | ||
| 475 | + final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + ".jpg").toPath()); | ||
| 476 | + resp.setContentType(MediaType.IMAGE_PNG_VALUE); | ||
| 477 | + IOUtils.copy(in, resp.getOutputStream()); | ||
| 478 | + } catch (IOException e) { | ||
| 479 | + resp.setStatus(HttpServletResponse.SC_NOT_FOUND); | ||
| 480 | + } | ||
| 481 | + } | ||
| 459 | } | 482 | } |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
| @@ -72,7 +72,7 @@ public class GBRecordController { | @@ -72,7 +72,7 @@ public class GBRecordController { | ||
| 72 | if (!DateUtil.verification(startTime, DateUtil.formatter)){ | 72 | if (!DateUtil.verification(startTime, DateUtil.formatter)){ |
| 73 | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); | 73 | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| 74 | wvpResult.setCode(-1); | 74 | wvpResult.setCode(-1); |
| 75 | - wvpResult.setMsg("startTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss); | 75 | + wvpResult.setMsg("startTime error, format is " + DateUtil.PATTERN); |
| 76 | 76 | ||
| 77 | ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK); | 77 | ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK); |
| 78 | result.setResult(resultResponseEntity); | 78 | result.setResult(resultResponseEntity); |
| @@ -81,7 +81,7 @@ public class GBRecordController { | @@ -81,7 +81,7 @@ public class GBRecordController { | ||
| 81 | if (!DateUtil.verification(endTime, DateUtil.formatter)){ | 81 | if (!DateUtil.verification(endTime, DateUtil.formatter)){ |
| 82 | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); | 82 | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| 83 | wvpResult.setCode(-1); | 83 | wvpResult.setCode(-1); |
| 84 | - wvpResult.setMsg("endTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss); | 84 | + wvpResult.setMsg("endTime error, format is " + DateUtil.PATTERN); |
| 85 | ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK); | 85 | ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK); |
| 86 | result.setResult(resultResponseEntity); | 86 | result.setResult(resultResponseEntity); |
| 87 | return result; | 87 | return result; |
src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java
| @@ -76,14 +76,7 @@ public class LogController { | @@ -76,14 +76,7 @@ public class LogController { | ||
| 76 | logger.warn("自动记录日志功能已关闭,查询结果可能不完整。"); | 76 | logger.warn("自动记录日志功能已关闭,查询结果可能不完整。"); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | - try { | ||
| 80 | - if (startTime != null) { | ||
| 81 | - DateUtil.format.parse(startTime); | ||
| 82 | - } | ||
| 83 | - if (endTime != null) { | ||
| 84 | - DateUtil.format.parse(endTime); | ||
| 85 | - } | ||
| 86 | - } catch (ParseException e) { | 79 | + if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){ |
| 87 | return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); | 80 | return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); |
| 88 | } | 81 | } |
| 89 | 82 |
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java
| @@ -146,8 +146,8 @@ public class ApiDeviceController { | @@ -146,8 +146,8 @@ public class ApiDeviceController { | ||
| 146 | // 2-基于口令的双向认证, | 146 | // 2-基于口令的双向认证, |
| 147 | // 3-基于数字证书的双向认证 | 147 | // 3-基于数字证书的双向认证 |
| 148 | deviceJOSNChannel.put("Status", deviceChannel.getStatus()); | 148 | deviceJOSNChannel.put("Status", deviceChannel.getStatus()); |
| 149 | - deviceJOSNChannel.put("Longitude", deviceChannel.getLongitude()); | ||
| 150 | - deviceJOSNChannel.put("Latitude", deviceChannel.getLatitude()); | 149 | + deviceJOSNChannel.put("Longitude", deviceChannel.getLongitudeWgs84()); |
| 150 | + deviceJOSNChannel.put("Latitude", deviceChannel.getLatitudeWgs84()); | ||
| 151 | deviceJOSNChannel.put("PTZType ", deviceChannel.getPTZType()); // 云台类型, 0 - 未知, 1 - 球机, 2 - 半球, | 151 | deviceJOSNChannel.put("PTZType ", deviceChannel.getPTZType()); // 云台类型, 0 - 未知, 1 - 球机, 2 - 半球, |
| 152 | // 3 - 固定枪机, 4 - 遥控枪机 | 152 | // 3 - 固定枪机, 4 - 遥控枪机 |
| 153 | deviceJOSNChannel.put("CustomPTZType", ""); | 153 | deviceJOSNChannel.put("CustomPTZType", ""); |
src/main/resources/all-application.yml
| @@ -32,7 +32,7 @@ spring: | @@ -32,7 +32,7 @@ spring: | ||
| 32 | datasource: | 32 | datasource: |
| 33 | type: com.alibaba.druid.pool.DruidDataSource | 33 | type: com.alibaba.druid.pool.DruidDataSource |
| 34 | driver-class-name: com.mysql.cj.jdbc.Driver | 34 | driver-class-name: com.mysql.cj.jdbc.Driver |
| 35 | - url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false | 35 | + url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true |
| 36 | username: root | 36 | username: root |
| 37 | password: root123 | 37 | password: root123 |
| 38 | druid: | 38 | druid: |
src/main/resources/application-dev.yml
| @@ -20,7 +20,7 @@ spring: | @@ -20,7 +20,7 @@ spring: | ||
| 20 | datasource: | 20 | datasource: |
| 21 | type: com.alibaba.druid.pool.DruidDataSource | 21 | type: com.alibaba.druid.pool.DruidDataSource |
| 22 | driver-class-name: com.mysql.cj.jdbc.Driver | 22 | driver-class-name: com.mysql.cj.jdbc.Driver |
| 23 | - url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false | 23 | + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true |
| 24 | username: root | 24 | username: root |
| 25 | password: 123456 | 25 | password: 123456 |
| 26 | druid: | 26 | druid: |
src/main/resources/application-docker.yml
| @@ -20,7 +20,7 @@ spring: | @@ -20,7 +20,7 @@ spring: | ||
| 20 | datasource: | 20 | datasource: |
| 21 | # 使用mysql 打开23-28行注释, 删除29-36行 | 21 | # 使用mysql 打开23-28行注释, 删除29-36行 |
| 22 | name: wvp | 22 | name: wvp |
| 23 | - url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowMultiQueries=true&useSSL=false | 23 | + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowMultiQueries=true&useSSL=false&allowMultiQueries=true |
| 24 | username: root | 24 | username: root |
| 25 | password: root | 25 | password: root |
| 26 | type: com.alibaba.druid.pool.DruidDataSource | 26 | type: com.alibaba.druid.pool.DruidDataSource |
src/test/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImplTest.java
| @@ -8,6 +8,10 @@ import org.springframework.boot.test.context.SpringBootTest; | @@ -8,6 +8,10 @@ import org.springframework.boot.test.context.SpringBootTest; | ||
| 8 | import org.springframework.test.context.junit4.SpringRunner; | 8 | import org.springframework.test.context.junit4.SpringRunner; |
| 9 | 9 | ||
| 10 | import javax.annotation.Resource; | 10 | import javax.annotation.Resource; |
| 11 | +import java.time.Instant; | ||
| 12 | +import java.time.LocalDateTime; | ||
| 13 | +import java.time.ZoneOffset; | ||
| 14 | +import java.time.temporal.TemporalAccessor; | ||
| 11 | import java.util.Date; | 15 | import java.util.Date; |
| 12 | 16 | ||
| 13 | 17 | ||
| @@ -64,8 +68,8 @@ class DeviceAlarmServiceImplTest { | @@ -64,8 +68,8 @@ class DeviceAlarmServiceImplTest { | ||
| 64 | * * 7其他报警;可以为直接组合如12为电话报警或 设备报警- | 68 | * * 7其他报警;可以为直接组合如12为电话报警或 设备报警- |
| 65 | */ | 69 | */ |
| 66 | deviceAlarm.setAlarmMethod((int)(Math.random()*7 + 1) + ""); | 70 | deviceAlarm.setAlarmMethod((int)(Math.random()*7 + 1) + ""); |
| 67 | - Date date = randomDate("2021-01-01 00:00:00", "2021-06-01 00:00:00"); | ||
| 68 | - deviceAlarm.setAlarmTime(DateUtil.format.format(date)); | 71 | + Instant date = randomDate("2021-01-01 00:00:00", "2021-06-01 00:00:00"); |
| 72 | + deviceAlarm.setAlarmTime(DateUtil.formatter.format(date)); | ||
| 69 | /** | 73 | /** |
| 70 | * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情- | 74 | * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情- |
| 71 | */ | 75 | */ |
| @@ -85,17 +89,20 @@ class DeviceAlarmServiceImplTest { | @@ -85,17 +89,20 @@ class DeviceAlarmServiceImplTest { | ||
| 85 | 89 | ||
| 86 | 90 | ||
| 87 | 91 | ||
| 88 | - private Date randomDate(String beginDate, String endDate) { | 92 | + private Instant randomDate(String beginDate, String endDate) { |
| 89 | try { | 93 | try { |
| 90 | 94 | ||
| 91 | - Date start = DateUtil.format.parse(beginDate);//构造开始日期 | ||
| 92 | - Date end = DateUtil.format.parse(endDate);//构造结束日期 | 95 | + //构造开始日期 |
| 96 | + LocalDateTime start = LocalDateTime.parse(beginDate, DateUtil.formatter); | ||
| 97 | + | ||
| 98 | + //构造结束日期 | ||
| 99 | + LocalDateTime end = LocalDateTime.parse(endDate, DateUtil.formatter); | ||
| 93 | //getTime()表示返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。 | 100 | //getTime()表示返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。 |
| 94 | - if (start.getTime() >= end.getTime()) { | 101 | + if (start.isAfter(end)) { |
| 95 | return null; | 102 | return null; |
| 96 | } | 103 | } |
| 97 | - long date = random(start.getTime(), end.getTime()); | ||
| 98 | - return new Date(date); | 104 | + long date = random(start.toInstant(ZoneOffset.of("+8")).toEpochMilli(), end.toInstant(ZoneOffset.of("+8")).toEpochMilli()); |
| 105 | + return Instant.ofEpochMilli(date); | ||
| 99 | } catch (Exception e) { | 106 | } catch (Exception e) { |
| 100 | e.printStackTrace(); | 107 | e.printStackTrace(); |
| 101 | } | 108 | } |
web_src/package.json
| @@ -52,7 +52,7 @@ | @@ -52,7 +52,7 @@ | ||
| 52 | "postcss-url": "^7.2.1", | 52 | "postcss-url": "^7.2.1", |
| 53 | "rimraf": "^2.6.0", | 53 | "rimraf": "^2.6.0", |
| 54 | "semver": "^5.3.0", | 54 | "semver": "^5.3.0", |
| 55 | - "shelljs": "^0.7.6", | 55 | + "shelljs": "^0.8.5", |
| 56 | "uglifyjs-webpack-plugin": "^1.1.1", | 56 | "uglifyjs-webpack-plugin": "^1.1.1", |
| 57 | "url-loader": "^0.5.8", | 57 | "url-loader": "^0.5.8", |
| 58 | "vue-loader": "^13.3.0", | 58 | "vue-loader": "^13.3.0", |
web_src/src/App.vue
| @@ -76,7 +76,7 @@ body, | @@ -76,7 +76,7 @@ body, | ||
| 76 | line-height: 60px; | 76 | line-height: 60px; |
| 77 | } | 77 | } |
| 78 | .el-main { | 78 | .el-main { |
| 79 | - background-color: #e9eef3; | 79 | + background-color: #f0f2f5; |
| 80 | color: #333; | 80 | color: #333; |
| 81 | text-align: center; | 81 | text-align: center; |
| 82 | padding-top: 0px !important; | 82 | padding-top: 0px !important; |
| @@ -101,4 +101,8 @@ body, | @@ -101,4 +101,8 @@ body, | ||
| 101 | box-shadow: inset 0 0 6px rgba(0, 0, 0, .1); | 101 | box-shadow: inset 0 0 6px rgba(0, 0, 0, .1); |
| 102 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .1); | 102 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .1); |
| 103 | } | 103 | } |
| 104 | +.table-header { | ||
| 105 | + color: #727272; | ||
| 106 | + font-weight: 600; | ||
| 107 | +} | ||
| 104 | </style> | 108 | </style> |
web_src/src/components/CloudRecord.vue
| @@ -18,19 +18,17 @@ | @@ -18,19 +18,17 @@ | ||
| 18 | <div v-if="!recordDetail"> | 18 | <div v-if="!recordDetail"> |
| 19 | 19 | ||
| 20 | <!--设备列表--> | 20 | <!--设备列表--> |
| 21 | - <el-table :data="recordList" border style="width: 100%" :height="winHeight"> | ||
| 22 | - <el-table-column prop="app" label="应用名" align="center"> | 21 | + <el-table :data="recordList" style="width: 100%" :height="winHeight"> |
| 22 | + <el-table-column prop="app" label="应用名" > | ||
| 23 | </el-table-column> | 23 | </el-table-column> |
| 24 | - <el-table-column prop="stream" label="流ID" align="center"> | 24 | + <el-table-column prop="stream" label="流ID" > |
| 25 | </el-table-column> | 25 | </el-table-column> |
| 26 | - <el-table-column prop="time" label="时间" align="center"> | 26 | + <el-table-column prop="time" label="时间" > |
| 27 | </el-table-column> | 27 | </el-table-column> |
| 28 | - <el-table-column label="操作" width="360" align="center" fixed="right"> | 28 | + <el-table-column label="操作" width="360" fixed="right"> |
| 29 | <template slot-scope="scope"> | 29 | <template slot-scope="scope"> |
| 30 | - <el-button-group> | ||
| 31 | - <el-button size="mini" icon="el-icon-video-camera-solid" type="primary" @click="showRecordDetail(scope.row)">查看</el-button> | ||
| 32 | - <!-- <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteRecord(scope.row)">删除</el-button>--> | ||
| 33 | - </el-button-group> | 30 | + <el-button size="medium" icon="el-icon-folder-opened" type="text" @click="showRecordDetail(scope.row)">查看</el-button> |
| 31 | + <!-- <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteRecord(scope.row)">删除</el-button>--> | ||
| 34 | </template> | 32 | </template> |
| 35 | </el-table-column> | 33 | </el-table-column> |
| 36 | </el-table> | 34 | </el-table> |
web_src/src/components/DeviceList.vue
| @@ -7,34 +7,33 @@ | @@ -7,34 +7,33 @@ | ||
| 7 | @click="getDeviceList()"></el-button> | 7 | @click="getDeviceList()"></el-button> |
| 8 | </div> | 8 | </div> |
| 9 | </div> | 9 | </div> |
| 10 | - <!-- <devicePlayer ref="devicePlayer"></devicePlayer> --> | ||
| 11 | <!--设备列表--> | 10 | <!--设备列表--> |
| 12 | - <el-table :data="deviceList" border style="width: 100%;font-size: 12px;" :height="winHeight"> | ||
| 13 | - <el-table-column prop="name" label="名称" align="center"> | 11 | + <el-table :data="deviceList" style="width: 100%;font-size: 12px;" :height="winHeight" header-row-class-name="table-header"> |
| 12 | + <el-table-column prop="name" label="名称" min-width="160"> | ||
| 14 | </el-table-column> | 13 | </el-table-column> |
| 15 | - <el-table-column prop="deviceId" label="设备编号" width="180" align="center"> | 14 | + <el-table-column prop="deviceId" label="设备编号" min-width="200" > |
| 16 | </el-table-column> | 15 | </el-table-column> |
| 17 | - <el-table-column label="地址" width="180" align="center"> | 16 | + <el-table-column label="地址" min-width="160" > |
| 18 | <template slot-scope="scope"> | 17 | <template slot-scope="scope"> |
| 19 | <div slot="reference" class="name-wrapper"> | 18 | <div slot="reference" class="name-wrapper"> |
| 20 | <el-tag size="medium">{{ scope.row.hostAddress }}</el-tag> | 19 | <el-tag size="medium">{{ scope.row.hostAddress }}</el-tag> |
| 21 | </div> | 20 | </div> |
| 22 | </template> | 21 | </template> |
| 23 | </el-table-column> | 22 | </el-table-column> |
| 24 | - <el-table-column prop="manufacturer" label="厂家" align="center"> | 23 | + <el-table-column prop="manufacturer" label="厂家" min-width="120" > |
| 25 | </el-table-column> | 24 | </el-table-column> |
| 26 | - <el-table-column label="流传输模式" align="center" width="120"> | 25 | + <el-table-column label="流传输模式" min-width="160" > |
| 27 | <template slot-scope="scope"> | 26 | <template slot-scope="scope"> |
| 28 | - <el-select size="mini" @change="transportChange(scope.row)" v-model="scope.row.streamMode" placeholder="请选择"> | 27 | + <el-select size="mini" @change="transportChange(scope.row)" v-model="scope.row.streamMode" placeholder="请选择" style="width: 120px"> |
| 29 | <el-option key="UDP" label="UDP" value="UDP"></el-option> | 28 | <el-option key="UDP" label="UDP" value="UDP"></el-option> |
| 30 | <el-option key="TCP-ACTIVE" label="TCP主动模式" :disabled="true" value="TCP-ACTIVE"></el-option> | 29 | <el-option key="TCP-ACTIVE" label="TCP主动模式" :disabled="true" value="TCP-ACTIVE"></el-option> |
| 31 | <el-option key="TCP-PASSIVE" label="TCP被动模式" value="TCP-PASSIVE"></el-option> | 30 | <el-option key="TCP-PASSIVE" label="TCP被动模式" value="TCP-PASSIVE"></el-option> |
| 32 | </el-select> | 31 | </el-select> |
| 33 | </template> | 32 | </template> |
| 34 | </el-table-column> | 33 | </el-table-column> |
| 35 | - <el-table-column prop="channelCount" label="通道数" align="center"> | 34 | + <el-table-column prop="channelCount" label="通道数" min-width="120" > |
| 36 | </el-table-column> | 35 | </el-table-column> |
| 37 | - <el-table-column label="状态" width="120" align="center"> | 36 | + <el-table-column label="状态" min-width="120"> |
| 38 | <template slot-scope="scope"> | 37 | <template slot-scope="scope"> |
| 39 | <div slot="reference" class="name-wrapper"> | 38 | <div slot="reference" class="name-wrapper"> |
| 40 | <el-tag size="medium" v-if="scope.row.online == 1">在线</el-tag> | 39 | <el-tag size="medium" v-if="scope.row.online == 1">在线</el-tag> |
| @@ -42,30 +41,32 @@ | @@ -42,30 +41,32 @@ | ||
| 42 | </div> | 41 | </div> |
| 43 | </template> | 42 | </template> |
| 44 | </el-table-column> | 43 | </el-table-column> |
| 45 | - <el-table-column prop="keepaliveTime" label="最近心跳" align="center" width="140"> | 44 | + <el-table-column prop="keepaliveTime" label="最近心跳" min-width="160" > |
| 46 | </el-table-column> | 45 | </el-table-column> |
| 47 | - <el-table-column prop="registerTime" label="最近注册" align="center" width="140"> | ||
| 48 | - </el-table-column> | ||
| 49 | - <el-table-column prop="updateTime" label="更新时间" align="center" width="140"> | ||
| 50 | - </el-table-column> | ||
| 51 | - <el-table-column prop="createTime" label="创建时间" align="center" width="140"> | 46 | + <el-table-column prop="registerTime" label="最近注册" min-width="160"> |
| 52 | </el-table-column> | 47 | </el-table-column> |
| 48 | +<!-- <el-table-column prop="updateTime" label="更新时间" width="140">--> | ||
| 49 | +<!-- </el-table-column>--> | ||
| 50 | +<!-- <el-table-column prop="createTime" label="创建时间" width="140">--> | ||
| 51 | +<!-- </el-table-column>--> | ||
| 53 | 52 | ||
| 54 | - <el-table-column label="操作" width="450" align="center" fixed="right"> | 53 | + <el-table-column label="操作" min-width="450" fixed="right"> |
| 55 | <template slot-scope="scope"> | 54 | <template slot-scope="scope"> |
| 56 | - <el-button size="mini" v-if="scope.row.online!=0" icon="el-icon-refresh" @click="refDevice(scope.row)" | 55 | + <el-button type="text" size="medium" v-bind:disabled="scope.row.online==0" icon="el-icon-refresh" @click="refDevice(scope.row)" |
| 57 | @mouseover="getTooltipContent(scope.row.deviceId)">刷新 | 56 | @mouseover="getTooltipContent(scope.row.deviceId)">刷新 |
| 58 | </el-button> | 57 | </el-button> |
| 59 | - <el-button-group> | ||
| 60 | - <el-button size="mini" icon="el-icon-video-camera-solid" v-bind:disabled="scope.row.online==0" | ||
| 61 | - type="primary" @click="showChannelList(scope.row)">通道 | ||
| 62 | - </el-button> | ||
| 63 | - <el-button size="mini" icon="el-icon-location" v-bind:disabled="scope.row.online==0" type="primary" | ||
| 64 | - @click="showDevicePosition(scope.row)">定位 | ||
| 65 | - </el-button> | ||
| 66 | - <el-button size="mini" icon="el-icon-edit" type="primary" @click="edit(scope.row)">编辑</el-button> | ||
| 67 | - <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteDevice(scope.row)">删除</el-button> | ||
| 68 | - </el-button-group> | 58 | + <el-divider direction="vertical"></el-divider> |
| 59 | + <el-button type="text" size="medium" icon="el-icon-video-camera" v-bind:disabled="scope.row.online==0" | ||
| 60 | + @click="showChannelList(scope.row)">通道 | ||
| 61 | + </el-button> | ||
| 62 | + <el-divider direction="vertical"></el-divider> | ||
| 63 | + <el-button size="medium" icon="el-icon-location" v-bind:disabled="scope.row.online==0" type="text" | ||
| 64 | + @click="showDevicePosition(scope.row)">定位 | ||
| 65 | + </el-button> | ||
| 66 | + <el-divider direction="vertical"></el-divider> | ||
| 67 | + <el-button size="medium" icon="el-icon-edit" type="text" @click="edit(scope.row)">编辑</el-button> | ||
| 68 | + <el-divider direction="vertical"></el-divider> | ||
| 69 | + <el-button size="medium" icon="el-icon-delete" type="text" @click="deleteDevice(scope.row)" style="color: #f56c6c">删除</el-button> | ||
| 69 | </template> | 70 | </template> |
| 70 | </el-table-column> | 71 | </el-table-column> |
| 71 | </el-table> | 72 | </el-table> |
| @@ -347,4 +348,5 @@ export default { | @@ -347,4 +348,5 @@ export default { | ||
| 347 | padding: 0.3rem; | 348 | padding: 0.3rem; |
| 348 | width: 14.4rem; | 349 | width: 14.4rem; |
| 349 | } | 350 | } |
| 351 | + | ||
| 350 | </style> | 352 | </style> |
web_src/src/components/MediaServerManger.vue
| @@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
| 15 | <span style="font-size: 16px">{{item.id}}</span> | 15 | <span style="font-size: 16px">{{item.id}}</span> |
| 16 | <el-button v-if="!item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">编辑</el-button> | 16 | <el-button v-if="!item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">编辑</el-button> |
| 17 | <el-button v-if="item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">查看</el-button> | 17 | <el-button v-if="item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">查看</el-button> |
| 18 | - <el-button icon="el-icon-delete" style="margin-right: 10px;padding: 0;float: right;" type="text" @click="del(item)">移除</el-button> | 18 | + <el-button v-if="!item.defaultServer" icon="el-icon-delete" style="margin-right: 10px;padding: 0;float: right;" type="text" @click="del(item)">移除</el-button> |
| 19 | <div style="margin-top: 13px; line-height: 12px; "> | 19 | <div style="margin-top: 13px; line-height: 12px; "> |
| 20 | <span style="font-size: 14px; color: #999; margin-top: 5px; ">{{item.ip}}</span> | 20 | <span style="font-size: 14px; color: #999; margin-top: 5px; ">{{item.ip}}</span> |
| 21 | <span style="font-size: 14px; color: #999; margin-top: 5px; float: right;">{{item.createTime}}</span> | 21 | <span style="font-size: 14px; color: #999; margin-top: 5px; float: right;">{{item.createTime}}</span> |
web_src/src/components/ParentPlatformList.vue
| @@ -4,14 +4,15 @@ | @@ -4,14 +4,15 @@ | ||
| 4 | <div class="page-title">上级平台列表</div> | 4 | <div class="page-title">上级平台列表</div> |
| 5 | <div class="page-header-btn"> | 5 | <div class="page-header-btn"> |
| 6 | <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addParentPlatform">添加</el-button> | 6 | <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addParentPlatform">添加</el-button> |
| 7 | + <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button> | ||
| 7 | </div> | 8 | </div> |
| 8 | </div> | 9 | </div> |
| 9 | 10 | ||
| 10 | <!--设备列表--> | 11 | <!--设备列表--> |
| 11 | - <el-table :data="platformList" border style="width: 100%" :height="winHeight"> | ||
| 12 | - <el-table-column prop="name" label="名称" align="center"></el-table-column> | ||
| 13 | - <el-table-column prop="serverGBId" label="平台编号" align="center"></el-table-column> | ||
| 14 | - <el-table-column label="是否启用" width="120" align="center"> | 12 | + <el-table :data="platformList" style="width: 100%" :height="winHeight"> |
| 13 | + <el-table-column prop="name" label="名称" ></el-table-column> | ||
| 14 | + <el-table-column prop="serverGBId" label="平台编号" min-width="200"></el-table-column> | ||
| 15 | + <el-table-column label="是否启用" min-width="80" > | ||
| 15 | <template slot-scope="scope"> | 16 | <template slot-scope="scope"> |
| 16 | <div slot="reference" class="name-wrapper"> | 17 | <div slot="reference" class="name-wrapper"> |
| 17 | <el-tag size="medium" v-if="scope.row.enable">已启用</el-tag> | 18 | <el-tag size="medium" v-if="scope.row.enable">已启用</el-tag> |
| @@ -19,7 +20,7 @@ | @@ -19,7 +20,7 @@ | ||
| 19 | </div> | 20 | </div> |
| 20 | </template> | 21 | </template> |
| 21 | </el-table-column> | 22 | </el-table-column> |
| 22 | - <el-table-column label="状态" width="120" align="center"> | 23 | + <el-table-column label="状态" min-width="80" > |
| 23 | <template slot-scope="scope"> | 24 | <template slot-scope="scope"> |
| 24 | <div slot="reference" class="name-wrapper"> | 25 | <div slot="reference" class="name-wrapper"> |
| 25 | <el-tag size="medium" v-if="scope.row.status">在线</el-tag> | 26 | <el-tag size="medium" v-if="scope.row.status">在线</el-tag> |
| @@ -27,17 +28,17 @@ | @@ -27,17 +28,17 @@ | ||
| 27 | </div> | 28 | </div> |
| 28 | </template> | 29 | </template> |
| 29 | </el-table-column> | 30 | </el-table-column> |
| 30 | - <el-table-column label="地址" width="180" align="center"> | 31 | + <el-table-column label="地址" min-width="160" > |
| 31 | <template slot-scope="scope"> | 32 | <template slot-scope="scope"> |
| 32 | <div slot="reference" class="name-wrapper"> | 33 | <div slot="reference" class="name-wrapper"> |
| 33 | <el-tag size="medium">{{ scope.row.serverIP}}:{{scope.row.serverPort }}</el-tag> | 34 | <el-tag size="medium">{{ scope.row.serverIP}}:{{scope.row.serverPort }}</el-tag> |
| 34 | </div> | 35 | </div> |
| 35 | </template> | 36 | </template> |
| 36 | </el-table-column> | 37 | </el-table-column> |
| 37 | - <el-table-column prop="deviceGBId" label="设备国标编号" width="200" align="center"></el-table-column> | ||
| 38 | - <el-table-column prop="transport" label="信令传输模式" width="120" align="center"></el-table-column> | ||
| 39 | - <el-table-column prop="channelCount" label="通道数" width="120" align="center"></el-table-column> | ||
| 40 | - <el-table-column label="订阅信息" width="240" align="center" fixed="right"> | 38 | + <el-table-column prop="deviceGBId" label="设备国标编号" min-width="200" ></el-table-column> |
| 39 | + <el-table-column prop="transport" label="信令传输模式" min-width="120" ></el-table-column> | ||
| 40 | + <el-table-column prop="channelCount" label="通道数" min-width="120" ></el-table-column> | ||
| 41 | + <el-table-column label="订阅信息" min-width="120" fixed="right"> | ||
| 41 | <template slot-scope="scope"> | 42 | <template slot-scope="scope"> |
| 42 | <i v-if="scope.row.alarmSubscribe" style="font-size: 20px" title="报警订阅" class="iconfont icon-gbaojings subscribe-on " ></i> | 43 | <i v-if="scope.row.alarmSubscribe" style="font-size: 20px" title="报警订阅" class="iconfont icon-gbaojings subscribe-on " ></i> |
| 43 | <i v-if="!scope.row.alarmSubscribe" style="font-size: 20px" title="报警订阅" class="iconfont icon-gbaojings subscribe-off " ></i> | 44 | <i v-if="!scope.row.alarmSubscribe" style="font-size: 20px" title="报警订阅" class="iconfont icon-gbaojings subscribe-off " ></i> |
| @@ -48,11 +49,11 @@ | @@ -48,11 +49,11 @@ | ||
| 48 | </template> | 49 | </template> |
| 49 | </el-table-column> | 50 | </el-table-column> |
| 50 | 51 | ||
| 51 | - <el-table-column label="操作" width="300" align="center" fixed="right"> | 52 | + <el-table-column label="操作" min-width="240" fixed="right"> |
| 52 | <template slot-scope="scope"> | 53 | <template slot-scope="scope"> |
| 53 | - <el-button size="mini" icon="el-icon-edit" @click="editPlatform(scope.row)">编辑</el-button> | ||
| 54 | - <el-button size="mini" icon="el-icon-share" type="primary" @click="chooseChannel(scope.row)">选择通道</el-button> | ||
| 55 | - <el-button size="mini" icon="el-icon-delete" type="danger" @click="deletePlatform(scope.row)">删除</el-button> | 54 | + <el-button size="medium" icon="el-icon-edit" type="text" @click="editPlatform(scope.row)">编辑</el-button> |
| 55 | + <el-button size="medium" icon="el-icon-share" type="text" @click="chooseChannel(scope.row)">选择通道</el-button> | ||
| 56 | + <el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c" @click="deletePlatform(scope.row)">删除</el-button> | ||
| 56 | </template> | 57 | </template> |
| 57 | </el-table-column> | 58 | </el-table-column> |
| 58 | </el-table> | 59 | </el-table> |
| @@ -168,6 +169,9 @@ export default { | @@ -168,6 +169,9 @@ export default { | ||
| 168 | console.log(error); | 169 | console.log(error); |
| 169 | }); | 170 | }); |
| 170 | 171 | ||
| 172 | + }, | ||
| 173 | + refresh: function (){ | ||
| 174 | + this.initData(); | ||
| 171 | } | 175 | } |
| 172 | 176 | ||
| 173 | } | 177 | } |
web_src/src/components/PushVideoList.vue
| @@ -34,52 +34,54 @@ | @@ -34,52 +34,54 @@ | ||
| 34 | <el-button icon="el-icon-delete" size="mini" style="margin-right: 1rem;" | 34 | <el-button icon="el-icon-delete" size="mini" style="margin-right: 1rem;" |
| 35 | :disabled="multipleSelection.length === 0" type="danger" @click="batchDel">批量移除 | 35 | :disabled="multipleSelection.length === 0" type="danger" @click="batchDel">批量移除 |
| 36 | </el-button> | 36 | </el-button> |
| 37 | + <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button> | ||
| 37 | </div> | 38 | </div> |
| 38 | </div> | 39 | </div> |
| 39 | <devicePlayer ref="devicePlayer"></devicePlayer> | 40 | <devicePlayer ref="devicePlayer"></devicePlayer> |
| 40 | <addStreamTOGB ref="addStreamTOGB"></addStreamTOGB> | 41 | <addStreamTOGB ref="addStreamTOGB"></addStreamTOGB> |
| 41 | - <el-table ref="pushListTable" :data="pushList" border style="width: 100%" :height="winHeight" | 42 | + <el-table ref="pushListTable" :data="pushList" style="width: 100%" :height="winHeight" |
| 42 | @selection-change="handleSelectionChange" :row-key="(row)=> row.app + row.stream"> | 43 | @selection-change="handleSelectionChange" :row-key="(row)=> row.app + row.stream"> |
| 43 | - <el-table-column align="center" type="selection" :reserve-selection="true" width="55"> | 44 | + <el-table-column type="selection" :reserve-selection="true" min-width="55"> |
| 44 | </el-table-column> | 45 | </el-table-column> |
| 45 | - <el-table-column prop="name" label="名称" align="center"> | 46 | + <el-table-column prop="name" label="名称" min-width="200"> |
| 46 | </el-table-column> | 47 | </el-table-column> |
| 47 | - <el-table-column prop="app" label="APP" align="center"> | 48 | + <el-table-column prop="app" label="APP" min-width="200"> |
| 48 | </el-table-column> | 49 | </el-table-column> |
| 49 | - <el-table-column prop="stream" label="流ID" align="center"> | 50 | + <el-table-column prop="stream" label="流ID" min-width="200"> |
| 50 | </el-table-column> | 51 | </el-table-column> |
| 51 | - <el-table-column prop="gbId" label="国标编码" width="200" align="center"> | 52 | + <el-table-column prop="gbId" label="国标编码" min-width="200" > |
| 52 | </el-table-column> | 53 | </el-table-column> |
| 53 | - <el-table-column prop="mediaServerId" label="流媒体" width="200" align="center"> | 54 | + <el-table-column prop="mediaServerId" label="流媒体" min-width="200" > |
| 54 | </el-table-column> | 55 | </el-table-column> |
| 55 | - <el-table-column label="开始时间" align="center" width="200"> | 56 | + <el-table-column label="开始时间" min-width="200"> |
| 56 | <template slot-scope="scope"> | 57 | <template slot-scope="scope"> |
| 57 | <el-button-group> | 58 | <el-button-group> |
| 58 | {{ dateFormat(parseInt(scope.row.createStamp)) }} | 59 | {{ dateFormat(parseInt(scope.row.createStamp)) }} |
| 59 | </el-button-group> | 60 | </el-button-group> |
| 60 | </template> | 61 | </template> |
| 61 | </el-table-column> | 62 | </el-table-column> |
| 62 | - <el-table-column label="正在推流" align="center" width="100"> | 63 | + <el-table-column label="正在推流" min-width="100"> |
| 63 | <template slot-scope="scope"> | 64 | <template slot-scope="scope"> |
| 64 | {{ (scope.row.status == false && scope.row.gbId == null) || scope.row.status ? '是' : '否' }} | 65 | {{ (scope.row.status == false && scope.row.gbId == null) || scope.row.status ? '是' : '否' }} |
| 65 | </template> | 66 | </template> |
| 66 | </el-table-column> | 67 | </el-table-column> |
| 67 | 68 | ||
| 68 | - <el-table-column label="操作" width="360" align="center" fixed="right"> | 69 | + <el-table-column label="操作" min-width="360" fixed="right"> |
| 69 | <template slot-scope="scope"> | 70 | <template slot-scope="scope"> |
| 70 | - <el-button-group> | ||
| 71 | - <el-button size="mini" icon="el-icon-video-play" | ||
| 72 | - v-if="(scope.row.status == false && scope.row.gbId == null) || scope.row.status" | ||
| 73 | - @click="playPush(scope.row)">播放 | ||
| 74 | - </el-button> | ||
| 75 | - <el-button size="mini" icon="el-icon-delete" type="danger" @click="stopPush(scope.row)">移除</el-button> | ||
| 76 | - <el-button size="mini" icon="el-icon-position" type="primary" v-if="!!!scope.row.gbId" | ||
| 77 | - @click="addToGB(scope.row)">加入国标 | ||
| 78 | - </el-button> | ||
| 79 | - <el-button size="mini" icon="el-icon-position" type="primary" v-if="!!scope.row.gbId" | ||
| 80 | - @click="removeFromGB(scope.row)">移出国标 | ||
| 81 | - </el-button> | ||
| 82 | - </el-button-group> | 71 | + <el-button size="medium" icon="el-icon-video-play" |
| 72 | + v-if="(scope.row.status == false && scope.row.gbId == null) || scope.row.status" | ||
| 73 | + @click="playPush(scope.row)" type="text">播放 | ||
| 74 | + </el-button> | ||
| 75 | + <el-divider direction="vertical"></el-divider> | ||
| 76 | + <el-button size="medium" icon="el-icon-delete" type="text" @click="stopPush(scope.row)" style="color: #f56c6c" >移除</el-button> | ||
| 77 | + <el-divider direction="vertical"></el-divider> | ||
| 78 | + <el-button size="medium" icon="el-icon-position" type="text" v-if="!!!scope.row.gbId" | ||
| 79 | + @click="addToGB(scope.row)">加入国标 | ||
| 80 | + </el-button> | ||
| 81 | + <el-divider v-if="!!!scope.row.gbId" direction="vertical"></el-divider> | ||
| 82 | + <el-button size="medium" icon="el-icon-position" type="text" v-if="!!scope.row.gbId" | ||
| 83 | + @click="removeFromGB(scope.row)">移出国标 | ||
| 84 | + </el-button> | ||
| 83 | </template> | 85 | </template> |
| 84 | </el-table-column> | 86 | </el-table-column> |
| 85 | </el-table> | 87 | </el-table> |
| @@ -284,6 +286,9 @@ export default { | @@ -284,6 +286,9 @@ export default { | ||
| 284 | handleSelectionChange: function (val) { | 286 | handleSelectionChange: function (val) { |
| 285 | this.multipleSelection = val; | 287 | this.multipleSelection = val; |
| 286 | }, | 288 | }, |
| 289 | + refresh: function () { | ||
| 290 | + this.initData(); | ||
| 291 | + }, | ||
| 287 | } | 292 | } |
| 288 | }; | 293 | }; |
| 289 | </script> | 294 | </script> |
web_src/src/components/StreamProxyList.vue
| @@ -5,14 +5,15 @@ | @@ -5,14 +5,15 @@ | ||
| 5 | <div class="page-header-btn"> | 5 | <div class="page-header-btn"> |
| 6 | <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addStreamProxy">添加代理</el-button> | 6 | <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addStreamProxy">添加代理</el-button> |
| 7 | <el-button v-if="false" icon="el-icon-search" size="mini" style="margin-right: 1rem;" type="primary" @click="addOnvif">搜索ONVIF</el-button> | 7 | <el-button v-if="false" icon="el-icon-search" size="mini" style="margin-right: 1rem;" type="primary" @click="addOnvif">搜索ONVIF</el-button> |
| 8 | + <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button> | ||
| 8 | </div> | 9 | </div> |
| 9 | </div> | 10 | </div> |
| 10 | <devicePlayer ref="devicePlayer"></devicePlayer> | 11 | <devicePlayer ref="devicePlayer"></devicePlayer> |
| 11 | - <el-table :data="streamProxyList" border style="width: 100%" :height="winHeight"> | ||
| 12 | - <el-table-column prop="name" label="名称" align="center" show-overflow-tooltip/> | ||
| 13 | - <el-table-column prop="app" label="流应用名" align="center" show-overflow-tooltip/> | ||
| 14 | - <el-table-column prop="stream" label="流ID" align="center" show-overflow-tooltip/> | ||
| 15 | - <el-table-column label="流地址" width="400" align="center" show-overflow-tooltip > | 12 | + <el-table :data="streamProxyList" style="width: 100%" :height="winHeight"> |
| 13 | + <el-table-column prop="name" label="名称" min-width="120" show-overflow-tooltip/> | ||
| 14 | + <el-table-column prop="app" label="流应用名" min-width="120" show-overflow-tooltip/> | ||
| 15 | + <el-table-column prop="stream" label="流ID" min-width="120" show-overflow-tooltip/> | ||
| 16 | + <el-table-column label="流地址" min-width="400" show-overflow-tooltip > | ||
| 16 | <template slot-scope="scope"> | 17 | <template slot-scope="scope"> |
| 17 | <div slot="reference" class="name-wrapper"> | 18 | <div slot="reference" class="name-wrapper"> |
| 18 | 19 | ||
| @@ -27,8 +28,8 @@ | @@ -27,8 +28,8 @@ | ||
| 27 | </div> | 28 | </div> |
| 28 | </template> | 29 | </template> |
| 29 | </el-table-column> | 30 | </el-table-column> |
| 30 | - <el-table-column prop="mediaServerId" label="流媒体" width="150" align="center"></el-table-column> | ||
| 31 | - <el-table-column label="类型" width="100" align="center"> | 31 | + <el-table-column prop="mediaServerId" label="流媒体" min-width="180" ></el-table-column> |
| 32 | + <el-table-column label="类型" width="100" > | ||
| 32 | <template slot-scope="scope"> | 33 | <template slot-scope="scope"> |
| 33 | <div slot="reference" class="name-wrapper"> | 34 | <div slot="reference" class="name-wrapper"> |
| 34 | <el-tag size="medium">{{scope.row.type}}</el-tag> | 35 | <el-tag size="medium">{{scope.row.type}}</el-tag> |
| @@ -36,8 +37,8 @@ | @@ -36,8 +37,8 @@ | ||
| 36 | </template> | 37 | </template> |
| 37 | </el-table-column> | 38 | </el-table-column> |
| 38 | 39 | ||
| 39 | - <el-table-column prop="gbId" label="国标编码" width="180" align="center" show-overflow-tooltip/> | ||
| 40 | - <el-table-column label="状态" width="120" align="center"> | 40 | + <el-table-column prop="gbId" label="国标编码" min-width="180" show-overflow-tooltip/> |
| 41 | + <el-table-column label="状态" min-width="120" > | ||
| 41 | <template slot-scope="scope"> | 42 | <template slot-scope="scope"> |
| 42 | <div slot="reference" class="name-wrapper"> | 43 | <div slot="reference" class="name-wrapper"> |
| 43 | <el-tag size="medium" v-if="scope.row.status">在线</el-tag> | 44 | <el-tag size="medium" v-if="scope.row.status">在线</el-tag> |
| @@ -45,7 +46,7 @@ | @@ -45,7 +46,7 @@ | ||
| 45 | </div> | 46 | </div> |
| 46 | </template> | 47 | </template> |
| 47 | </el-table-column> | 48 | </el-table-column> |
| 48 | - <el-table-column label="启用" width="120" align="center"> | 49 | + <el-table-column label="启用" min-width="120" > |
| 49 | <template slot-scope="scope"> | 50 | <template slot-scope="scope"> |
| 50 | <div slot="reference" class="name-wrapper"> | 51 | <div slot="reference" class="name-wrapper"> |
| 51 | <el-tag size="medium" v-if="scope.row.enable">已启用</el-tag> | 52 | <el-tag size="medium" v-if="scope.row.enable">已启用</el-tag> |
| @@ -53,8 +54,8 @@ | @@ -53,8 +54,8 @@ | ||
| 53 | </div> | 54 | </div> |
| 54 | </template> | 55 | </template> |
| 55 | </el-table-column> | 56 | </el-table-column> |
| 56 | - <el-table-column prop="createTime" label="创建时间" align="center" width="150" show-overflow-tooltip/> | ||
| 57 | - <el-table-column label="转HLS" width="120" align="center"> | 57 | + <el-table-column prop="createTime" label="创建时间" min-width="150" show-overflow-tooltip/> |
| 58 | + <el-table-column label="转HLS" min-width="120" > | ||
| 58 | <template slot-scope="scope"> | 59 | <template slot-scope="scope"> |
| 59 | <div slot="reference" class="name-wrapper"> | 60 | <div slot="reference" class="name-wrapper"> |
| 60 | <el-tag size="medium" v-if="scope.row.enable_hls">已启用</el-tag> | 61 | <el-tag size="medium" v-if="scope.row.enable_hls">已启用</el-tag> |
| @@ -62,7 +63,7 @@ | @@ -62,7 +63,7 @@ | ||
| 62 | </div> | 63 | </div> |
| 63 | </template> | 64 | </template> |
| 64 | </el-table-column> | 65 | </el-table-column> |
| 65 | - <el-table-column label="MP4录制" width="120" align="center"> | 66 | + <el-table-column label="MP4录制" min-width="120" > |
| 66 | <template slot-scope="scope"> | 67 | <template slot-scope="scope"> |
| 67 | <div slot="reference" class="name-wrapper"> | 68 | <div slot="reference" class="name-wrapper"> |
| 68 | <el-tag size="medium" v-if="scope.row.enable_mp4">已启用</el-tag> | 69 | <el-tag size="medium" v-if="scope.row.enable_mp4">已启用</el-tag> |
| @@ -70,7 +71,7 @@ | @@ -70,7 +71,7 @@ | ||
| 70 | </div> | 71 | </div> |
| 71 | </template> | 72 | </template> |
| 72 | </el-table-column> | 73 | </el-table-column> |
| 73 | - <el-table-column label="无人观看自动删除" width="160" align="center"> | 74 | + <el-table-column label="无人观看自动删除" min-width="160" > |
| 74 | <template slot-scope="scope"> | 75 | <template slot-scope="scope"> |
| 75 | <div slot="reference" class="name-wrapper"> | 76 | <div slot="reference" class="name-wrapper"> |
| 76 | <el-tag size="medium" v-if="scope.row.enable_remove_none_reader">已启用</el-tag> | 77 | <el-tag size="medium" v-if="scope.row.enable_remove_none_reader">已启用</el-tag> |
| @@ -80,14 +81,15 @@ | @@ -80,14 +81,15 @@ | ||
| 80 | </el-table-column> | 81 | </el-table-column> |
| 81 | 82 | ||
| 82 | 83 | ||
| 83 | - <el-table-column label="操作" width="360" align="center" fixed="right"> | 84 | + <el-table-column label="操作" width="360" fixed="right"> |
| 84 | <template slot-scope="scope"> | 85 | <template slot-scope="scope"> |
| 85 | - <el-button-group> | ||
| 86 | - <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.enable" @click="play(scope.row)">播放</el-button> | ||
| 87 | - <el-button size="mini" icon="el-icon-close" type="success" v-if="scope.row.enable" @click="stop(scope.row)">停用</el-button> | ||
| 88 | - <el-button size="mini" icon="el-icon-check" type="primary" :loading="startBtnLaoding" v-if="!scope.row.enable" @click="start(scope.row)">启用</el-button> | ||
| 89 | - <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteStreamProxy(scope.row)">删除</el-button> | ||
| 90 | - </el-button-group> | 86 | + <el-button size="medium" icon="el-icon-video-play" type="text" v-if="scope.row.enable" @click="play(scope.row)">播放</el-button> |
| 87 | + <el-divider direction="vertical"></el-divider> | ||
| 88 | + <el-button size="medium" icon="el-icon-switch-button" type="text" v-if="scope.row.enable" @click="stop(scope.row)">停用</el-button> | ||
| 89 | + <el-divider direction="vertical"></el-divider> | ||
| 90 | + <el-button size="medium" icon="el-icon-check" type="text" :loading="startBtnLaoding" v-if="!scope.row.enable" @click="start(scope.row)">启用</el-button> | ||
| 91 | + <el-divider v-if="!scope.row.enable" direction="vertical"></el-divider> | ||
| 92 | + <el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c" @click="deleteStreamProxy(scope.row)">删除</el-button> | ||
| 91 | </template> | 93 | </template> |
| 92 | </el-table-column> | 94 | </el-table-column> |
| 93 | </el-table> | 95 | </el-table> |
| @@ -305,8 +307,10 @@ | @@ -305,8 +307,10 @@ | ||
| 305 | console.log(error); | 307 | console.log(error); |
| 306 | that.getListLoading = false; | 308 | that.getListLoading = false; |
| 307 | }); | 309 | }); |
| 308 | - } | ||
| 309 | - | 310 | + }, |
| 311 | + refresh: function (){ | ||
| 312 | + this.initData(); | ||
| 313 | + } | ||
| 310 | } | 314 | } |
| 311 | }; | 315 | }; |
| 312 | </script> | 316 | </script> |
web_src/src/components/channelList.vue
| @@ -2,10 +2,10 @@ | @@ -2,10 +2,10 @@ | ||
| 2 | <div id="channelList" style="width: 100%"> | 2 | <div id="channelList" style="width: 100%"> |
| 3 | <div class="page-header"> | 3 | <div class="page-header"> |
| 4 | <div class="page-title"> | 4 | <div class="page-title"> |
| 5 | - <el-button icon="el-icon-arrow-left" size="mini" style="margin-right: 1rem;" type="primary" @click="showDevice"> | ||
| 6 | - 返回 | ||
| 7 | - </el-button> | ||
| 8 | - 通道列表({{ parentChannelId == 0 ? deviceId : parentChannelId }})</div> | 5 | + <el-button icon="el-icon-back" size="mini" style="font-size: 20px; color: #000;" type="text" @click="showDevice" ></el-button> |
| 6 | + <el-divider direction="vertical"></el-divider> | ||
| 7 | + 通道列表 | ||
| 8 | + </div> | ||
| 9 | <div class="page-header-btn"> | 9 | <div class="page-header-btn"> |
| 10 | 搜索: | 10 | 搜索: |
| 11 | <el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字" | 11 | <el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字" |
| @@ -25,84 +25,85 @@ | @@ -25,84 +25,85 @@ | ||
| 25 | <el-option label="在线" value="true"></el-option> | 25 | <el-option label="在线" value="true"></el-option> |
| 26 | <el-option label="离线" value="false"></el-option> | 26 | <el-option label="离线" value="false"></el-option> |
| 27 | </el-select> | 27 | </el-select> |
| 28 | - <el-checkbox size="mini" v-model="autoList" @change="autoListChange"> | ||
| 29 | - 自动刷新 | ||
| 30 | - </el-checkbox> | 28 | + <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button> |
| 31 | </div> | 29 | </div> |
| 32 | </div> | 30 | </div> |
| 33 | <devicePlayer ref="devicePlayer" v-loading="isLoging"></devicePlayer> | 31 | <devicePlayer ref="devicePlayer" v-loading="isLoging"></devicePlayer> |
| 34 | <!--设备列表--> | 32 | <!--设备列表--> |
| 35 | - <el-table ref="channelListTable" :data="deviceChannelList" :height="winHeight" border style="width: 100%"> | ||
| 36 | - <el-table-column prop="channelId" label="通道编号" width="200"> | 33 | + <el-table ref="channelListTable" :data="deviceChannelList" :height="winHeight" style="width: 100%" header-row-class-name="table-header"> |
| 34 | + <el-table-column prop="channelId" label="通道编号" min-width="200"> | ||
| 37 | </el-table-column> | 35 | </el-table-column> |
| 38 | - <el-table-column prop="name" label="通道名称"> | 36 | + <el-table-column prop="deviceId" label="设备编号" min-width="200"> |
| 39 | </el-table-column> | 37 | </el-table-column> |
| 40 | - <el-table-column label="快照" width="80" align="center"> | ||
| 41 | - <template slot-scope="scope"> | ||
| 42 | - <img style="max-height: 3rem;max-width: 4rem;" | ||
| 43 | - v-if="scope.row.subCount === 0 && scope.row.parental === 0" | ||
| 44 | - :id="scope.row.deviceId + '_' + scope.row.channelId" | ||
| 45 | - :src="getSnap(scope.row)" | ||
| 46 | - @error="getSnapErrorEvent($event.target.id)" | ||
| 47 | - alt=""> | ||
| 48 | - <!-- <el-image--> | ||
| 49 | - <!-- :id="'snapImg_' + scope.row.deviceId + '_' + scope.row.channelId"--> | ||
| 50 | - <!-- :src="getSnap(scope.row)"--> | ||
| 51 | - <!-- @error="getSnapErrorEvent($event, scope.row)"--> | ||
| 52 | - <!-- :fit="'contain'">--> | ||
| 53 | - <!-- <div slot="error" class="image-slot">--> | ||
| 54 | - <!-- <i class="el-icon-picture-outline"></i>--> | ||
| 55 | - <!-- </div>--> | ||
| 56 | - <!-- </el-image>--> | 38 | + <el-table-column prop="name" label="通道名称" min-width="200"> |
| 39 | + </el-table-column> | ||
| 40 | + <el-table-column label="快照" min-width="120"> | ||
| 41 | + <template v-slot:default="scope"> | ||
| 42 | + <el-image | ||
| 43 | + :src="getSnap(scope.row)" | ||
| 44 | + :preview-src-list="getBigSnap(scope.row)" | ||
| 45 | + @error="getSnapErrorEvent(scope.row.deviceId, scope.row.channelId)" | ||
| 46 | + :fit="'contain'" | ||
| 47 | + style="width: 60px"> | ||
| 48 | + <div slot="error" class="image-slot"> | ||
| 49 | + <i class="el-icon-picture-outline"></i> | ||
| 50 | + </div> | ||
| 51 | + </el-image> | ||
| 57 | </template> | 52 | </template> |
| 58 | </el-table-column> | 53 | </el-table-column> |
| 59 | - <el-table-column prop="subCount" label="子节点数"> | 54 | + <el-table-column prop="subCount" label="子节点数" min-width="120"> |
| 60 | </el-table-column> | 55 | </el-table-column> |
| 61 | - <el-table-column prop="manufacture" label="厂家"> | 56 | + <el-table-column prop="manufacture" label="厂家" min-width="120"> |
| 62 | </el-table-column> | 57 | </el-table-column> |
| 63 | - <el-table-column label="位置信息" align="center"> | 58 | + <el-table-column label="位置信息" min-width="200"> |
| 64 | <template slot-scope="scope"> | 59 | <template slot-scope="scope"> |
| 65 | - <span>{{ scope.row.longitude }},{{ scope.row.latitude }}</span> | 60 | + <span v-if="scope.row.longitude*scope.row.latitude > 0">{{ scope.row.longitude }},<br>{{ scope.row.latitude }}</span> |
| 61 | + <span v-if="scope.row.longitude*scope.row.latitude === 0">无</span> | ||
| 66 | </template> | 62 | </template> |
| 67 | </el-table-column> | 63 | </el-table-column> |
| 68 | - <el-table-column prop="ptztypeText" label="云台类型"/> | ||
| 69 | - <el-table-column label="开启音频" align="center"> | 64 | + <el-table-column prop="ptztypeText" label="云台类型" min-width="120"/> |
| 65 | + <el-table-column label="开启音频" min-width="120"> | ||
| 70 | <template slot-scope="scope"> | 66 | <template slot-scope="scope"> |
| 71 | <el-switch @change="updateChannel(scope.row)" v-model="scope.row.hasAudio" active-color="#409EFF"> | 67 | <el-switch @change="updateChannel(scope.row)" v-model="scope.row.hasAudio" active-color="#409EFF"> |
| 72 | </el-switch> | 68 | </el-switch> |
| 73 | </template> | 69 | </template> |
| 74 | </el-table-column> | 70 | </el-table-column> |
| 75 | - <el-table-column label="状态" width="180" align="center"> | 71 | + <el-table-column label="状态" min-width="120"> |
| 76 | <template slot-scope="scope"> | 72 | <template slot-scope="scope"> |
| 77 | <div slot="reference" class="name-wrapper"> | 73 | <div slot="reference" class="name-wrapper"> |
| 78 | - <el-tag size="medium" v-if="scope.row.status == 1">开启</el-tag> | ||
| 79 | - <el-tag size="medium" type="info" v-if="scope.row.status == 0">关闭</el-tag> | 74 | + <el-tag size="medium" v-if="scope.row.status === 1">在线</el-tag> |
| 75 | + <el-tag size="medium" type="info" v-if="scope.row.status === 0">离线</el-tag> | ||
| 80 | </div> | 76 | </div> |
| 81 | </template> | 77 | </template> |
| 82 | </el-table-column> | 78 | </el-table-column> |
| 83 | 79 | ||
| 84 | 80 | ||
| 85 | - <el-table-column label="操作" width="280" align="center" fixed="right"> | 81 | + <el-table-column label="操作" min-width="280" fixed="right"> |
| 86 | <template slot-scope="scope"> | 82 | <template slot-scope="scope"> |
| 87 | - <el-button-group> | ||
| 88 | - <!-- <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> --> | ||
| 89 | - <el-button size="mini" icon="el-icon-video-play" @click="sendDevicePush(scope.row)">播放</el-button> | ||
| 90 | - <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="!!scope.row.streamId" | ||
| 91 | - @click="stopDevicePush(scope.row)">停止 | ||
| 92 | - </el-button> | ||
| 93 | - <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.subCount > 0 || scope.row.parental === 1" | ||
| 94 | - @click="changeSubchannel(scope.row)">查看 | ||
| 95 | - </el-button> | ||
| 96 | - <el-button size="mini" icon="el-icon-video-camera" type="primary" @click="queryRecords(scope.row)">设备录像 | ||
| 97 | - </el-button> | ||
| 98 | - <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> --> | ||
| 99 | - </el-button-group> | 83 | + <!-- <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> --> |
| 84 | + <el-button size="medium" icon="el-icon-video-play" type="text" @click="sendDevicePush(scope.row)">播放</el-button> | ||
| 85 | + <el-button size="medium" icon="el-icon-switch-button" type="text" style="color: #f56c6c" v-if="!!scope.row.streamId" | ||
| 86 | + @click="stopDevicePush(scope.row)">停止 | ||
| 87 | + </el-button> | ||
| 88 | + <el-divider direction="vertical"></el-divider> | ||
| 89 | + <el-button size="medium" icon="el-icon-s-open" type="text" v-if="scope.row.subCount > 0 || scope.row.parental === 1" | ||
| 90 | + @click="changeSubchannel(scope.row)">查看 | ||
| 91 | + </el-button> | ||
| 92 | + <el-divider v-if="scope.row.subCount > 0 || scope.row.parental === 1" direction="vertical"></el-divider> | ||
| 93 | + <el-button size="medium" icon="el-icon-video-camera" type="text" @click="queryRecords(scope.row)">设备录像 | ||
| 94 | + </el-button> | ||
| 100 | </template> | 95 | </template> |
| 101 | </el-table-column> | 96 | </el-table-column> |
| 102 | </el-table> | 97 | </el-table> |
| 103 | - <el-pagination style="float: right" @size-change="handleSizeChange" @current-change="currentChange" | ||
| 104 | - :current-page="currentPage" :page-size="count" :page-sizes="[15, 20, 30, 50]" | ||
| 105 | - layout="total, sizes, prev, pager, next" :total="total"> | 98 | + <el-pagination |
| 99 | + style="float: right" | ||
| 100 | + @size-change="handleSizeChange" | ||
| 101 | + @current-change="currentChange" | ||
| 102 | + :current-page="currentPage" | ||
| 103 | + :page-size="count" | ||
| 104 | + :page-sizes="[15, 25, 35, 50]" | ||
| 105 | + layout="total, sizes, prev, pager, next" | ||
| 106 | + :total="total"> | ||
| 106 | </el-pagination> | 107 | </el-pagination> |
| 107 | </div> | 108 | </div> |
| 108 | </template> | 109 | </template> |
| @@ -111,6 +112,8 @@ | @@ -111,6 +112,8 @@ | ||
| 111 | import devicePlayer from './dialog/devicePlayer.vue' | 112 | import devicePlayer from './dialog/devicePlayer.vue' |
| 112 | import uiHeader from '../layout/UiHeader.vue' | 113 | import uiHeader from '../layout/UiHeader.vue' |
| 113 | import moment from "moment"; | 114 | import moment from "moment"; |
| 115 | +import DviceService from "./service/DeviceService"; | ||
| 116 | +import DeviceService from "./service/DeviceService"; | ||
| 114 | 117 | ||
| 115 | export default { | 118 | export default { |
| 116 | name: 'channelList', | 119 | name: 'channelList', |
| @@ -120,6 +123,8 @@ export default { | @@ -120,6 +123,8 @@ export default { | ||
| 120 | }, | 123 | }, |
| 121 | data() { | 124 | data() { |
| 122 | return { | 125 | return { |
| 126 | + deviceService: new DeviceService(), | ||
| 127 | + device: null, | ||
| 123 | deviceId: this.$route.params.deviceId, | 128 | deviceId: this.$route.params.deviceId, |
| 124 | parentChannelId: this.$route.params.parentChannelId, | 129 | parentChannelId: this.$route.params.parentChannelId, |
| 125 | deviceChannelList: [], | 130 | deviceChannelList: [], |
| @@ -135,16 +140,21 @@ export default { | @@ -135,16 +140,21 @@ export default { | ||
| 135 | total: 0, | 140 | total: 0, |
| 136 | beforeUrl: "/deviceList", | 141 | beforeUrl: "/deviceList", |
| 137 | isLoging: false, | 142 | isLoging: false, |
| 138 | - autoList: true, | ||
| 139 | loadSnap: {} | 143 | loadSnap: {} |
| 140 | }; | 144 | }; |
| 141 | }, | 145 | }, |
| 142 | 146 | ||
| 143 | mounted() { | 147 | mounted() { |
| 144 | - this.initData(); | ||
| 145 | - if (this.autoList) { | ||
| 146 | - this.updateLooper = setInterval(this.initData, 5000); | 148 | + if (this.deviceId) { |
| 149 | + this.deviceService.getDevice(this.deviceId, (result)=>{ | ||
| 150 | + this.device = result; | ||
| 151 | + | ||
| 152 | + }, (error)=>{ | ||
| 153 | + console.log("获取设备信息失败") | ||
| 154 | + console.error(error) | ||
| 155 | + }) | ||
| 147 | } | 156 | } |
| 157 | + this.initData(); | ||
| 148 | 158 | ||
| 149 | }, | 159 | }, |
| 150 | destroyed() { | 160 | destroyed() { |
| @@ -177,12 +187,8 @@ export default { | @@ -177,12 +187,8 @@ export default { | ||
| 177 | }) | 187 | }) |
| 178 | }, | 188 | }, |
| 179 | handleSizeChange: function (val) { | 189 | handleSizeChange: function (val) { |
| 180 | - var url = `/${this.$router.currentRoute.name}/${this.$router.params.deviceId}/${this.$router.params.parentChannelId}/${val}/1` | ||
| 181 | - this.$router.push(url).then(() => { | ||
| 182 | - this.initParam(); | ||
| 183 | - this.initData(); | ||
| 184 | - }) | ||
| 185 | - | 190 | + this.count = val; |
| 191 | + this.getDeviceChannelList(); | ||
| 186 | }, | 192 | }, |
| 187 | getDeviceChannelList: function () { | 193 | getDeviceChannelList: function () { |
| 188 | let that = this; | 194 | let that = this; |
| @@ -227,7 +233,7 @@ export default { | @@ -227,7 +233,7 @@ export default { | ||
| 227 | setTimeout(() => { | 233 | setTimeout(() => { |
| 228 | 234 | ||
| 229 | let snapId = deviceId + "_" + channelId; | 235 | let snapId = deviceId + "_" + channelId; |
| 230 | - that.loadSnap[snapId] = 0; | 236 | + that.loadSnap[deviceId + channelId] = 0; |
| 231 | that.getSnapErrorEvent(snapId) | 237 | that.getSnapErrorEvent(snapId) |
| 232 | }, 5000) | 238 | }, 5000) |
| 233 | that.$refs.devicePlayer.openDialog("media", deviceId, channelId, { | 239 | that.$refs.devicePlayer.openDialog("media", deviceId, channelId, { |
| @@ -269,19 +275,24 @@ export default { | @@ -269,19 +275,24 @@ export default { | ||
| 269 | }); | 275 | }); |
| 270 | }, | 276 | }, |
| 271 | getSnap: function (row) { | 277 | getSnap: function (row) { |
| 272 | - return '/static/snap/' + row.deviceId + '_' + row.channelId + '.jpg' | 278 | + let url = (process.env.NODE_ENV === 'development'? "debug": "") + '/api/device/query/snap/' + row.deviceId + '/' + row.channelId |
| 279 | + return url | ||
| 273 | }, | 280 | }, |
| 274 | - getSnapErrorEvent: function (id) { | 281 | + getBigSnap: function (row) { |
| 282 | + return [this.getSnap(row)] | ||
| 283 | + }, | ||
| 284 | + getSnapErrorEvent: function (deviceId, channelId) { | ||
| 275 | 285 | ||
| 276 | - if (typeof (this.loadSnap[id]) != "undefined") { | ||
| 277 | - console.log("下载截图" + this.loadSnap[id]) | ||
| 278 | - if (this.loadSnap[id] > 5) { | ||
| 279 | - delete this.loadSnap[id]; | 286 | + if (typeof (this.loadSnap[deviceId + channelId]) != "undefined") { |
| 287 | + console.log("下载截图" + this.loadSnap[deviceId + channelId]) | ||
| 288 | + if (this.loadSnap[deviceId + channelId] > 5) { | ||
| 289 | + delete this.loadSnap[deviceId + channelId]; | ||
| 280 | return; | 290 | return; |
| 281 | } | 291 | } |
| 282 | setTimeout(() => { | 292 | setTimeout(() => { |
| 283 | - this.loadSnap[id]++ | ||
| 284 | - document.getElementById(id).setAttribute("src", '/static/snap/' + id + '.jpg?' + new Date().getTime()) | 293 | + let url = (process.env.NODE_ENV === 'development'? "debug": "") + '/api/device/query/snap/' + deviceId + '/' + channelId |
| 294 | + this.loadSnap[deviceId + channelId]++ | ||
| 295 | + document.getElementById(deviceId + channelId).setAttribute("src", url + '?' + new Date().getTime()) | ||
| 285 | }, 1000) | 296 | }, 1000) |
| 286 | 297 | ||
| 287 | } | 298 | } |
| @@ -342,12 +353,8 @@ export default { | @@ -342,12 +353,8 @@ export default { | ||
| 342 | console.log(JSON.stringify(res)); | 353 | console.log(JSON.stringify(res)); |
| 343 | }); | 354 | }); |
| 344 | }, | 355 | }, |
| 345 | - autoListChange: function () { | ||
| 346 | - if (this.autoList) { | ||
| 347 | - this.updateLooper = setInterval(this.initData, 1500); | ||
| 348 | - } else { | ||
| 349 | - window.clearInterval(this.updateLooper); | ||
| 350 | - } | 356 | + refresh: function () { |
| 357 | + this.initData(); | ||
| 351 | } | 358 | } |
| 352 | 359 | ||
| 353 | } | 360 | } |
web_src/src/components/common/DeviceTree.vue
| @@ -84,22 +84,34 @@ export default { | @@ -84,22 +84,34 @@ export default { | ||
| 84 | }else { | 84 | }else { |
| 85 | resolve([]) | 85 | resolve([]) |
| 86 | } | 86 | } |
| 87 | + }, (list)=>{ | ||
| 88 | + console.log("设备加载完成") | ||
| 87 | }, (error)=>{ | 89 | }, (error)=>{ |
| 88 | 90 | ||
| 89 | }) | 91 | }) |
| 90 | } | 92 | } |
| 91 | if (node.level === 1) { | 93 | if (node.level === 1) { |
| 92 | - this.deviceService.getAllChannel(true, true, node.data.id, (catalogData) => { | ||
| 93 | - this.deviceService.getAllChannel(false, true, node.data.id, (channelData) => { | ||
| 94 | - let data = catalogData.concat(channelData) | ||
| 95 | - this.channelDataHandler(data, resolve) | 94 | + let channelArray = [] |
| 95 | + this.deviceService.getAllChannel(true, true, node.data.id, catalogData =>{ | ||
| 96 | + channelArray = channelArray.concat(catalogData) | ||
| 97 | + this.channelDataHandler(channelArray, resolve) | ||
| 98 | + },(endCatalogData) => { | ||
| 99 | + this.deviceService.getAllChannel(false, true, node.data.id, channelData => { | ||
| 100 | + channelArray = channelArray.concat(channelData) | ||
| 101 | + this.channelDataHandler(channelArray, resolve) | ||
| 102 | + }, endChannelList => { | ||
| 103 | + | ||
| 96 | }) | 104 | }) |
| 97 | }) | 105 | }) |
| 98 | }else if (node.level > 1){ | 106 | }else if (node.level > 1){ |
| 107 | + let channelArray = [] | ||
| 99 | this.deviceService.getAllSubChannel(true, node.data.deviceId, node.data.id, (catalogData)=>{ | 108 | this.deviceService.getAllSubChannel(true, node.data.deviceId, node.data.id, (catalogData)=>{ |
| 109 | + channelArray = channelArray.concat(catalogData) | ||
| 110 | + this.channelDataHandler(channelArray, resolve) | ||
| 111 | + }, (endCatalogData)=>{ | ||
| 100 | this.deviceService.getAllSubChannel(false, node.data.deviceId, node.data.id, (channelData)=>{ | 112 | this.deviceService.getAllSubChannel(false, node.data.deviceId, node.data.id, (channelData)=>{ |
| 101 | - let data = catalogData.concat(channelData) | ||
| 102 | - this.channelDataHandler(data, resolve) | 113 | + channelArray = channelArray.concat(channelData) |
| 114 | + this.channelDataHandler(channelArray, resolve) | ||
| 103 | }) | 115 | }) |
| 104 | }) | 116 | }) |
| 105 | } | 117 | } |
web_src/src/components/common/jessibuca.vue
| @@ -23,11 +23,11 @@ | @@ -23,11 +23,11 @@ | ||
| 23 | </template> | 23 | </template> |
| 24 | 24 | ||
| 25 | <script> | 25 | <script> |
| 26 | +let jessibucaPlayer = {}; | ||
| 26 | export default { | 27 | export default { |
| 27 | name: 'jessibuca', | 28 | name: 'jessibuca', |
| 28 | data() { | 29 | data() { |
| 29 | return { | 30 | return { |
| 30 | - jessibuca: null, | ||
| 31 | playing: false, | 31 | playing: false, |
| 32 | isNotMute: false, | 32 | isNotMute: false, |
| 33 | quieting: false, | 33 | quieting: false, |
| @@ -49,6 +49,7 @@ export default { | @@ -49,6 +49,7 @@ export default { | ||
| 49 | window.onerror = (msg) => { | 49 | window.onerror = (msg) => { |
| 50 | // console.error(msg) | 50 | // console.error(msg) |
| 51 | }; | 51 | }; |
| 52 | + console.log(this._uid) | ||
| 52 | let paramUrl = decodeURIComponent(this.$route.params.url) | 53 | let paramUrl = decodeURIComponent(this.$route.params.url) |
| 53 | this.$nextTick(() => { | 54 | this.$nextTick(() => { |
| 54 | this.updatePlayerDomSize() | 55 | this.updatePlayerDomSize() |
| @@ -88,7 +89,7 @@ export default { | @@ -88,7 +89,7 @@ export default { | ||
| 88 | let options = {}; | 89 | let options = {}; |
| 89 | console.log("hasAudio " + this.hasAudio) | 90 | console.log("hasAudio " + this.hasAudio) |
| 90 | 91 | ||
| 91 | - this.jessibuca = new window.Jessibuca(Object.assign( | 92 | + jessibucaPlayer[this._uid] = new window.Jessibuca(Object.assign( |
| 92 | { | 93 | { |
| 93 | container: this.$refs.container, | 94 | container: this.$refs.container, |
| 94 | videoBuffer: 0.2, // 最大缓冲时长,单位秒 | 95 | videoBuffer: 0.2, // 最大缓冲时长,单位秒 |
| @@ -117,70 +118,70 @@ export default { | @@ -117,70 +118,70 @@ export default { | ||
| 117 | }, | 118 | }, |
| 118 | options | 119 | options |
| 119 | )); | 120 | )); |
| 120 | - | 121 | + let jessibuca = jessibucaPlayer[this._uid]; |
| 121 | let _this = this; | 122 | let _this = this; |
| 122 | - this.jessibuca.on("load", function () { | 123 | + jessibuca.on("load", function () { |
| 123 | console.log("on load init"); | 124 | console.log("on load init"); |
| 124 | }); | 125 | }); |
| 125 | 126 | ||
| 126 | - this.jessibuca.on("log", function (msg) { | 127 | + jessibuca.on("log", function (msg) { |
| 127 | console.log("on log", msg); | 128 | console.log("on log", msg); |
| 128 | }); | 129 | }); |
| 129 | - this.jessibuca.on("record", function (msg) { | 130 | + jessibuca.on("record", function (msg) { |
| 130 | console.log("on record:", msg); | 131 | console.log("on record:", msg); |
| 131 | }); | 132 | }); |
| 132 | - this.jessibuca.on("pause", function () { | 133 | + jessibuca.on("pause", function () { |
| 133 | _this.playing = false; | 134 | _this.playing = false; |
| 134 | }); | 135 | }); |
| 135 | - this.jessibuca.on("play", function () { | 136 | + jessibuca.on("play", function () { |
| 136 | _this.playing = true; | 137 | _this.playing = true; |
| 137 | }); | 138 | }); |
| 138 | - this.jessibuca.on("fullscreen", function (msg) { | 139 | + jessibuca.on("fullscreen", function (msg) { |
| 139 | console.log("on fullscreen", msg); | 140 | console.log("on fullscreen", msg); |
| 140 | _this.fullscreen = msg | 141 | _this.fullscreen = msg |
| 141 | }); | 142 | }); |
| 142 | 143 | ||
| 143 | - this.jessibuca.on("mute", function (msg) { | 144 | + jessibuca.on("mute", function (msg) { |
| 144 | console.log("on mute", msg); | 145 | console.log("on mute", msg); |
| 145 | _this.isNotMute = !msg; | 146 | _this.isNotMute = !msg; |
| 146 | }); | 147 | }); |
| 147 | - this.jessibuca.on("audioInfo", function (msg) { | 148 | + jessibuca.on("audioInfo", function (msg) { |
| 148 | // console.log("audioInfo", msg); | 149 | // console.log("audioInfo", msg); |
| 149 | }); | 150 | }); |
| 150 | 151 | ||
| 151 | - this.jessibuca.on("videoInfo", function (msg) { | 152 | + jessibuca.on("videoInfo", function (msg) { |
| 152 | // this.videoInfo = msg; | 153 | // this.videoInfo = msg; |
| 153 | console.log("videoInfo", msg); | 154 | console.log("videoInfo", msg); |
| 154 | 155 | ||
| 155 | }); | 156 | }); |
| 156 | 157 | ||
| 157 | - this.jessibuca.on("bps", function (bps) { | 158 | + jessibuca.on("bps", function (bps) { |
| 158 | // console.log('bps', bps); | 159 | // console.log('bps', bps); |
| 159 | 160 | ||
| 160 | }); | 161 | }); |
| 161 | let _ts = 0; | 162 | let _ts = 0; |
| 162 | - this.jessibuca.on("timeUpdate", function (ts) { | 163 | + jessibuca.on("timeUpdate", function (ts) { |
| 163 | // console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts); | 164 | // console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts); |
| 164 | _ts = ts; | 165 | _ts = ts; |
| 165 | }); | 166 | }); |
| 166 | 167 | ||
| 167 | - this.jessibuca.on("videoInfo", function (info) { | 168 | + jessibuca.on("videoInfo", function (info) { |
| 168 | console.log("videoInfo", info); | 169 | console.log("videoInfo", info); |
| 169 | }); | 170 | }); |
| 170 | 171 | ||
| 171 | - this.jessibuca.on("error", function (error) { | 172 | + jessibuca.on("error", function (error) { |
| 172 | console.log("error", error); | 173 | console.log("error", error); |
| 173 | }); | 174 | }); |
| 174 | 175 | ||
| 175 | - this.jessibuca.on("timeout", function () { | 176 | + jessibuca.on("timeout", function () { |
| 176 | console.log("timeout"); | 177 | console.log("timeout"); |
| 177 | }); | 178 | }); |
| 178 | 179 | ||
| 179 | - this.jessibuca.on('start', function () { | 180 | + jessibuca.on('start', function () { |
| 180 | console.log('start'); | 181 | console.log('start'); |
| 181 | }) | 182 | }) |
| 182 | 183 | ||
| 183 | - this.jessibuca.on("performance", function (performance) { | 184 | + jessibuca.on("performance", function (performance) { |
| 184 | let show = "卡顿"; | 185 | let show = "卡顿"; |
| 185 | if (performance === 2) { | 186 | if (performance === 2) { |
| 186 | show = "非常流畅"; | 187 | show = "非常流畅"; |
| @@ -189,25 +190,25 @@ export default { | @@ -189,25 +190,25 @@ export default { | ||
| 189 | } | 190 | } |
| 190 | _this.performance = show; | 191 | _this.performance = show; |
| 191 | }); | 192 | }); |
| 192 | - this.jessibuca.on('buffer', function (buffer) { | 193 | + jessibuca.on('buffer', function (buffer) { |
| 193 | // console.log('buffer', buffer); | 194 | // console.log('buffer', buffer); |
| 194 | }) | 195 | }) |
| 195 | 196 | ||
| 196 | - this.jessibuca.on('stats', function (stats) { | 197 | + jessibuca.on('stats', function (stats) { |
| 197 | // console.log('stats', stats); | 198 | // console.log('stats', stats); |
| 198 | }) | 199 | }) |
| 199 | 200 | ||
| 200 | - this.jessibuca.on('kBps', function (kBps) { | 201 | + jessibuca.on('kBps', function (kBps) { |
| 201 | _this.kBps = Math.round(kBps); | 202 | _this.kBps = Math.round(kBps); |
| 202 | }); | 203 | }); |
| 203 | 204 | ||
| 204 | // 显示时间戳 PTS | 205 | // 显示时间戳 PTS |
| 205 | - this.jessibuca.on('videoFrame', function () { | 206 | + jessibuca.on('videoFrame', function () { |
| 206 | 207 | ||
| 207 | }) | 208 | }) |
| 208 | 209 | ||
| 209 | // | 210 | // |
| 210 | - this.jessibuca.on('metadata', function () { | 211 | + jessibuca.on('metadata', function () { |
| 211 | 212 | ||
| 212 | }); | 213 | }); |
| 213 | }, | 214 | }, |
| @@ -216,40 +217,40 @@ export default { | @@ -216,40 +217,40 @@ export default { | ||
| 216 | }, | 217 | }, |
| 217 | play: function (url) { | 218 | play: function (url) { |
| 218 | console.log(url) | 219 | console.log(url) |
| 219 | - if (this.jessibuca) { | 220 | + if (jessibucaPlayer[this._uid]) { |
| 220 | this.destroy(); | 221 | this.destroy(); |
| 221 | } | 222 | } |
| 222 | this.create(); | 223 | this.create(); |
| 223 | - this.jessibuca.on("play", () => { | 224 | + jessibucaPlayer[this._uid].on("play", () => { |
| 224 | this.playing = true; | 225 | this.playing = true; |
| 225 | this.loaded = true; | 226 | this.loaded = true; |
| 226 | - this.quieting = this.jessibuca.quieting; | 227 | + this.quieting = jessibuca.quieting; |
| 227 | }); | 228 | }); |
| 228 | - if (this.jessibuca.hasLoaded()) { | ||
| 229 | - this.jessibuca.play(url); | 229 | + if (jessibucaPlayer[this._uid].hasLoaded()) { |
| 230 | + jessibucaPlayer[this._uid].play(url); | ||
| 230 | } else { | 231 | } else { |
| 231 | - this.jessibuca.on("load", () => { | 232 | + jessibucaPlayer[this._uid].on("load", () => { |
| 232 | console.log("load 播放") | 233 | console.log("load 播放") |
| 233 | - this.jessibuca.play(url); | 234 | + jessibucaPlayer[this._uid].play(url); |
| 234 | }); | 235 | }); |
| 235 | } | 236 | } |
| 236 | }, | 237 | }, |
| 237 | pause: function () { | 238 | pause: function () { |
| 238 | - if (this.jessibuca) { | ||
| 239 | - this.jessibuca.pause(); | 239 | + if (jessibucaPlayer[this._uid]) { |
| 240 | + jessibucaPlayer[this._uid].pause(); | ||
| 240 | } | 241 | } |
| 241 | this.playing = false; | 242 | this.playing = false; |
| 242 | this.err = ""; | 243 | this.err = ""; |
| 243 | this.performance = ""; | 244 | this.performance = ""; |
| 244 | }, | 245 | }, |
| 245 | destroy: function () { | 246 | destroy: function () { |
| 246 | - if (this.jessibuca) { | ||
| 247 | - this.jessibuca.destroy(); | 247 | + if (jessibucaPlayer[this._uid]) { |
| 248 | + jessibucaPlayer[this._uid].destroy(); | ||
| 248 | } | 249 | } |
| 249 | if (document.getElementById("buttonsBox") == null) { | 250 | if (document.getElementById("buttonsBox") == null) { |
| 250 | this.$refs.container.appendChild(this.btnDom) | 251 | this.$refs.container.appendChild(this.btnDom) |
| 251 | } | 252 | } |
| 252 | - this.jessibuca = null; | 253 | + jessibucaPlayer[this._uid] = null; |
| 253 | this.playing = false; | 254 | this.playing = false; |
| 254 | this.err = ""; | 255 | this.err = ""; |
| 255 | this.performance = ""; | 256 | this.performance = ""; |
| @@ -262,7 +263,7 @@ export default { | @@ -262,7 +263,7 @@ export default { | ||
| 262 | }, | 263 | }, |
| 263 | fullscreenSwich: function () { | 264 | fullscreenSwich: function () { |
| 264 | let isFull = this.isFullscreen() | 265 | let isFull = this.isFullscreen() |
| 265 | - this.jessibuca.setFullscreen(!isFull) | 266 | + jessibucaPlayer[this._uid].setFullscreen(!isFull) |
| 266 | this.fullscreen = !isFull; | 267 | this.fullscreen = !isFull; |
| 267 | }, | 268 | }, |
| 268 | isFullscreen: function () { | 269 | isFullscreen: function () { |
| @@ -273,8 +274,8 @@ export default { | @@ -273,8 +274,8 @@ export default { | ||
| 273 | } | 274 | } |
| 274 | }, | 275 | }, |
| 275 | destroyed() { | 276 | destroyed() { |
| 276 | - if (this.jessibuca) { | ||
| 277 | - this.jessibuca.destroy(); | 277 | + if (jessibucaPlayer[this._uid]) { |
| 278 | + jessibucaPlayer[this._uid].destroy(); | ||
| 278 | } | 279 | } |
| 279 | this.playing = false; | 280 | this.playing = false; |
| 280 | this.loaded = false; | 281 | this.loaded = false; |
web_src/src/components/control.vue
| @@ -235,10 +235,8 @@ | @@ -235,10 +235,8 @@ | ||
| 235 | <el-table-column prop="local_ip" label="本地"></el-table-column> | 235 | <el-table-column prop="local_ip" label="本地"></el-table-column> |
| 236 | <el-table-column prop="typeid" label="类型"></el-table-column> | 236 | <el-table-column prop="typeid" label="类型"></el-table-column> |
| 237 | <el-table-column align="right"> | 237 | <el-table-column align="right"> |
| 238 | - <template slot="header" slot-scope="scope"> | ||
| 239 | - <el-button icon="el-icon-refresh-right" circle @click="getAllSession()"></el-button> | ||
| 240 | - </template> | ||
| 241 | - <template slot-scope="scope"> | 238 | + <template v-slot:default="scope"> |
| 239 | + <el-button size="mini" icon="el-icon-refresh-right" circle @click="getAllSession()"></el-button> | ||
| 242 | <el-button @click.native.prevent="deleteRow(scope.$index, allSessionData)" type="text" size="small">移除 | 240 | <el-button @click.native.prevent="deleteRow(scope.$index, allSessionData)" type="text" size="small">移除 |
| 243 | </el-button> | 241 | </el-button> |
| 244 | </template> | 242 | </template> |
web_src/src/components/dialog/deviceEdit.vue
| @@ -39,6 +39,12 @@ | @@ -39,6 +39,12 @@ | ||
| 39 | <el-form-item label="语音发送通道" prop="name"> | 39 | <el-form-item label="语音发送通道" prop="name"> |
| 40 | <el-input v-model="form.audioChannelForReceive" clearable></el-input> | 40 | <el-input v-model="form.audioChannelForReceive" clearable></el-input> |
| 41 | </el-form-item> | 41 | </el-form-item> |
| 42 | + <el-form-item label="地理坐标系" prop="geoCoordSys" > | ||
| 43 | + <el-select v-model="form.geoCoordSys" style="float: left; width: 100%" > | ||
| 44 | + <el-option key="WGS84" label="WGS84" value="WGS84"></el-option> | ||
| 45 | + <el-option key="GCJ02" label="GCJ02" value="GCJ02"></el-option> | ||
| 46 | + </el-select> | ||
| 47 | + </el-form-item> | ||
| 42 | <el-form-item label="目录订阅" title="0为取消订阅" prop="subscribeCycleForCatalog" > | 48 | <el-form-item label="目录订阅" title="0为取消订阅" prop="subscribeCycleForCatalog" > |
| 43 | <el-input v-model="form.subscribeCycleForCatalog" clearable ></el-input> | 49 | <el-input v-model="form.subscribeCycleForCatalog" clearable ></el-input> |
| 44 | </el-form-item> | 50 | </el-form-item> |
web_src/src/components/dialog/devicePlayer.vue
| @@ -3,9 +3,23 @@ | @@ -3,9 +3,23 @@ | ||
| 3 | 3 | ||
| 4 | <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" @close="close()"> | 4 | <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" @close="close()"> |
| 5 | <!-- <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> --> | 5 | <!-- <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> --> |
| 6 | - <player ref="videoPlayer" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" :height="false" :hasAudio="hasAudio" fluent autoplay live ></player> | 6 | + <div style="width: 100%; height: 100%"> |
| 7 | + <el-tabs type="card" :stretch="true" v-model="activePlayer" @tab-click="changePlayer" v-if="Object.keys(this.player).length > 1"> | ||
| 8 | + <el-tab-pane label="Jessibuca" name="jessibuca"> | ||
| 9 | + <jessibucaPlayer v-if="activePlayer === 'jessibuca'" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></jessibucaPlayer> | ||
| 10 | + </el-tab-pane> | ||
| 11 | + <el-tab-pane label="WebRTC" name="webRTC"> | ||
| 12 | + <rtc-player v-if="activePlayer === 'webRTC'" ref="webRTC" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></rtc-player> | ||
| 13 | + </el-tab-pane> | ||
| 14 | + <el-tab-pane label="h265web">h265web敬请期待</el-tab-pane> | ||
| 15 | + <el-tab-pane label="wsPlayer">wsPlayer 敬请期待</el-tab-pane> | ||
| 16 | + </el-tabs> | ||
| 17 | + <jessibucaPlayer v-if="Object.keys(this.player).length == 1 && this.player.jessibuca" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></jessibucaPlayer> | ||
| 18 | + <rtc-player v-if="Object.keys(this.player).length == 1 && this.player.webRTC" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></rtc-player> | ||
| 19 | + | ||
| 20 | + </div> | ||
| 7 | <div id="shared" style="text-align: right; margin-top: 1rem;"> | 21 | <div id="shared" style="text-align: right; margin-top: 1rem;"> |
| 8 | - <el-tabs v-model="tabActiveName" @tab-click="tabHandleClick"> | 22 | + <el-tabs v-model="tabActiveName" @tab-click="tabHandleClick" > |
| 9 | <el-tab-pane label="实时视频" name="media"> | 23 | <el-tab-pane label="实时视频" name="media"> |
| 10 | <div style="margin-bottom: 0.5rem;"> | 24 | <div style="margin-bottom: 0.5rem;"> |
| 11 | <!-- <el-button type="primary" size="small" @click="playRecord(true, '')">播放</el-button>--> | 25 | <!-- <el-button type="primary" size="small" @click="playRecord(true, '')">播放</el-button>--> |
| @@ -31,10 +45,100 @@ | @@ -31,10 +45,100 @@ | ||
| 31 | <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;"> | 45 | <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;"> |
| 32 | <span style="width: 5rem; line-height: 2.5rem; text-align: right;">资源地址:</span> | 46 | <span style="width: 5rem; line-height: 2.5rem; text-align: right;">资源地址:</span> |
| 33 | <el-input v-model="getPlayerShared.sharedRtmp" :disabled="true" > | 47 | <el-input v-model="getPlayerShared.sharedRtmp" :disabled="true" > |
| 34 | - <template slot="append"> | ||
| 35 | - <i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedRtmp" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i> | ||
| 36 | - </template> | 48 | + <el-button slot="append" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedRtmp" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button> |
| 49 | + <el-dropdown slot="prepend" v-if="streamInfo" trigger="click" @command="copyUrl"> | ||
| 50 | + <el-button > | ||
| 51 | + 更多地址<i class="el-icon-arrow-down el-icon--right"></i> | ||
| 52 | + </el-button> | ||
| 53 | + <el-dropdown-menu slot="dropdown" > | ||
| 54 | + <el-dropdown-item :command="streamInfo.flv"> | ||
| 55 | + <el-tag >FLV:</el-tag> | ||
| 56 | + <span>{{ streamInfo.flv }}</span> | ||
| 57 | + </el-dropdown-item> | ||
| 58 | + <el-dropdown-item :command="streamInfo.https_flv"> | ||
| 59 | + <el-tag >FLV(https):</el-tag> | ||
| 60 | + <span>{{ streamInfo.https_flv }}</span> | ||
| 61 | + </el-dropdown-item> | ||
| 62 | + <el-dropdown-item :command="streamInfo.ws_flv"> | ||
| 63 | + <el-tag >FLV(ws):</el-tag> | ||
| 64 | + <span >{{ streamInfo.ws_flv }}</span> | ||
| 65 | + </el-dropdown-item> | ||
| 66 | + <el-dropdown-item :command="streamInfo.wss_flv"> | ||
| 67 | + <el-tag >FLV(wss):</el-tag> | ||
| 68 | + <span>{{ streamInfo.wss_flv }}</span> | ||
| 69 | + </el-dropdown-item> | ||
| 70 | + <el-dropdown-item :command="streamInfo.fmp4"> | ||
| 71 | + <el-tag >FMP4:</el-tag> | ||
| 72 | + <span>{{ streamInfo.fmp4 }}</span> | ||
| 73 | + </el-dropdown-item> | ||
| 74 | + <el-dropdown-item :command="streamInfo.https_fmp4"> | ||
| 75 | + <el-tag >FMP4(https):</el-tag> | ||
| 76 | + <span>{{ streamInfo.https_fmp4 }}</span> | ||
| 77 | + </el-dropdown-item> | ||
| 78 | + <el-dropdown-item :command="streamInfo.ws_fmp4"> | ||
| 79 | + <el-tag >FMP4(ws):</el-tag> | ||
| 80 | + <span>{{ streamInfo.ws_fmp4 }}</span> | ||
| 81 | + </el-dropdown-item> | ||
| 82 | + <el-dropdown-item :command="streamInfo.wss_fmp4"> | ||
| 83 | + <el-tag >FMP4(wss):</el-tag> | ||
| 84 | + <span>{{ streamInfo.wss_fmp4 }}</span> | ||
| 85 | + </el-dropdown-item> | ||
| 86 | + <el-dropdown-item :command="streamInfo.hls"> | ||
| 87 | + <el-tag>HLS:</el-tag> | ||
| 88 | + <span>{{ streamInfo.hls }}</span> | ||
| 89 | + </el-dropdown-item> | ||
| 90 | + <el-dropdown-item :command="streamInfo.https_hls"> | ||
| 91 | + <el-tag >HLS(https):</el-tag> | ||
| 92 | + <span>{{ streamInfo.https_hls }}</span> | ||
| 93 | + </el-dropdown-item> | ||
| 94 | + <el-dropdown-item :command="streamInfo.ws_hls"> | ||
| 95 | + <el-tag >HLS(ws):</el-tag> | ||
| 96 | + <span>{{ streamInfo.ws_hls }}</span> | ||
| 97 | + </el-dropdown-item> | ||
| 98 | + <el-dropdown-item :command="streamInfo.wss_hls"> | ||
| 99 | + <el-tag >HLS(wss):</el-tag> | ||
| 100 | + <span>{{ streamInfo.wss_hls }}</span> | ||
| 101 | + </el-dropdown-item> | ||
| 102 | + <el-dropdown-item :command="streamInfo.ts"> | ||
| 103 | + <el-tag>TS:</el-tag> | ||
| 104 | + <span>{{ streamInfo.ts }}</span> | ||
| 105 | + </el-dropdown-item> | ||
| 106 | + <el-dropdown-item :command="streamInfo.https_ts"> | ||
| 107 | + <el-tag>TS(https):</el-tag> | ||
| 108 | + <span>{{ streamInfo.https_ts }}</span> | ||
| 109 | + </el-dropdown-item> | ||
| 110 | + <el-dropdown-item :command="streamInfo.ws_ts"> | ||
| 111 | + <el-tag>TS(ws):</el-tag> | ||
| 112 | + <span>{{ streamInfo.ws_ts }}</span> | ||
| 113 | + </el-dropdown-item> | ||
| 114 | + <el-dropdown-item :command="streamInfo.wss_ts"> | ||
| 115 | + <el-tag>TS(wss):</el-tag> | ||
| 116 | + <span>{{ streamInfo.wss_ts }}</span> | ||
| 117 | + </el-dropdown-item> | ||
| 118 | + <el-dropdown-item :command="streamInfo.rtc"> | ||
| 119 | + <el-tag >RTC:</el-tag> | ||
| 120 | + <span>{{ streamInfo.rtc }}</span> | ||
| 121 | + </el-dropdown-item> | ||
| 122 | + <el-dropdown-item :command="streamInfo.rtmp"> | ||
| 123 | + <el-tag >RTMP:</el-tag> | ||
| 124 | + <span>{{ streamInfo.rtmp }}</span> | ||
| 125 | + </el-dropdown-item> | ||
| 126 | + <el-dropdown-item :command="streamInfo.rtmps"> | ||
| 127 | + <el-tag >RTMPS:</el-tag> | ||
| 128 | + <span>{{ streamInfo.rtmps }}</span> | ||
| 129 | + </el-dropdown-item> | ||
| 130 | + <el-dropdown-item :command="streamInfo.rtsp"> | ||
| 131 | + <el-tag >RTSP:</el-tag> | ||
| 132 | + <span>{{ streamInfo.rtsp }}</span> | ||
| 133 | + </el-dropdown-item> | ||
| 134 | + <el-dropdown-item :command="streamInfo.rtsps"> | ||
| 135 | + <el-tag >RTSPS:</el-tag> | ||
| 136 | + <span>{{ streamInfo.rtsps }}</span> | ||
| 137 | + </el-dropdown-item> | ||
| 138 | + </el-dropdown-menu> | ||
| 139 | + </el-dropdown> | ||
| 37 | </el-input> | 140 | </el-input> |
| 141 | + | ||
| 38 | </div> | 142 | </div> |
| 39 | </el-tab-pane> | 143 | </el-tab-pane> |
| 40 | <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}--> | 144 | <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}--> |
| @@ -115,27 +219,27 @@ | @@ -115,27 +219,27 @@ | ||
| 115 | 219 | ||
| 116 | <div class="control-panel"> | 220 | <div class="control-panel"> |
| 117 | <el-button-group> | 221 | <el-button-group> |
| 118 | - <el-tag style="position :absolute; left: 0rem; top: 0rem; width: 5rem; text-align: center" size="medium" type="info">预置位编号</el-tag> | 222 | + <el-tag style="position :absolute; left: 0rem; top: 0rem; width: 5rem; text-align: center" size="medium">预置位编号</el-tag> |
| 119 | <el-input-number style="position: absolute; left: 5rem; top: 0rem; width: 6rem" size="mini" v-model="presetPos" controls-position="right" :precision="0" :step="1" :min="1" :max="255"></el-input-number> | 223 | <el-input-number style="position: absolute; left: 5rem; top: 0rem; width: 6rem" size="mini" v-model="presetPos" controls-position="right" :precision="0" :step="1" :min="1" :max="255"></el-input-number> |
| 120 | <el-button style="position: absolute; left: 11rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="presetPosition(129, presetPos)">设置</el-button> | 224 | <el-button style="position: absolute; left: 11rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="presetPosition(129, presetPos)">设置</el-button> |
| 121 | <el-button style="position: absolute; left: 27rem; top: 0rem; width: 5rem" size="mini" type="primary" icon="el-icon-place" @click="presetPosition(130, presetPos)">调用</el-button> | 225 | <el-button style="position: absolute; left: 27rem; top: 0rem; width: 5rem" size="mini" type="primary" icon="el-icon-place" @click="presetPosition(130, presetPos)">调用</el-button> |
| 122 | <el-button style="position: absolute; left: 16rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="presetPosition(131, presetPos)">删除</el-button> | 226 | <el-button style="position: absolute; left: 16rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="presetPosition(131, presetPos)">删除</el-button> |
| 123 | - <el-tag style="position :absolute; left: 0rem; top: 2.5rem; width: 5rem; text-align: center" size="medium" type="info">巡航速度</el-tag> | 227 | + <el-tag style="position :absolute; left: 0rem; top: 2.5rem; width: 5rem; text-align: center" size="medium">巡航速度</el-tag> |
| 124 | <el-input-number style="position: absolute; left: 5rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number> | 228 | <el-input-number style="position: absolute; left: 5rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number> |
| 125 | <el-button style="position: absolute; left: 11rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(134, cruisingGroup, cruisingSpeed)">设置</el-button> | 229 | <el-button style="position: absolute; left: 11rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(134, cruisingGroup, cruisingSpeed)">设置</el-button> |
| 126 | - <el-tag style="position :absolute; left: 16rem; top: 2.5rem; width: 5rem; text-align: center" size="medium" type="info">停留时间</el-tag> | 230 | + <el-tag style="position :absolute; left: 16rem; top: 2.5rem; width: 5rem; text-align: center" size="medium">停留时间</el-tag> |
| 127 | <el-input-number style="position: absolute; left: 21rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingTime" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number> | 231 | <el-input-number style="position: absolute; left: 21rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingTime" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number> |
| 128 | <el-button style="position: absolute; left: 27rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-timer" @click="setSpeedOrTime(135, cruisingGroup, cruisingTime)">设置</el-button> | 232 | <el-button style="position: absolute; left: 27rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-timer" @click="setSpeedOrTime(135, cruisingGroup, cruisingTime)">设置</el-button> |
| 129 | - <el-tag style="position :absolute; left: 0rem; top: 4.5rem; width: 5rem; text-align: center" size="medium" type="info">巡航组编号</el-tag> | 233 | + <el-tag style="position :absolute; left: 0rem; top: 4.5rem; width: 5rem; text-align: center" size="medium">巡航组编号</el-tag> |
| 130 | <el-input-number style="position: absolute; left: 5rem; top: 4.5rem; width: 6rem" size="mini" v-model="cruisingGroup" controls-position="right" :precision="0" :min="0" :max="255"></el-input-number> | 234 | <el-input-number style="position: absolute; left: 5rem; top: 4.5rem; width: 6rem" size="mini" v-model="cruisingGroup" controls-position="right" :precision="0" :min="0" :max="255"></el-input-number> |
| 131 | <el-button style="position: absolute; left: 11rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="setCommand(132, cruisingGroup, presetPos)">添加点</el-button> | 235 | <el-button style="position: absolute; left: 11rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="setCommand(132, cruisingGroup, presetPos)">添加点</el-button> |
| 132 | <el-button style="position: absolute; left: 16rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="setCommand(133, cruisingGroup, presetPos)">删除点</el-button> | 236 | <el-button style="position: absolute; left: 16rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="setCommand(133, cruisingGroup, presetPos)">删除点</el-button> |
| 133 | <el-button style="position: absolute; left: 21rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete" @click="setCommand(133, cruisingGroup, 0)">删除组</el-button> | 237 | <el-button style="position: absolute; left: 21rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete" @click="setCommand(133, cruisingGroup, 0)">删除组</el-button> |
| 134 | <el-button style="position: absolute; left: 27rem; top: 5rem; width: 5rem" size="mini" type="primary" icon="el-icon-video-camera-solid" @click="setCommand(136, cruisingGroup, 0)">巡航</el-button> | 238 | <el-button style="position: absolute; left: 27rem; top: 5rem; width: 5rem" size="mini" type="primary" icon="el-icon-video-camera-solid" @click="setCommand(136, cruisingGroup, 0)">巡航</el-button> |
| 135 | - <el-tag style="position :absolute; left: 0rem; top: 7rem; width: 5rem; text-align: center" size="medium" type="info">扫描速度</el-tag> | 239 | + <el-tag style="position :absolute; left: 0rem; top: 7rem; width: 5rem; text-align: center" size="medium">扫描速度</el-tag> |
| 136 | <el-input-number style="position: absolute; left: 5rem; top: 7rem; width: 6rem" size="mini" v-model="scanSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number> | 240 | <el-input-number style="position: absolute; left: 5rem; top: 7rem; width: 6rem" size="mini" v-model="scanSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number> |
| 137 | <el-button style="position: absolute; left: 11rem; top: 7rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(138, scanGroup, scanSpeed)">设置</el-button> | 241 | <el-button style="position: absolute; left: 11rem; top: 7rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(138, scanGroup, scanSpeed)">设置</el-button> |
| 138 | - <el-tag style="position :absolute; left: 0rem; top: 9rem; width: 5rem; text-align: center" size="medium" type="info">扫描组编号</el-tag> | 242 | + <el-tag style="position :absolute; left: 0rem; top: 9rem; width: 5rem; text-align: center" size="medium">扫描组编号</el-tag> |
| 139 | <el-input-number style="position: absolute; left: 5rem; top: 9rem; width: 6rem" size="mini" v-model="scanGroup" controls-position="right" :precision="0" :step="1" :min="0" :max="255"></el-input-number> | 243 | <el-input-number style="position: absolute; left: 5rem; top: 9rem; width: 6rem" size="mini" v-model="scanGroup" controls-position="right" :precision="0" :step="1" :min="0" :max="255"></el-input-number> |
| 140 | <el-button style="position: absolute; left: 11rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-left" @click="setCommand(137, scanGroup, 1)">左边界</el-button> | 244 | <el-button style="position: absolute; left: 11rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-left" @click="setCommand(137, scanGroup, 1)">左边界</el-button> |
| 141 | <el-button style="position: absolute; left: 16rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-right" @click="setCommand(137, scanGroup, 2)">右边界</el-button> | 245 | <el-button style="position: absolute; left: 16rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-right" @click="setCommand(137, scanGroup, 2)">右边界</el-button> |
| @@ -172,26 +276,28 @@ | @@ -172,26 +276,28 @@ | ||
| 172 | </div> | 276 | </div> |
| 173 | 277 | ||
| 174 | </el-tab-pane> | 278 | </el-tab-pane> |
| 279 | + | ||
| 175 | </el-tabs> | 280 | </el-tabs> |
| 176 | </div> | 281 | </div> |
| 177 | </el-dialog> | 282 | </el-dialog> |
| 283 | + <recordDownload ref="recordDownload"></recordDownload> | ||
| 178 | </div> | 284 | </div> |
| 179 | </template> | 285 | </template> |
| 180 | 286 | ||
| 181 | <script> | 287 | <script> |
| 182 | -import player from '../dialog/rtcPlayer.vue' | 288 | +import rtcPlayer from '../dialog/rtcPlayer.vue' |
| 183 | // import LivePlayer from '@liveqing/liveplayer' | 289 | // import LivePlayer from '@liveqing/liveplayer' |
| 184 | // import player from '../dialog/easyPlayer.vue' | 290 | // import player from '../dialog/easyPlayer.vue' |
| 185 | -// import player from '../dialog/jessibuca.vue' | 291 | +import jessibucaPlayer from '../common/jessibuca.vue' |
| 292 | +import recordDownload from '../dialog/recordDownload.vue' | ||
| 186 | export default { | 293 | export default { |
| 187 | name: 'devicePlayer', | 294 | name: 'devicePlayer', |
| 188 | props: {}, | 295 | props: {}, |
| 189 | components: { | 296 | components: { |
| 190 | - player, | 297 | + jessibucaPlayer, rtcPlayer, recordDownload, |
| 191 | }, | 298 | }, |
| 192 | computed: { | 299 | computed: { |
| 193 | getPlayerShared: function () { | 300 | getPlayerShared: function () { |
| 194 | - | ||
| 195 | return { | 301 | return { |
| 196 | sharedUrl: window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl), | 302 | sharedUrl: window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl), |
| 197 | sharedIframe: '<iframe src="' + window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl) + '"></iframe>', | 303 | sharedIframe: '<iframe src="' + window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl) + '"></iframe>', |
| @@ -199,11 +305,22 @@ export default { | @@ -199,11 +305,22 @@ export default { | ||
| 199 | }; | 305 | }; |
| 200 | } | 306 | } |
| 201 | }, | 307 | }, |
| 202 | - created() {}, | 308 | + created() { |
| 309 | + console.log(this.player) | ||
| 310 | + if (Object.keys(this.player).length === 1) { | ||
| 311 | + this.activePlayer = Object.keys(this.player)[0] | ||
| 312 | + } | ||
| 313 | + }, | ||
| 203 | data() { | 314 | data() { |
| 204 | return { | 315 | return { |
| 205 | video: 'http://lndxyj.iqilu.com/public/upload/2019/10/14/8c001ea0c09cdc59a57829dabc8010fa.mp4', | 316 | video: 'http://lndxyj.iqilu.com/public/upload/2019/10/14/8c001ea0c09cdc59a57829dabc8010fa.mp4', |
| 206 | videoUrl: '', | 317 | videoUrl: '', |
| 318 | + activePlayer: "jessibuca", | ||
| 319 | + // 如何你只是用一种播放器,直接注释掉不用的部分即可 | ||
| 320 | + player: { | ||
| 321 | + jessibuca : ["ws_flv", "wss_flv"], | ||
| 322 | + webRTC: ["rtc", "rtc"], | ||
| 323 | + }, | ||
| 207 | videoHistory: { | 324 | videoHistory: { |
| 208 | date: '', | 325 | date: '', |
| 209 | searchHistoryResult: [] //媒体流历史记录搜索结果 | 326 | searchHistoryResult: [] //媒体流历史记录搜索结果 |
| @@ -241,6 +358,7 @@ export default { | @@ -241,6 +358,7 @@ export default { | ||
| 241 | seekTime: 0, | 358 | seekTime: 0, |
| 242 | recordStartTime: 0, | 359 | recordStartTime: 0, |
| 243 | showTimeText: "00:00:00", | 360 | showTimeText: "00:00:00", |
| 361 | + streamInfo: null, | ||
| 244 | }; | 362 | }; |
| 245 | }, | 363 | }, |
| 246 | methods: { | 364 | methods: { |
| @@ -250,7 +368,7 @@ export default { | @@ -250,7 +368,7 @@ export default { | ||
| 250 | that.tracks = []; | 368 | that.tracks = []; |
| 251 | that.tracksLoading = true; | 369 | that.tracksLoading = true; |
| 252 | that.tracksNotLoaded = false; | 370 | that.tracksNotLoaded = false; |
| 253 | - if (tab.name == "codec") { | 371 | + if (tab.name === "codec") { |
| 254 | this.$axios({ | 372 | this.$axios({ |
| 255 | method: 'get', | 373 | method: 'get', |
| 256 | url: '/zlm/' +this.mediaServerId+ '/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app='+ this.app +'&stream='+ this.streamId | 374 | url: '/zlm/' +this.mediaServerId+ '/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app='+ this.app +'&stream='+ this.streamId |
| @@ -269,6 +387,12 @@ export default { | @@ -269,6 +387,12 @@ export default { | ||
| 269 | }).catch(function (e) {}); | 387 | }).catch(function (e) {}); |
| 270 | } | 388 | } |
| 271 | }, | 389 | }, |
| 390 | + changePlayer: function (tab) { | ||
| 391 | + console.log(this.player[tab.name][0]) | ||
| 392 | + this.activePlayer = tab.name; | ||
| 393 | + this.videoUrl = this.streamInfo[this.player[tab.name][0]] | ||
| 394 | + console.log(this.videoUrl) | ||
| 395 | + }, | ||
| 272 | openDialog: function (tab, deviceId, channelId, param) { | 396 | openDialog: function (tab, deviceId, channelId, param) { |
| 273 | this.tabActiveName = tab; | 397 | this.tabActiveName = tab; |
| 274 | this.channelId = channelId; | 398 | this.channelId = channelId; |
| @@ -277,8 +401,8 @@ export default { | @@ -277,8 +401,8 @@ export default { | ||
| 277 | this.mediaServerId = ""; | 401 | this.mediaServerId = ""; |
| 278 | this.app = ""; | 402 | this.app = ""; |
| 279 | this.videoUrl = "" | 403 | this.videoUrl = "" |
| 280 | - if (!!this.$refs.videoPlayer) { | ||
| 281 | - this.$refs.videoPlayer.pause(); | 404 | + if (!!this.$refs[this.activePlayer]) { |
| 405 | + this.$refs[this.activePlayer].pause(); | ||
| 282 | } | 406 | } |
| 283 | switch (tab) { | 407 | switch (tab) { |
| 284 | case "media": | 408 | case "media": |
| @@ -303,44 +427,32 @@ export default { | @@ -303,44 +427,32 @@ export default { | ||
| 303 | console.log(val) | 427 | console.log(val) |
| 304 | }, | 428 | }, |
| 305 | play: function (streamInfo, hasAudio) { | 429 | play: function (streamInfo, hasAudio) { |
| 430 | + this.streamInfo = streamInfo; | ||
| 306 | this.hasAudio = hasAudio; | 431 | this.hasAudio = hasAudio; |
| 307 | this.isLoging = false; | 432 | this.isLoging = false; |
| 308 | - this.videoUrl = streamInfo.rtc; | ||
| 309 | - // this.videoUrl = this.getUrlByStreamInfo(streamInfo); | ||
| 310 | - this.streamId = streamInfo.streamId; | 433 | + // this.videoUrl = streamInfo.rtc; |
| 434 | + this.videoUrl = this.getUrlByStreamInfo(); | ||
| 435 | + this.streamId = streamInfo.stream; | ||
| 311 | this.app = streamInfo.app; | 436 | this.app = streamInfo.app; |
| 312 | this.mediaServerId = streamInfo.mediaServerId; | 437 | this.mediaServerId = streamInfo.mediaServerId; |
| 313 | this.playFromStreamInfo(false, streamInfo) | 438 | this.playFromStreamInfo(false, streamInfo) |
| 314 | }, | 439 | }, |
| 315 | - getUrlByStreamInfo(streamInfo){ | ||
| 316 | - let baseZlmApi = process.env.NODE_ENV === 'development'?`${location.host}/debug/zlm`:`${location.host}/zlm` | ||
| 317 | - // return `${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`; | ||
| 318 | - // return `http://${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`; | 440 | + getUrlByStreamInfo(){ |
| 319 | if (location.protocol === "https:") { | 441 | if (location.protocol === "https:") { |
| 320 | - if (streamInfo.wss_flv === null) { | ||
| 321 | - console.error("媒体服务器未配置ssl端口, 使用http端口") | ||
| 322 | - // this.$message({ | ||
| 323 | - // showClose: true, | ||
| 324 | - // message: '媒体服务器未配置ssl端口, ', | ||
| 325 | - // type: 'error' | ||
| 326 | - // }); | ||
| 327 | - return streamInfo.ws_flv | ||
| 328 | - }else { | ||
| 329 | - return streamInfo.wss_flv; | ||
| 330 | - } | ||
| 331 | - | 442 | + this.videoUrl = this.streamInfo[this.player[this.activePlayer][1]] |
| 332 | }else { | 443 | }else { |
| 333 | - return streamInfo.ws_flv; | 444 | + this.videoUrl = this.streamInfo[this.player[this.activePlayer][0]] |
| 334 | } | 445 | } |
| 446 | + return this.videoUrl; | ||
| 335 | 447 | ||
| 336 | }, | 448 | }, |
| 337 | coverPlay: function () { | 449 | coverPlay: function () { |
| 338 | var that = this; | 450 | var that = this; |
| 339 | this.coverPlaying = true; | 451 | this.coverPlaying = true; |
| 340 | - this.$refs.videoPlayer.pause() | 452 | + this.$refs[this.activePlayer].pause() |
| 341 | that.$axios({ | 453 | that.$axios({ |
| 342 | method: 'post', | 454 | method: 'post', |
| 343 | - url: '/api/play/convert/' + that.streamId | 455 | + url: '/api/gb_record/convert/' + that.streamId |
| 344 | }).then(function (res) { | 456 | }).then(function (res) { |
| 345 | if (res.data.code == 0) { | 457 | if (res.data.code == 0) { |
| 346 | that.convertKey = res.data.key; | 458 | that.convertKey = res.data.key; |
| @@ -369,7 +481,7 @@ export default { | @@ -369,7 +481,7 @@ export default { | ||
| 369 | }, | 481 | }, |
| 370 | convertStopClick: function() { | 482 | convertStopClick: function() { |
| 371 | this.convertStop(()=>{ | 483 | this.convertStop(()=>{ |
| 372 | - this.$refs.videoPlayer.play(this.videoUrl) | 484 | + this.$refs[this.activePlayer].play(this.videoUrl) |
| 373 | }); | 485 | }); |
| 374 | }, | 486 | }, |
| 375 | convertStop: function(callback) { | 487 | convertStop: function(callback) { |
| @@ -394,12 +506,12 @@ export default { | @@ -394,12 +506,12 @@ export default { | ||
| 394 | playFromStreamInfo: function (realHasAudio, streamInfo) { | 506 | playFromStreamInfo: function (realHasAudio, streamInfo) { |
| 395 | this.showVideoDialog = true; | 507 | this.showVideoDialog = true; |
| 396 | this.hasaudio = realHasAudio && this.hasaudio; | 508 | this.hasaudio = realHasAudio && this.hasaudio; |
| 397 | - this.$refs.videoPlayer.play(this.getUrlByStreamInfo(streamInfo)) | 509 | + this.$refs[this.activePlayer].play(this.getUrlByStreamInfo(streamInfo)) |
| 398 | }, | 510 | }, |
| 399 | close: function () { | 511 | close: function () { |
| 400 | console.log('关闭视频'); | 512 | console.log('关闭视频'); |
| 401 | - if (!!this.$refs.videoPlayer){ | ||
| 402 | - this.$refs.videoPlayer.pause(); | 513 | + if (!!this.$refs[this.activePlayer]){ |
| 514 | + this.$refs[this.activePlayer].pause(); | ||
| 403 | } | 515 | } |
| 404 | this.videoUrl = ''; | 516 | this.videoUrl = ''; |
| 405 | this.coverPlaying = false; | 517 | this.coverPlaying = false; |
| @@ -450,9 +562,19 @@ export default { | @@ -450,9 +562,19 @@ export default { | ||
| 450 | method: 'get', | 562 | method: 'get', |
| 451 | url: '/api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + startTime + '&endTime=' + endTime | 563 | url: '/api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + startTime + '&endTime=' + endTime |
| 452 | }).then(function (res) { | 564 | }).then(function (res) { |
| 453 | - // 处理时间信息 | ||
| 454 | - that.videoHistory.searchHistoryResult = res.data.recordList; | ||
| 455 | - that.recordsLoading = false; | 565 | + console.log(res) |
| 566 | + if(res.data.code === 0) { | ||
| 567 | + // 处理时间信息 | ||
| 568 | + that.videoHistory.searchHistoryResult = res.data.data.recordList; | ||
| 569 | + that.recordsLoading = false; | ||
| 570 | + }else { | ||
| 571 | + this.$message({ | ||
| 572 | + showClose: true, | ||
| 573 | + message: res.data.msg, | ||
| 574 | + type: "error", | ||
| 575 | + }); | ||
| 576 | + } | ||
| 577 | + | ||
| 456 | }).catch(function (e) { | 578 | }).catch(function (e) { |
| 457 | console.log(e.message); | 579 | console.log(e.message); |
| 458 | // that.videoHistory.searchHistoryResult = falsificationData.recordData; | 580 | // that.videoHistory.searchHistoryResult = falsificationData.recordData; |
| @@ -474,8 +596,8 @@ export default { | @@ -474,8 +596,8 @@ export default { | ||
| 474 | console.log(this.seekTime) | 596 | console.log(this.seekTime) |
| 475 | if (that.streamId != "") { | 597 | if (that.streamId != "") { |
| 476 | that.stopPlayRecord(function () { | 598 | that.stopPlayRecord(function () { |
| 477 | - that.streamId = "", | ||
| 478 | - that.playRecord(row); | 599 | + that.streamId = ""; |
| 600 | + that.playRecord(row); | ||
| 479 | }) | 601 | }) |
| 480 | } else { | 602 | } else { |
| 481 | this.$axios({ | 603 | this.$axios({ |
| @@ -483,21 +605,22 @@ export default { | @@ -483,21 +605,22 @@ export default { | ||
| 483 | url: '/api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' + | 605 | url: '/api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' + |
| 484 | row.endTime | 606 | row.endTime |
| 485 | }).then(function (res) { | 607 | }).then(function (res) { |
| 486 | - var streamInfo = res.data; | ||
| 487 | - that.app = streamInfo.app; | ||
| 488 | - that.streamId = streamInfo.streamId; | ||
| 489 | - that.mediaServerId = streamInfo.mediaServerId; | ||
| 490 | - that.videoUrl = that.getUrlByStreamInfo(streamInfo); | 608 | + that.streamInfo = res.data; |
| 609 | + that.app = that.streamInfo.app; | ||
| 610 | + that.streamId = that.streamInfo.stream; | ||
| 611 | + that.mediaServerId = that.streamInfo.mediaServerId; | ||
| 612 | + that.ssrc = that.streamInfo.ssrc; | ||
| 613 | + that.videoUrl = that.getUrlByStreamInfo(); | ||
| 491 | that.recordPlay = true; | 614 | that.recordPlay = true; |
| 492 | }); | 615 | }); |
| 493 | } | 616 | } |
| 494 | }, | 617 | }, |
| 495 | stopPlayRecord: function (callback) { | 618 | stopPlayRecord: function (callback) { |
| 496 | - this.$refs.videoPlayer.pause(); | 619 | + this.$refs[this.activePlayer].pause(); |
| 497 | this.videoUrl = ''; | 620 | this.videoUrl = ''; |
| 498 | this.$axios({ | 621 | this.$axios({ |
| 499 | method: 'get', | 622 | method: 'get', |
| 500 | - url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId | 623 | + url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId |
| 501 | }).then(function (res) { | 624 | }).then(function (res) { |
| 502 | if (callback) callback() | 625 | if (callback) callback() |
| 503 | }); | 626 | }); |
| @@ -505,33 +628,47 @@ export default { | @@ -505,33 +628,47 @@ export default { | ||
| 505 | downloadRecord: function (row) { | 628 | downloadRecord: function (row) { |
| 506 | let that = this; | 629 | let that = this; |
| 507 | if (that.streamId != "") { | 630 | if (that.streamId != "") { |
| 508 | - that.stopDownloadRecord(function () { | ||
| 509 | - that.streamId = "", | ||
| 510 | - that.downloadRecord(row); | 631 | + that.stopDownloadRecord(function (res) { |
| 632 | + if (res.code == 0) { | ||
| 633 | + that.streamId = ""; | ||
| 634 | + that.downloadRecord(row); | ||
| 635 | + }else { | ||
| 636 | + this.$message({ | ||
| 637 | + showClose: true, | ||
| 638 | + message: res.data.msg, | ||
| 639 | + type: "error", | ||
| 640 | + }); | ||
| 641 | + } | ||
| 642 | + | ||
| 511 | }) | 643 | }) |
| 512 | } else { | 644 | } else { |
| 513 | this.$axios({ | 645 | this.$axios({ |
| 514 | method: 'get', | 646 | method: 'get', |
| 515 | - url: '/api/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' + | 647 | + url: '/api/gb_record/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' + |
| 516 | row.endTime + '&downloadSpeed=4' | 648 | row.endTime + '&downloadSpeed=4' |
| 517 | }).then(function (res) { | 649 | }).then(function (res) { |
| 518 | - var streamInfo = res.data; | ||
| 519 | - that.app = streamInfo.app; | ||
| 520 | - that.streamId = streamInfo.streamId; | ||
| 521 | - that.mediaServerId = streamInfo.mediaServerId; | ||
| 522 | - that.videoUrl = that.getUrlByStreamInfo(streamInfo); | ||
| 523 | - that.recordPlay = true; | 650 | + if (res.data.code == 0) { |
| 651 | + let streamInfo = res.data.data; | ||
| 652 | + that.recordPlay = false; | ||
| 653 | + that.$refs.recordDownload.openDialog(that.deviceId, that.channelId, streamInfo.app, streamInfo.stream, streamInfo.mediaServerId); | ||
| 654 | + }else { | ||
| 655 | + that.$message({ | ||
| 656 | + showClose: true, | ||
| 657 | + message: res.data.msg, | ||
| 658 | + type: "error", | ||
| 659 | + }); | ||
| 660 | + } | ||
| 524 | }); | 661 | }); |
| 525 | } | 662 | } |
| 526 | }, | 663 | }, |
| 527 | stopDownloadRecord: function (callback) { | 664 | stopDownloadRecord: function (callback) { |
| 528 | - this.$refs.videoPlayer.pause(); | 665 | + this.$refs[this.activePlayer].pause(); |
| 529 | this.videoUrl = ''; | 666 | this.videoUrl = ''; |
| 530 | this.$axios({ | 667 | this.$axios({ |
| 531 | method: 'get', | 668 | method: 'get', |
| 532 | - url: '/api/download/stop/' + this.deviceId + "/" + this.channelId | ||
| 533 | - }).then(function (res) { | ||
| 534 | - if (callback) callback() | 669 | + url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.streamId |
| 670 | + }).then((res)=> { | ||
| 671 | + if (callback) callback(res) | ||
| 535 | }); | 672 | }); |
| 536 | }, | 673 | }, |
| 537 | ptzCamera: function (command) { | 674 | ptzCamera: function (command) { |
| @@ -539,8 +676,6 @@ export default { | @@ -539,8 +676,6 @@ export default { | ||
| 539 | let that = this; | 676 | let that = this; |
| 540 | this.$axios({ | 677 | this.$axios({ |
| 541 | method: 'post', | 678 | method: 'post', |
| 542 | - // url: '/api/ptz/' + this.deviceId + '/' + this.channelId + '?leftRight=' + leftRight + '&upDown=' + upDown + | ||
| 543 | - // '&inOut=' + zoom + '&moveSpeed=50&zoomSpeed=50' | ||
| 544 | url: '/api/ptz/control/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&horizonSpeed=' + this.controSpeed + '&verticalSpeed=' + this.controSpeed + '&zoomSpeed=' + this.controSpeed | 679 | url: '/api/ptz/control/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&horizonSpeed=' + this.controSpeed + '&verticalSpeed=' + this.controSpeed + '&zoomSpeed=' + this.controSpeed |
| 545 | }).then(function (res) {}); | 680 | }).then(function (res) {}); |
| 546 | }, | 681 | }, |
| @@ -620,13 +755,21 @@ export default { | @@ -620,13 +755,21 @@ export default { | ||
| 620 | console.log(resultArray) | 755 | console.log(resultArray) |
| 621 | return resultArray; | 756 | return resultArray; |
| 622 | }, | 757 | }, |
| 758 | + copyUrl: function (dropdownItem){ | ||
| 759 | + console.log(dropdownItem) | ||
| 760 | + this.$copyText(dropdownItem).then((e)=> { | ||
| 761 | + this.$message.success("成功拷贝到粘贴板"); | ||
| 762 | + }, (e)=> { | ||
| 763 | + | ||
| 764 | + }) | ||
| 765 | + }, | ||
| 623 | gbPlay(){ | 766 | gbPlay(){ |
| 624 | console.log('前端控制:播放'); | 767 | console.log('前端控制:播放'); |
| 625 | this.$axios({ | 768 | this.$axios({ |
| 626 | method: 'get', | 769 | method: 'get', |
| 627 | url: '/api/playback/resume/' + this.streamId | 770 | url: '/api/playback/resume/' + this.streamId |
| 628 | }).then((res)=> { | 771 | }).then((res)=> { |
| 629 | - this.$refs.videoPlayer.play(this.videoUrl) | 772 | + this.$refs[this.activePlayer].play(this.videoUrl) |
| 630 | }); | 773 | }); |
| 631 | }, | 774 | }, |
| 632 | gbPause(){ | 775 | gbPause(){ |
| @@ -655,8 +798,13 @@ export default { | @@ -655,8 +798,13 @@ export default { | ||
| 655 | this.$axios({ | 798 | this.$axios({ |
| 656 | method: 'get', | 799 | method: 'get', |
| 657 | url: `/api/playback/seek/${this.streamId }/` + Math.floor(this.seekTime * val / 100000) | 800 | url: `/api/playback/seek/${this.streamId }/` + Math.floor(this.seekTime * val / 100000) |
| 658 | - }).then(function (res) {}); | ||
| 659 | - } | 801 | + }).then( (res)=> { |
| 802 | + setTimeout(()=>{ | ||
| 803 | + this.$refs[this.activePlayer].play(this.videoUrl) | ||
| 804 | + }, 600) | ||
| 805 | + }); | ||
| 806 | + }, | ||
| 807 | + | ||
| 660 | 808 | ||
| 661 | } | 809 | } |
| 662 | }; | 810 | }; |
web_src/src/components/dialog/rtcPlayer.vue
| @@ -7,11 +7,11 @@ | @@ -7,11 +7,11 @@ | ||
| 7 | </template> | 7 | </template> |
| 8 | 8 | ||
| 9 | <script> | 9 | <script> |
| 10 | +let webrtcPlayer = null; | ||
| 10 | export default { | 11 | export default { |
| 11 | name: 'rtcPlayer', | 12 | name: 'rtcPlayer', |
| 12 | data() { | 13 | data() { |
| 13 | return { | 14 | return { |
| 14 | - webrtcPlayer: null, | ||
| 15 | timer: null | 15 | timer: null |
| 16 | }; | 16 | }; |
| 17 | }, | 17 | }, |
| @@ -35,7 +35,7 @@ export default { | @@ -35,7 +35,7 @@ export default { | ||
| 35 | }, | 35 | }, |
| 36 | methods: { | 36 | methods: { |
| 37 | play: function (url) { | 37 | play: function (url) { |
| 38 | - this.webrtcPlayer = new ZLMRTCClient.Endpoint({ | 38 | + webrtcPlayer = new ZLMRTCClient.Endpoint({ |
| 39 | element: document.getElementById('webRtcPlayerBox'),// video 标签 | 39 | element: document.getElementById('webRtcPlayerBox'),// video 标签 |
| 40 | debug: true,// 是否打印日志 | 40 | debug: true,// 是否打印日志 |
| 41 | zlmsdpUrl: url,//流地址 | 41 | zlmsdpUrl: url,//流地址 |
| @@ -45,17 +45,17 @@ export default { | @@ -45,17 +45,17 @@ export default { | ||
| 45 | videoEnable: false, | 45 | videoEnable: false, |
| 46 | recvOnly: true, | 46 | recvOnly: true, |
| 47 | }) | 47 | }) |
| 48 | - this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,(e)=>{// ICE 协商出错 | 48 | + webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,(e)=>{// ICE 协商出错 |
| 49 | console.error('ICE 协商出错') | 49 | console.error('ICE 协商出错') |
| 50 | this.eventcallbacK("ICE ERROR", "ICE 协商出错") | 50 | this.eventcallbacK("ICE ERROR", "ICE 协商出错") |
| 51 | }); | 51 | }); |
| 52 | 52 | ||
| 53 | - this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(e)=>{//获取到了远端流,可以播放 | 53 | + webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(e)=>{//获取到了远端流,可以播放 |
| 54 | console.error('播放成功',e.streams) | 54 | console.error('播放成功',e.streams) |
| 55 | this.eventcallbacK("playing", "播放成功") | 55 | this.eventcallbacK("playing", "播放成功") |
| 56 | }); | 56 | }); |
| 57 | 57 | ||
| 58 | - this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e)=>{// offer anwser 交换失败 | 58 | + webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e)=>{// offer anwser 交换失败 |
| 59 | console.error('offer anwser 交换失败',e) | 59 | console.error('offer anwser 交换失败',e) |
| 60 | this.eventcallbacK("OFFER ANSWER ERROR ", "offer anwser 交换失败") | 60 | this.eventcallbacK("OFFER ANSWER ERROR ", "offer anwser 交换失败") |
| 61 | if (e.code ==-400 && e.msg=="流不存在"){ | 61 | if (e.code ==-400 && e.msg=="流不存在"){ |
| @@ -68,7 +68,7 @@ export default { | @@ -68,7 +68,7 @@ export default { | ||
| 68 | } | 68 | } |
| 69 | }); | 69 | }); |
| 70 | 70 | ||
| 71 | - this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,(s)=>{// 获取到了本地流 | 71 | + webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,(s)=>{// 获取到了本地流 |
| 72 | 72 | ||
| 73 | // document.getElementById('selfVideo').srcObject=s; | 73 | // document.getElementById('selfVideo').srcObject=s; |
| 74 | this.eventcallbacK("LOCAL STREAM", "获取到了本地流") | 74 | this.eventcallbacK("LOCAL STREAM", "获取到了本地流") |
| @@ -76,9 +76,9 @@ export default { | @@ -76,9 +76,9 @@ export default { | ||
| 76 | 76 | ||
| 77 | }, | 77 | }, |
| 78 | pause: function () { | 78 | pause: function () { |
| 79 | - if (this.webrtcPlayer != null) { | ||
| 80 | - this.webrtcPlayer.close(); | ||
| 81 | - this.webrtcPlayer = null; | 79 | + if (webrtcPlayer != null) { |
| 80 | + webrtcPlayer.close(); | ||
| 81 | + webrtcPlayer = null; | ||
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | }, | 84 | }, |
web_src/src/components/devicePosition.vue renamed to web_src/src/components/map.vue
| @@ -49,7 +49,7 @@ import devicePlayer from './dialog/devicePlayer.vue' | @@ -49,7 +49,7 @@ import devicePlayer from './dialog/devicePlayer.vue' | ||
| 49 | import queryTrace from './dialog/queryTrace.vue' | 49 | import queryTrace from './dialog/queryTrace.vue' |
| 50 | 50 | ||
| 51 | export default { | 51 | export default { |
| 52 | - name: "devicePosition", | 52 | + name: "map", |
| 53 | components: { | 53 | components: { |
| 54 | MapComponent, | 54 | MapComponent, |
| 55 | DeviceTree, | 55 | DeviceTree, |
| @@ -183,12 +183,27 @@ export default { | @@ -183,12 +183,27 @@ export default { | ||
| 183 | this.clean() | 183 | this.clean() |
| 184 | this.closeInfoBox() | 184 | this.closeInfoBox() |
| 185 | let params = []; | 185 | let params = []; |
| 186 | + let longitudeStr; | ||
| 187 | + let latitudeStr; | ||
| 188 | + if (window.mapParam.coordinateSystem == "GCJ-02") { | ||
| 189 | + longitudeStr = "longitudeGcj02"; | ||
| 190 | + latitudeStr = "latitudeGcj02"; | ||
| 191 | + }else if (window.mapParam.coordinateSystem == "WGS84") { | ||
| 192 | + longitudeStr = "longitudeWgs84"; | ||
| 193 | + latitudeStr = "latitudeWgs84"; | ||
| 194 | + }else { | ||
| 195 | + longitudeStr = "longitude"; | ||
| 196 | + latitudeStr = "latitude"; | ||
| 197 | + } | ||
| 198 | + | ||
| 186 | for (let i = 0; i < channels.length; i++) { | 199 | for (let i = 0; i < channels.length; i++) { |
| 187 | - if (channels[i].longitude * channels[i].latitude === 0) { | 200 | + let longitude = channels[i][longitudeStr]; |
| 201 | + let latitude = channels[i][latitudeStr]; | ||
| 202 | + if (longitude * latitude === 0) { | ||
| 188 | continue; | 203 | continue; |
| 189 | } | 204 | } |
| 190 | let item = { | 205 | let item = { |
| 191 | - position: [channels[i].longitude, channels[i].latitude], | 206 | + position: [longitude, latitude], |
| 192 | image: { | 207 | image: { |
| 193 | src: this.getImageByChannel(channels[i]), | 208 | src: this.getImageByChannel(channels[i]), |
| 194 | anchor: [0.5, 1] | 209 | anchor: [0.5, 1] |
| @@ -202,7 +217,7 @@ export default { | @@ -202,7 +217,7 @@ export default { | ||
| 202 | this.layer = this.$refs.map.addLayer(params, this.featureClickEvent) | 217 | this.layer = this.$refs.map.addLayer(params, this.featureClickEvent) |
| 203 | console.log(4) | 218 | console.log(4) |
| 204 | if (params.length === 1) { | 219 | if (params.length === 1) { |
| 205 | - this.$refs.map.panTo([channels[0].longitude, channels[0].latitude], mapParam.maxZoom) | 220 | + this.$refs.map.panTo([channels[0][longitudeStr], channels[0][latitudeStr]], mapParam.maxZoom) |
| 206 | } else if (params.length > 1) { | 221 | } else if (params.length > 1) { |
| 207 | this.$refs.map.fit(this.layer) | 222 | this.$refs.map.fit(this.layer) |
| 208 | } else { | 223 | } else { |
| @@ -251,7 +266,20 @@ export default { | @@ -251,7 +266,20 @@ export default { | ||
| 251 | this.channel = channels[0] | 266 | this.channel = channels[0] |
| 252 | } | 267 | } |
| 253 | this.$nextTick(() => { | 268 | this.$nextTick(() => { |
| 254 | - this.infoBoxId = this.$refs.map.openInfoBox([this.channel.longitude, this.channel.latitude], this.$refs.infobox, [0, -50]) | 269 | + let longitudeStr; |
| 270 | + let latitudeStr; | ||
| 271 | + if (window.mapParam.coordinateSystem == "GCJ-02") { | ||
| 272 | + longitudeStr = "longitudeGcj02"; | ||
| 273 | + latitudeStr = "latitudeGcj02"; | ||
| 274 | + }else if (window.mapParam.coordinateSystem == "WGS84") { | ||
| 275 | + longitudeStr = "longitudeWgs84"; | ||
| 276 | + latitudeStr = "latitudeWgs84"; | ||
| 277 | + }else { | ||
| 278 | + longitudeStr = "longitude"; | ||
| 279 | + latitudeStr = "latitude"; | ||
| 280 | + } | ||
| 281 | + let position = [this.channel[longitudeStr], this.channel[latitudeStr]]; | ||
| 282 | + this.infoBoxId = this.$refs.map.openInfoBox(position, this.$refs.infobox, [0, -50]) | ||
| 255 | }) | 283 | }) |
| 256 | }, | 284 | }, |
| 257 | closeInfoBox: function () { | 285 | closeInfoBox: function () { |
web_src/src/components/service/DeviceService.js
| @@ -21,47 +21,60 @@ class DeviceService{ | @@ -21,47 +21,60 @@ class DeviceService{ | ||
| 21 | if (typeof (errorCallback) == "function") errorCallback(error) | 21 | if (typeof (errorCallback) == "function") errorCallback(error) |
| 22 | }); | 22 | }); |
| 23 | } | 23 | } |
| 24 | - getAllDeviceList(callback, errorCallback) { | 24 | + |
| 25 | + getDevice(deviceId, callback, errorCallback){ | ||
| 26 | + this.$axios({ | ||
| 27 | + method: 'get', | ||
| 28 | + url:`/api/device/query/devices/${deviceId}`, | ||
| 29 | + }).then((res) => { | ||
| 30 | + if (typeof (callback) == "function") callback(res.data) | ||
| 31 | + }).catch((error) => { | ||
| 32 | + console.log(error); | ||
| 33 | + if (typeof (errorCallback) == "function") errorCallback(error) | ||
| 34 | + }); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + getAllDeviceList(callback,endCallback, errorCallback) { | ||
| 25 | let currentPage = 1; | 38 | let currentPage = 1; |
| 26 | let count = 100; | 39 | let count = 100; |
| 27 | let deviceList = [] | 40 | let deviceList = [] |
| 28 | - this.getAllDeviceListIteration(deviceList, currentPage, count, (data) => { | ||
| 29 | - if (typeof (callback) == "function") callback(data) | ||
| 30 | - }, errorCallback) | 41 | + this.getAllDeviceListIteration(deviceList, currentPage, count, callback, endCallback, errorCallback) |
| 31 | } | 42 | } |
| 32 | 43 | ||
| 33 | - getAllDeviceListIteration(deviceList, currentPage, count, callback, errorCallback) { | 44 | + getAllDeviceListIteration(deviceList, currentPage, count, callback, endCallback, errorCallback) { |
| 34 | this.getDeviceList(currentPage, count, (data) => { | 45 | this.getDeviceList(currentPage, count, (data) => { |
| 35 | if (data.list) { | 46 | if (data.list) { |
| 47 | + if (typeof (callback) == "function") callback(data.list) | ||
| 36 | deviceList = deviceList.concat(data.list); | 48 | deviceList = deviceList.concat(data.list); |
| 37 | if (deviceList.length < data.total) { | 49 | if (deviceList.length < data.total) { |
| 38 | currentPage ++ | 50 | currentPage ++ |
| 39 | - this.getAllDeviceListIteration(deviceList, currentPage, count, callback, errorCallback) | 51 | + this.getAllDeviceListIteration(deviceList, currentPage, count, callback, endCallback, errorCallback) |
| 40 | }else { | 52 | }else { |
| 41 | - if (typeof (callback) == "function") callback(deviceList) | 53 | + if (typeof (endCallback) == "function") endCallback(deviceList) |
| 42 | } | 54 | } |
| 43 | } | 55 | } |
| 44 | }, errorCallback) | 56 | }, errorCallback) |
| 45 | } | 57 | } |
| 46 | 58 | ||
| 47 | 59 | ||
| 48 | - getAllChannel(isCatalog, catalogUnderDevice, deviceId, callback, errorCallback) { | 60 | + getAllChannel(isCatalog, catalogUnderDevice, deviceId, callback, endCallback, errorCallback) { |
| 49 | let currentPage = 1; | 61 | let currentPage = 1; |
| 50 | let count = 100; | 62 | let count = 100; |
| 51 | let catalogList = [] | 63 | let catalogList = [] |
| 52 | - this.getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, errorCallback) | 64 | + this.getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, endCallback, errorCallback) |
| 53 | } | 65 | } |
| 54 | 66 | ||
| 55 | - getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, errorCallback) { | 67 | + getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, endCallback, errorCallback) { |
| 56 | this.getChanel(isCatalog, catalogUnderDevice, deviceId, currentPage, count, (data) => { | 68 | this.getChanel(isCatalog, catalogUnderDevice, deviceId, currentPage, count, (data) => { |
| 57 | if (data.list) { | 69 | if (data.list) { |
| 70 | + if (typeof (callback) == "function") callback(data.list) | ||
| 58 | catalogList = catalogList.concat(data.list); | 71 | catalogList = catalogList.concat(data.list); |
| 59 | if (catalogList.length < data.total) { | 72 | if (catalogList.length < data.total) { |
| 60 | currentPage ++ | 73 | currentPage ++ |
| 61 | this.getAllChannelIteration(isCatalog,catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, errorCallback) | 74 | this.getAllChannelIteration(isCatalog,catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, errorCallback) |
| 62 | }else { | 75 | }else { |
| 63 | console.log(1) | 76 | console.log(1) |
| 64 | - if (typeof (callback) == "function") callback(catalogList) | 77 | + if (typeof (endCallback) == "function") endCallback(catalogList) |
| 65 | } | 78 | } |
| 66 | } | 79 | } |
| 67 | }, errorCallback) | 80 | }, errorCallback) |
| @@ -84,22 +97,23 @@ class DeviceService{ | @@ -84,22 +97,23 @@ class DeviceService{ | ||
| 84 | } | 97 | } |
| 85 | 98 | ||
| 86 | 99 | ||
| 87 | - getAllSubChannel(isCatalog, deviceId, channelId, callback, errorCallback) { | 100 | + getAllSubChannel(isCatalog, deviceId, channelId, callback, endCallback, errorCallback) { |
| 88 | let currentPage = 1; | 101 | let currentPage = 1; |
| 89 | let count = 100; | 102 | let count = 100; |
| 90 | let catalogList = [] | 103 | let catalogList = [] |
| 91 | - this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, errorCallback) | 104 | + this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, endCallback, errorCallback) |
| 92 | } | 105 | } |
| 93 | 106 | ||
| 94 | - getAllSubChannelIteration(isCatalog, deviceId,channelId, catalogList, currentPage, count, callback, errorCallback) { | 107 | + getAllSubChannelIteration(isCatalog, deviceId,channelId, catalogList, currentPage, count, callback, endCallback, errorCallback) { |
| 95 | this.getSubChannel(isCatalog, deviceId, channelId, currentPage, count, (data) => { | 108 | this.getSubChannel(isCatalog, deviceId, channelId, currentPage, count, (data) => { |
| 96 | if (data.list) { | 109 | if (data.list) { |
| 110 | + if (typeof (callback) == "function") callback(data.list) | ||
| 97 | catalogList = catalogList.concat(data.list); | 111 | catalogList = catalogList.concat(data.list); |
| 98 | if (catalogList.length < data.total) { | 112 | if (catalogList.length < data.total) { |
| 99 | currentPage ++ | 113 | currentPage ++ |
| 100 | - this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, errorCallback) | 114 | + this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, endCallback, errorCallback) |
| 101 | }else { | 115 | }else { |
| 102 | - if (typeof (callback) == "function") callback(catalogList) | 116 | + if (typeof (endCallback) == "function") endCallback(catalogList) |
| 103 | } | 117 | } |
| 104 | } | 118 | } |
| 105 | }, errorCallback) | 119 | }, errorCallback) |
web_src/src/layout/UiHeader.vue
| 1 | <template> | 1 | <template> |
| 2 | <div id="UiHeader"> | 2 | <div id="UiHeader"> |
| 3 | - <el-menu router :default-active="activeIndex" menu-trigger="click" background-color="#545c64" text-color="#fff" | ||
| 4 | - active-text-color="#ffd04b" mode="horizontal"> | 3 | + |
| 4 | + <el-menu router :default-active="activeIndex" menu-trigger="click" background-color="#001529" text-color="#fff" | ||
| 5 | + active-text-color="#1890ff" mode="horizontal"> | ||
| 6 | + | ||
| 5 | <el-menu-item index="/control">控制台</el-menu-item> | 7 | <el-menu-item index="/control">控制台</el-menu-item> |
| 6 | - <el-menu-item index="/live">实时监控</el-menu-item> | 8 | + <el-menu-item index="/live">分屏监控</el-menu-item> |
| 7 | <el-menu-item index="/deviceList">国标设备</el-menu-item> | 9 | <el-menu-item index="/deviceList">国标设备</el-menu-item> |
| 8 | <el-menu-item index="/map">电子地图</el-menu-item> | 10 | <el-menu-item index="/map">电子地图</el-menu-item> |
| 9 | <el-menu-item index="/pushVideoList">推流列表</el-menu-item> | 11 | <el-menu-item index="/pushVideoList">推流列表</el-menu-item> |
| @@ -148,4 +150,8 @@ export default { | @@ -148,4 +150,8 @@ export default { | ||
| 148 | #UiHeader .el-switch__label.is-active{ | 150 | #UiHeader .el-switch__label.is-active{ |
| 149 | color: #409EFF; | 151 | color: #409EFF; |
| 150 | } | 152 | } |
| 153 | +#UiHeader .el-menu-item.is-active { | ||
| 154 | + color: #fff!important; | ||
| 155 | + background-color: #1890ff!important; | ||
| 156 | +} | ||
| 151 | </style> | 157 | </style> |
web_src/src/router/index.js
| @@ -7,7 +7,7 @@ import deviceList from '../components/DeviceList.vue' | @@ -7,7 +7,7 @@ import deviceList from '../components/DeviceList.vue' | ||
| 7 | import channelList from '../components/channelList.vue' | 7 | import channelList from '../components/channelList.vue' |
| 8 | import pushVideoList from '../components/PushVideoList.vue' | 8 | import pushVideoList from '../components/PushVideoList.vue' |
| 9 | import streamProxyList from '../components/StreamProxyList.vue' | 9 | import streamProxyList from '../components/StreamProxyList.vue' |
| 10 | -import devicePosition from '../components/devicePosition.vue' | 10 | +import map from '../components/map.vue' |
| 11 | import login from '../components/Login.vue' | 11 | import login from '../components/Login.vue' |
| 12 | import parentPlatformList from '../components/ParentPlatformList.vue' | 12 | import parentPlatformList from '../components/ParentPlatformList.vue' |
| 13 | import cloudRecord from '../components/CloudRecord.vue' | 13 | import cloudRecord from '../components/CloudRecord.vue' |
| @@ -69,9 +69,9 @@ export default new VueRouter({ | @@ -69,9 +69,9 @@ export default new VueRouter({ | ||
| 69 | component: parentPlatformList, | 69 | component: parentPlatformList, |
| 70 | }, | 70 | }, |
| 71 | { | 71 | { |
| 72 | - path: '/devicePosition/:deviceId/:parentChannelId/:count/:page', | ||
| 73 | - name: 'devicePosition', | ||
| 74 | - component: devicePosition, | 72 | + path: '/map/:deviceId/:parentChannelId/:count/:page', |
| 73 | + name: 'map', | ||
| 74 | + component: map, | ||
| 75 | }, | 75 | }, |
| 76 | { | 76 | { |
| 77 | path: '/cloudRecord', | 77 | path: '/cloudRecord', |
| @@ -100,8 +100,8 @@ export default new VueRouter({ | @@ -100,8 +100,8 @@ export default new VueRouter({ | ||
| 100 | }, | 100 | }, |
| 101 | { | 101 | { |
| 102 | path: '/map', | 102 | path: '/map', |
| 103 | - name: 'devicePosition', | ||
| 104 | - component: devicePosition, | 103 | + name: 'map', |
| 104 | + component: map, | ||
| 105 | }, | 105 | }, |
| 106 | ] | 106 | ] |
| 107 | }, | 107 | }, |