Commit bb22908cf7cf698853a06d51593a22eaa64e789d
Committed by
GitHub
Merge pull request #67 from lawrencehj/wvp-28181-2.0
增加上级平台信令功能实现,解决上级点播的一些问题
Showing
19 changed files
with
391 additions
and
98 deletions
README.md
| ... | ... | @@ -60,21 +60,24 @@ https://gitee.com/18010473990/wvp-GB28181.git |
| 60 | 60 | 15. 支持订阅与通知方法 |
| 61 | 61 | - [X] 移动位置订阅 |
| 62 | 62 | - [X] 移动位置通知处理 |
| 63 | - - [ ] 报警事件订阅 | |
| 63 | + - [X] 报警事件订阅 | |
| 64 | 64 | - [X] 报警事件通知处理 |
| 65 | 65 | - [ ] 设备目录订阅 |
| 66 | 66 | - [X] 设备目录通知处理 |
| 67 | 67 | 16. 移动位置查询和显示,可通过配置文件设置移动位置历史是否存储 |
| 68 | 68 | |
| 69 | 69 | # 2.0 支持特性 |
| 70 | -- [ ] 国标通道向上级联 | |
| 70 | +- [X] 国标通道向上级联 | |
| 71 | 71 | - [X] WEB添加上级平台 |
| 72 | 72 | - [X] 注册 |
| 73 | 73 | - [X] 心跳保活 |
| 74 | 74 | - [X] 通道选择 |
| 75 | 75 | - [X] 通道推送 |
| 76 | - - [ ] 点播 | |
| 77 | - - [ ] 云台控制 | |
| 76 | + - [X] 点播 | |
| 77 | + - [X] 云台控制 | |
| 78 | + - [X] 平台状态查询 | |
| 79 | + - [X] 平台信息查询 | |
| 80 | + - [X] 平台远程启动 | |
| 78 | 81 | - [ ] 添加RTSP视频 |
| 79 | 82 | - [ ] 添加ONVIF探测局域网内的设备 |
| 80 | 83 | - [ ] 添加RTMP视频 | ... | ... |
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
| ... | ... | @@ -4,10 +4,20 @@ import java.util.logging.LogManager; |
| 4 | 4 | |
| 5 | 5 | import org.springframework.boot.SpringApplication; |
| 6 | 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; |
| 7 | +import org.springframework.context.ConfigurableApplicationContext; | |
| 7 | 8 | |
| 8 | 9 | @SpringBootApplication |
| 9 | 10 | public class VManageBootstrap extends LogManager { |
| 11 | + private static String[] args; | |
| 12 | + private static ConfigurableApplicationContext context; | |
| 10 | 13 | public static void main(String[] args) { |
| 11 | - SpringApplication.run(VManageBootstrap.class, args); | |
| 14 | + VManageBootstrap.args = args; | |
| 15 | + VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args); | |
| 16 | + } | |
| 17 | + // 项目重启 | |
| 18 | + public static void restart() { | |
| 19 | + context.close(); | |
| 20 | + VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args); | |
| 21 | + | |
| 12 | 22 | } |
| 13 | 23 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
| ... | ... | @@ -15,6 +15,7 @@ import org.springframework.stereotype.Component; |
| 15 | 15 | public class VideoStreamSessionManager { |
| 16 | 16 | |
| 17 | 17 | private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>(); |
| 18 | + private ConcurrentHashMap<String, String> ssrcMap = new ConcurrentHashMap<>(); | |
| 18 | 19 | |
| 19 | 20 | public String createPlaySsrc(){ |
| 20 | 21 | return SsrcUtil.getPlaySsrc(); |
| ... | ... | @@ -24,16 +25,18 @@ public class VideoStreamSessionManager { |
| 24 | 25 | return SsrcUtil.getPlayBackSsrc(); |
| 25 | 26 | } |
| 26 | 27 | |
| 27 | - public void put(String ssrc,ClientTransaction transaction){ | |
| 28 | - sessionMap.put(ssrc, transaction); | |
| 28 | + public void put(String streamId,String ssrc,ClientTransaction transaction){ | |
| 29 | + sessionMap.put(streamId, transaction); | |
| 30 | + ssrcMap.put(streamId, ssrc); | |
| 29 | 31 | } |
| 30 | 32 | |
| 31 | - public ClientTransaction get(String ssrc){ | |
| 32 | - return sessionMap.get(ssrc); | |
| 33 | + public ClientTransaction get(String streamId){ | |
| 34 | + return sessionMap.get(streamId); | |
| 33 | 35 | } |
| 34 | 36 | |
| 35 | - public void remove(String ssrc) { | |
| 36 | - sessionMap.remove(ssrc); | |
| 37 | - SsrcUtil.releaseSsrc(ssrc); | |
| 37 | + public void remove(String streamId) { | |
| 38 | + sessionMap.remove(streamId); | |
| 39 | + SsrcUtil.releaseSsrc(ssrcMap.get(streamId)); | |
| 40 | + ssrcMap.remove(streamId); | |
| 38 | 41 | } |
| 39 | 42 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
| ... | ... | @@ -156,6 +156,7 @@ public class SIPProcessorFactory { |
| 156 | 156 | processor.setRequestEvent(evt); |
| 157 | 157 | processor.setRedisCatchStorage(redisCatchStorage); |
| 158 | 158 | processor.setZlmrtpServerFactory(zlmrtpServerFactory); |
| 159 | + processor.setSIPCommander(cmder); | |
| 159 | 160 | return processor; |
| 160 | 161 | } else if (Request.CANCEL.equals(method)) { |
| 161 | 162 | CancelRequestProcessor processor = new CancelRequestProcessor(); | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
| ... | ... | @@ -78,6 +78,14 @@ public interface ISIPCommander { |
| 78 | 78 | boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2); |
| 79 | 79 | |
| 80 | 80 | /** |
| 81 | + * 前端控制指令(用于转发上级指令) | |
| 82 | + * @param device 控制设备 | |
| 83 | + * @param channelId 预览通道 | |
| 84 | + * @param cmdString 前端控制指令串 | |
| 85 | + */ | |
| 86 | + boolean fronEndCmd(Device device, String channelId, String cmdString); | |
| 87 | + | |
| 88 | + /** | |
| 81 | 89 | * 请求预览视频流 |
| 82 | 90 | * |
| 83 | 91 | * @param device 视频设备 | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
| ... | ... | @@ -42,4 +42,23 @@ public interface ISIPCommanderForPlatform { |
| 42 | 42 | * @return |
| 43 | 43 | */ |
| 44 | 44 | boolean catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size); |
| 45 | + | |
| 46 | + /** | |
| 47 | + * 向上级回复DeviceInfo查询信息 | |
| 48 | + * @param parentPlatform 平台信息 | |
| 49 | + * @param sn | |
| 50 | + * @param fromTag | |
| 51 | + * @return | |
| 52 | + */ | |
| 53 | + boolean deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag); | |
| 54 | + | |
| 55 | + /** | |
| 56 | + * 向上级回复DeviceStatus查询信息 | |
| 57 | + * @param parentPlatform 平台信息 | |
| 58 | + * @param sn | |
| 59 | + * @param fromTag | |
| 60 | + * @return | |
| 61 | + */ | |
| 62 | + boolean deviceStatusResponse(ParentPlatform parentPlatform, String sn, String fromTag); | |
| 63 | + | |
| 45 | 64 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| ... | ... | @@ -235,7 +235,7 @@ public class SIPCommander implements ISIPCommander { |
| 235 | 235 | ptzXml.append("</Control>\r\n"); |
| 236 | 236 | |
| 237 | 237 | String tm = Long.toString(System.currentTimeMillis()); |
| 238 | - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtz" + tm, null); | |
| 238 | + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null); | |
| 239 | 239 | |
| 240 | 240 | transmitRequest(device, request); |
| 241 | 241 | return true; |
| ... | ... | @@ -272,7 +272,7 @@ public class SIPCommander implements ISIPCommander { |
| 272 | 272 | ptzXml.append("</Control>\r\n"); |
| 273 | 273 | |
| 274 | 274 | String tm = Long.toString(System.currentTimeMillis()); |
| 275 | - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtz" + tm, null); | |
| 275 | + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null); | |
| 276 | 276 | transmitRequest(device, request); |
| 277 | 277 | return true; |
| 278 | 278 | } catch (SipException | ParseException | InvalidArgumentException e) { |
| ... | ... | @@ -282,6 +282,36 @@ public class SIPCommander implements ISIPCommander { |
| 282 | 282 | } |
| 283 | 283 | |
| 284 | 284 | /** |
| 285 | + * 前端控制指令(用于转发上级指令) | |
| 286 | + * @param device 控制设备 | |
| 287 | + * @param channelId 预览通道 | |
| 288 | + * @param cmdString 前端控制指令串 | |
| 289 | + */ | |
| 290 | + @Override | |
| 291 | + public boolean fronEndCmd(Device device, String channelId, String cmdString) { | |
| 292 | + try { | |
| 293 | + StringBuffer ptzXml = new StringBuffer(200); | |
| 294 | + ptzXml.append("<?xml version=\"1.0\" ?>\r\n"); | |
| 295 | + ptzXml.append("<Control>\r\n"); | |
| 296 | + ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n"); | |
| 297 | + ptzXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); | |
| 298 | + ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); | |
| 299 | + ptzXml.append("<PTZCmd>" + cmdString + "</PTZCmd>\r\n"); | |
| 300 | + ptzXml.append("<Info>\r\n"); | |
| 301 | + ptzXml.append("</Info>\r\n"); | |
| 302 | + ptzXml.append("</Control>\r\n"); | |
| 303 | + | |
| 304 | + String tm = Long.toString(System.currentTimeMillis()); | |
| 305 | + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null); | |
| 306 | + transmitRequest(device, request); | |
| 307 | + return true; | |
| 308 | + } catch (SipException | ParseException | InvalidArgumentException e) { | |
| 309 | + e.printStackTrace(); | |
| 310 | + } | |
| 311 | + return false; | |
| 312 | + } | |
| 313 | + | |
| 314 | + /** | |
| 285 | 315 | * 请求预览视频流 |
| 286 | 316 | * @param device 视频设备 |
| 287 | 317 | * @param channelId 预览通道 |
| ... | ... | @@ -387,9 +417,7 @@ public class SIPCommander implements ISIPCommander { |
| 387 | 417 | Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrc); |
| 388 | 418 | |
| 389 | 419 | ClientTransaction transaction = transmitRequest(device, request, errorEvent); |
| 390 | - streamSession.put(streamId, transaction); | |
| 391 | - | |
| 392 | - | |
| 420 | + streamSession.put(streamId,ssrc, transaction); | |
| 393 | 421 | |
| 394 | 422 | } catch ( SipException | ParseException | InvalidArgumentException e) { |
| 395 | 423 | e.printStackTrace(); |
| ... | ... | @@ -487,7 +515,7 @@ public class SIPCommander implements ISIPCommander { |
| 487 | 515 | Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null); |
| 488 | 516 | |
| 489 | 517 | ClientTransaction transaction = transmitRequest(device, request, errorEvent); |
| 490 | - streamSession.put(streamId, transaction); | |
| 518 | + streamSession.put(streamId, ssrc, transaction); | |
| 491 | 519 | |
| 492 | 520 | } catch ( SipException | ParseException | InvalidArgumentException e) { |
| 493 | 521 | e.printStackTrace(); |
| ... | ... | @@ -893,7 +921,7 @@ public class SIPCommander implements ISIPCommander { |
| 893 | 921 | catalogXml.append("</Query>\r\n"); |
| 894 | 922 | |
| 895 | 923 | String tm = Long.toString(System.currentTimeMillis()); |
| 896 | - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "z9hG4bK-ViaDeviceInfo" + tm, "FromDev" + tm, null); | |
| 924 | + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "z9hG4bK-ViaDeviceInfo-" + tm, "FromDev" + tm, null); | |
| 897 | 925 | |
| 898 | 926 | transmitRequest(device, request); |
| 899 | 927 | |
| ... | ... | @@ -923,7 +951,7 @@ public class SIPCommander implements ISIPCommander { |
| 923 | 951 | catalogXml.append("</Query>\r\n"); |
| 924 | 952 | |
| 925 | 953 | String tm = Long.toString(System.currentTimeMillis()); |
| 926 | - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "z9hG4bK-ViaCatalog" + tm, "FromCat" + tm, null); | |
| 954 | + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "z9hG4bK-ViaCatalog-" + tm, "FromCat" + tm, null); | |
| 927 | 955 | |
| 928 | 956 | transmitRequest(device, request, errorEvent); |
| 929 | 957 | } catch (SipException | ParseException | InvalidArgumentException e) { |
| ... | ... | @@ -958,7 +986,7 @@ public class SIPCommander implements ISIPCommander { |
| 958 | 986 | recordInfoXml.append("</Query>\r\n"); |
| 959 | 987 | |
| 960 | 988 | String tm = Long.toString(System.currentTimeMillis()); |
| 961 | - Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), "ViaRecordInfoBranch", "fromRec" + tm, null); | |
| 989 | + Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), "z9hG4bK-ViaRecordInfo-" + tm, "fromRec" + tm, null); | |
| 962 | 990 | |
| 963 | 991 | transmitRequest(device, request); |
| 964 | 992 | } catch (SipException | ParseException | InvalidArgumentException e) { |
| ... | ... | @@ -1101,7 +1129,7 @@ public class SIPCommander implements ISIPCommander { |
| 1101 | 1129 | mobilePostitionXml.append("</Query>\r\n"); |
| 1102 | 1130 | |
| 1103 | 1131 | String tm = Long.toString(System.currentTimeMillis()); |
| 1104 | - Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null); | |
| 1132 | + Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null); | |
| 1105 | 1133 | |
| 1106 | 1134 | transmitRequest(device, request, errorEvent); |
| 1107 | 1135 | |
| ... | ... | @@ -1134,7 +1162,7 @@ public class SIPCommander implements ISIPCommander { |
| 1134 | 1162 | subscribePostitionXml.append("</Query>\r\n"); |
| 1135 | 1163 | |
| 1136 | 1164 | String tm = Long.toString(System.currentTimeMillis()); |
| 1137 | - Request request = headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "presence" ); //Position;id=" + tm.substring(tm.length() - 4)); | |
| 1165 | + Request request = headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, expires, "presence" ); //Position;id=" + tm.substring(tm.length() - 4)); | |
| 1138 | 1166 | transmitRequest(device, request); |
| 1139 | 1167 | |
| 1140 | 1168 | return true; |
| ... | ... | @@ -1187,7 +1215,7 @@ public class SIPCommander implements ISIPCommander { |
| 1187 | 1215 | cmdXml.append("</Query>\r\n"); |
| 1188 | 1216 | |
| 1189 | 1217 | String tm = Long.toString(System.currentTimeMillis()); |
| 1190 | - Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "presence" ); | |
| 1218 | + Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, expires, "presence" ); | |
| 1191 | 1219 | transmitRequest(device, request); |
| 1192 | 1220 | |
| 1193 | 1221 | return true; | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
| ... | ... | @@ -118,7 +118,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { |
| 118 | 118 | try { |
| 119 | 119 | |
| 120 | 120 | StringBuffer keepaliveXml = new StringBuffer(200); |
| 121 | - keepaliveXml.append("<?xml version=\"1.0\"?>\r\n");//" encoding=\"GB2312\"?>\r\n"); | |
| 121 | + keepaliveXml.append("<?xml version=\"1.0\"?>\r\n"); | |
| 122 | 122 | keepaliveXml.append("<Notify>\r\n"); |
| 123 | 123 | keepaliveXml.append("<CmdType>Keepalive</CmdType>\r\n"); |
| 124 | 124 | keepaliveXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); |
| ... | ... | @@ -217,4 +217,72 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { |
| 217 | 217 | } |
| 218 | 218 | return true; |
| 219 | 219 | } |
| 220 | + | |
| 221 | + /** | |
| 222 | + * 向上级回复DeviceInfo查询信息 | |
| 223 | + * @param parentPlatform 平台信息 | |
| 224 | + * @param sn | |
| 225 | + * @param fromTag | |
| 226 | + * @return | |
| 227 | + */ | |
| 228 | + @Override | |
| 229 | + public boolean deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) { | |
| 230 | + if (parentPlatform == null) { | |
| 231 | + return false; | |
| 232 | + } | |
| 233 | + try { | |
| 234 | + StringBuffer deviceInfoXml = new StringBuffer(600); | |
| 235 | + deviceInfoXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n"); | |
| 236 | + deviceInfoXml.append("<Response>\r\n"); | |
| 237 | + deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n"); | |
| 238 | + deviceInfoXml.append("<SN>" +sn + "</SN>\r\n"); | |
| 239 | + deviceInfoXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n"); | |
| 240 | + deviceInfoXml.append("<DeviceName>GB28181 Video Platform</DeviceName>\r\n"); | |
| 241 | + deviceInfoXml.append("<Manufacturer>Manufacturer</Manufacturer>\r\n"); | |
| 242 | + deviceInfoXml.append("<Model>wvp-28181</Model>\r\n"); | |
| 243 | + deviceInfoXml.append("<Firmware>2.0.202103</Firmware>\r\n"); | |
| 244 | + deviceInfoXml.append("<Result>OK</Result>\r\n"); | |
| 245 | + deviceInfoXml.append("</Response>\r\n"); | |
| 246 | + Request request = headerProviderPlarformProvider.createMessageRequest(parentPlatform, deviceInfoXml.toString(), fromTag); | |
| 247 | + transmitRequest(parentPlatform, request); | |
| 248 | + | |
| 249 | + } catch (SipException | ParseException | InvalidArgumentException e) { | |
| 250 | + e.printStackTrace(); | |
| 251 | + return false; | |
| 252 | + } | |
| 253 | + return true; | |
| 254 | + } | |
| 255 | + | |
| 256 | + /** | |
| 257 | + * 向上级回复DeviceStatus查询信息 | |
| 258 | + * @param parentPlatform 平台信息 | |
| 259 | + * @param sn | |
| 260 | + * @param fromTag | |
| 261 | + * @return | |
| 262 | + */ | |
| 263 | + @Override | |
| 264 | + public boolean deviceStatusResponse(ParentPlatform parentPlatform, String sn, String fromTag) { | |
| 265 | + if (parentPlatform == null) { | |
| 266 | + return false; | |
| 267 | + } | |
| 268 | + try { | |
| 269 | + StringBuffer deviceStatusXml = new StringBuffer(600); | |
| 270 | + deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n"); | |
| 271 | + deviceStatusXml.append("<Response>\r\n"); | |
| 272 | + deviceStatusXml.append("<CmdType>DeviceStatus</CmdType>\r\n"); | |
| 273 | + deviceStatusXml.append("<SN>" +sn + "</SN>\r\n"); | |
| 274 | + deviceStatusXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n"); | |
| 275 | + deviceStatusXml.append("<Result>OK</Result>\r\n"); | |
| 276 | + deviceStatusXml.append("<Online>ONLINE</Online>\r\n"); | |
| 277 | + deviceStatusXml.append("<Status>OK</Status>\r\n"); | |
| 278 | + deviceStatusXml.append("</Response>\r\n"); | |
| 279 | + Request request = headerProviderPlarformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), fromTag); | |
| 280 | + transmitRequest(parentPlatform, request); | |
| 281 | + | |
| 282 | + } catch (SipException | ParseException | InvalidArgumentException e) { | |
| 283 | + e.printStackTrace(); | |
| 284 | + return false; | |
| 285 | + } | |
| 286 | + return true; | |
| 287 | + } | |
| 220 | 288 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java
| ... | ... | @@ -4,7 +4,10 @@ import java.util.HashMap; |
| 4 | 4 | import java.util.Map; |
| 5 | 5 | |
| 6 | 6 | import javax.sip.*; |
| 7 | -//import javax.sip.message.Request; | |
| 7 | +import javax.sip.address.SipURI; | |
| 8 | +import javax.sip.header.FromHeader; | |
| 9 | +import javax.sip.header.HeaderAddress; | |
| 10 | +import javax.sip.header.ToHeader; | |
| 8 | 11 | |
| 9 | 12 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 10 | 13 | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; |
| ... | ... | @@ -12,14 +15,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcesso |
| 12 | 15 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| 13 | 16 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 14 | 17 | |
| 15 | -import org.springframework.stereotype.Component; | |
| 16 | - | |
| 17 | 18 | /** |
| 18 | 19 | * @Description:ACK请求处理器 |
| 19 | 20 | * @author: swwheihei |
| 20 | 21 | * @date: 2020年5月3日 下午5:31:45 |
| 21 | 22 | */ |
| 22 | -@Component | |
| 23 | 23 | public class AckRequestProcessor extends SIPRequestAbstractProcessor { |
| 24 | 24 | |
| 25 | 25 | private IRedisCatchStorage redisCatchStorage; |
| ... | ... | @@ -38,10 +38,8 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor { |
| 38 | 38 | if (dialog == null) return; |
| 39 | 39 | //DialogState state = dialog.getState(); |
| 40 | 40 | if (/*request.getMethod().equals(Request.INVITE) &&*/ dialog.getState()== DialogState.CONFIRMED) { |
| 41 | - String remoteUri = dialog.getRemoteParty().getURI().toString(); | |
| 42 | - String localUri = dialog.getLocalParty().getURI().toString(); | |
| 43 | - String platformGbId = remoteUri.substring(remoteUri.indexOf(":") + 1, remoteUri.indexOf("@")); | |
| 44 | - String channelId = localUri.substring(remoteUri.indexOf(":") + 1, remoteUri.indexOf("@")); | |
| 41 | + String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); | |
| 42 | + String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); | |
| 45 | 43 | SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId); |
| 46 | 44 | String is_Udp = sendRtpItem.isTcp() ? "0" : "1"; |
| 47 | 45 | String deviceId = sendRtpItem.getDeviceId(); | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java
| 1 | 1 | package com.genersoft.iot.vmp.gb28181.transmit.request.impl; |
| 2 | 2 | |
| 3 | +import javax.sip.address.SipURI; | |
| 3 | 4 | import javax.sip.Dialog; |
| 4 | 5 | import javax.sip.DialogState; |
| 5 | 6 | import javax.sip.InvalidArgumentException; |
| 6 | 7 | import javax.sip.RequestEvent; |
| 7 | 8 | import javax.sip.SipException; |
| 9 | +import javax.sip.header.FromHeader; | |
| 10 | +import javax.sip.header.HeaderAddress; | |
| 11 | +import javax.sip.header.ToHeader; | |
| 8 | 12 | import javax.sip.message.Response; |
| 9 | 13 | |
| 10 | 14 | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; |
| 15 | +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; | |
| 11 | 16 | import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; |
| 12 | 17 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| 13 | 18 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| ... | ... | @@ -18,12 +23,14 @@ import java.util.Map; |
| 18 | 23 | |
| 19 | 24 | /** |
| 20 | 25 | * @Description: BYE请求处理器 |
| 21 | - * @author: swwheihei | |
| 22 | - * @date: 2020年5月3日 下午5:32:05 | |
| 26 | + * @author: lawrencehj | |
| 27 | + * @date: 2021年3月9日 | |
| 23 | 28 | */ |
| 24 | 29 | public class ByeRequestProcessor extends SIPRequestAbstractProcessor { |
| 25 | 30 | |
| 26 | - private IRedisCatchStorage redisCatchStorage; | |
| 31 | + private ISIPCommander cmder; | |
| 32 | + | |
| 33 | + private IRedisCatchStorage redisCatchStorage; | |
| 27 | 34 | |
| 28 | 35 | private ZLMRTPServerFactory zlmrtpServerFactory; |
| 29 | 36 | |
| ... | ... | @@ -38,10 +45,8 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor { |
| 38 | 45 | Dialog dialog = evt.getDialog(); |
| 39 | 46 | if (dialog == null) return; |
| 40 | 47 | if (dialog.getState().equals(DialogState.TERMINATED)) { |
| 41 | - String remoteUri = dialog.getRemoteParty().getURI().toString(); | |
| 42 | - String localUri = dialog.getLocalParty().getURI().toString(); | |
| 43 | - String platformGbId = remoteUri.substring(remoteUri.indexOf(":") + 1, remoteUri.indexOf("@")); | |
| 44 | - String channelId = localUri.substring(remoteUri.indexOf(":") + 1, remoteUri.indexOf("@")); | |
| 48 | + String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); | |
| 49 | + String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); | |
| 45 | 50 | SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId); |
| 46 | 51 | String streamId = sendRtpItem.getStreamId(); |
| 47 | 52 | Map<String, Object> param = new HashMap<>(); |
| ... | ... | @@ -50,6 +55,11 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor { |
| 50 | 55 | param.put("stream",streamId); |
| 51 | 56 | System.out.println("停止向上级推流:" + streamId); |
| 52 | 57 | zlmrtpServerFactory.stopSendRtpStream(param); |
| 58 | + redisCatchStorage.deleteSendRTPServer(platformGbId, channelId); | |
| 59 | + if (zlmrtpServerFactory.totalReaderCount(streamId) == 0) { | |
| 60 | + System.out.println(streamId + "无其它观看者,通知设备停止推流"); | |
| 61 | + cmder.streamByeCmd(streamId); | |
| 62 | + } | |
| 53 | 63 | } |
| 54 | 64 | } catch (SipException e) { |
| 55 | 65 | e.printStackTrace(); |
| ... | ... | @@ -58,8 +68,6 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor { |
| 58 | 68 | } catch (ParseException e) { |
| 59 | 69 | e.printStackTrace(); |
| 60 | 70 | } |
| 61 | - // TODO 优先级99 Bye Request消息实现,此消息一般为级联消息,上级给下级发送视频停止指令 | |
| 62 | - | |
| 63 | 71 | } |
| 64 | 72 | |
| 65 | 73 | /*** |
| ... | ... | @@ -89,4 +97,13 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor { |
| 89 | 97 | public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) { |
| 90 | 98 | this.zlmrtpServerFactory = zlmrtpServerFactory; |
| 91 | 99 | } |
| 100 | + | |
| 101 | + public ISIPCommander getSIPCommander() { | |
| 102 | + return cmder; | |
| 103 | + } | |
| 104 | + | |
| 105 | + public void setSIPCommander(ISIPCommander cmder) { | |
| 106 | + this.cmder = cmder; | |
| 107 | + } | |
| 108 | + | |
| 92 | 109 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
| ... | ... | @@ -75,20 +75,6 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { |
| 75 | 75 | SipURI sipURI = (SipURI) request.getRequestURI(); |
| 76 | 76 | String channelId = sipURI.getUser(); |
| 77 | 77 | String platformId = null; |
| 78 | -// SubjectHeader subjectHeader = (SubjectHeader)request.getHeader(SubjectHeader.NAME); | |
| 79 | -// // 查询通道是否存在 不存在回复404 | |
| 80 | -// if (subjectHeader != null) { // 存在则从subjectHeader 获取平台信息 | |
| 81 | -// String subject = subjectHeader.getSubject(); | |
| 82 | -// if (subject != null) { | |
| 83 | -// String[] info1 = subject.split(","); | |
| 84 | -// if (info1 != null && info1 .length == 2) { | |
| 85 | -// String[] info2 = info1[1].split(":"); | |
| 86 | -// if (info2 != null && info2.length == 2) { | |
| 87 | -// platformId = info2[0]; | |
| 88 | -// } | |
| 89 | -// } | |
| 90 | -// } | |
| 91 | -// } | |
| 92 | 78 | |
| 93 | 79 | FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); |
| 94 | 80 | AddressImpl address = (AddressImpl) fromHeader.getAddress(); |
| ... | ... | @@ -224,7 +210,9 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { |
| 224 | 210 | e.printStackTrace(); |
| 225 | 211 | } |
| 226 | 212 | })); |
| 227 | - playResult.getResult(); | |
| 213 | + if (logger.isDebugEnabled()) { | |
| 214 | + logger.debug(playResult.getResult().toString()); | |
| 215 | + } | |
| 228 | 216 | |
| 229 | 217 | } catch (SipException | InvalidArgumentException | ParseException e) { |
| 230 | 218 | e.printStackTrace(); | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
| ... | ... | @@ -4,14 +4,22 @@ import java.io.ByteArrayInputStream; |
| 4 | 4 | import java.text.ParseException; |
| 5 | 5 | import java.util.*; |
| 6 | 6 | |
| 7 | +import javax.sip.address.SipURI; | |
| 8 | + | |
| 7 | 9 | import javax.sip.header.FromHeader; |
| 10 | +import javax.sip.header.HeaderAddress; | |
| 11 | +import javax.sip.header.ToHeader; | |
| 8 | 12 | import javax.sip.InvalidArgumentException; |
| 13 | +import javax.sip.ListeningPoint; | |
| 14 | +import javax.sip.ObjectInUseException; | |
| 9 | 15 | import javax.sip.RequestEvent; |
| 10 | 16 | import javax.sip.SipException; |
| 17 | +import javax.sip.SipProvider; | |
| 11 | 18 | import javax.sip.message.Request; |
| 12 | 19 | import javax.sip.message.Response; |
| 13 | 20 | |
| 14 | 21 | import com.alibaba.fastjson.JSONObject; |
| 22 | +import com.genersoft.iot.vmp.VManageBootstrap; | |
| 15 | 23 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 16 | 24 | import com.genersoft.iot.vmp.common.VideoManagerConstants; |
| 17 | 25 | import com.genersoft.iot.vmp.conf.UserSetup; |
| ... | ... | @@ -34,6 +42,7 @@ import com.genersoft.iot.vmp.utils.SpringBeanFactory; |
| 34 | 42 | import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
| 35 | 43 | import com.genersoft.iot.vmp.vmanager.platform.bean.ChannelReduce; |
| 36 | 44 | |
| 45 | +import gov.nist.javax.sip.SipStackImpl; | |
| 37 | 46 | import gov.nist.javax.sip.address.AddressImpl; |
| 38 | 47 | import gov.nist.javax.sip.address.SipUri; |
| 39 | 48 | |
| ... | ... | @@ -114,10 +123,10 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { |
| 114 | 123 | logger.info("接收到Catalog消息"); |
| 115 | 124 | processMessageCatalogList(evt); |
| 116 | 125 | } else if (MESSAGE_DEVICE_INFO.equals(cmd)) { |
| 117 | - logger.info("接收到DeviceInfo消息"); | |
| 126 | + //DeviceInfo消息处理 | |
| 118 | 127 | processMessageDeviceInfo(evt); |
| 119 | 128 | } else if (MESSAGE_DEVICE_STATUS.equals(cmd)) { |
| 120 | - logger.info("接收到DeviceStatus消息"); | |
| 129 | + // DeviceStatus消息处理 | |
| 121 | 130 | processMessageDeviceStatus(evt); |
| 122 | 131 | } else if (MESSAGE_DEVICE_CONTROL.equals(cmd)) { |
| 123 | 132 | logger.info("接收到DeviceControl消息"); |
| ... | ... | @@ -211,27 +220,48 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { |
| 211 | 220 | private void processMessageDeviceStatus(RequestEvent evt) { |
| 212 | 221 | try { |
| 213 | 222 | Element rootElement = getRootElement(evt); |
| 214 | - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); | |
| 215 | - // 检查设备是否存在, 不存在则不回复 | |
| 216 | - if (storager.exists(deviceId)) { | |
| 217 | - // 回复200 OK | |
| 218 | - responseAck(evt); | |
| 219 | - JSONObject json = new JSONObject(); | |
| 220 | - XmlUtil.node2Json(rootElement, json); | |
| 221 | - if (logger.isDebugEnabled()) { | |
| 222 | - logger.debug(json.toJSONString()); | |
| 223 | - } | |
| 224 | - RequestMessage msg = new RequestMessage(); | |
| 225 | - msg.setDeviceId(deviceId); | |
| 226 | - msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS); | |
| 227 | - msg.setData(json); | |
| 228 | - deferredResultHolder.invokeResult(msg); | |
| 223 | + String name = rootElement.getName(); | |
| 224 | + Element deviceIdElement = rootElement.element("DeviceID"); | |
| 225 | + String deviceId = deviceIdElement.getText(); | |
| 229 | 226 | |
| 230 | - if (offLineDetector.isOnline(deviceId)) { | |
| 231 | - publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); | |
| 227 | + if (name.equalsIgnoreCase("Query")) { // 区分是Response——查询响应,还是Query——查询请求 | |
| 228 | + logger.info("接收到DeviceStatus查询消息"); | |
| 229 | + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); | |
| 230 | + String platformId = ((SipUri) fromHeader.getAddress().getURI()).getUser(); | |
| 231 | + if (platformId == null) { | |
| 232 | + response404Ack(evt); | |
| 233 | + return; | |
| 232 | 234 | } else { |
| 235 | + // 回复200 OK | |
| 236 | + responseAck(evt); | |
| 237 | + String sn = rootElement.element("SN").getText(); | |
| 238 | + ParentPlatform parentPlatform = storager.queryParentPlatById(platformId); | |
| 239 | + cmderFroPlatform.deviceStatusResponse(parentPlatform, sn, fromHeader.getTag()); | |
| 240 | + } | |
| 241 | + } else { | |
| 242 | + logger.info("接收到DeviceStatus应答消息"); | |
| 243 | + // 检查设备是否存在, 不存在则不回复 | |
| 244 | + if (storager.exists(deviceId)) { | |
| 245 | + // 回复200 OK | |
| 246 | + responseAck(evt); | |
| 247 | + JSONObject json = new JSONObject(); | |
| 248 | + XmlUtil.node2Json(rootElement, json); | |
| 249 | + if (logger.isDebugEnabled()) { | |
| 250 | + logger.debug(json.toJSONString()); | |
| 251 | + } | |
| 252 | + RequestMessage msg = new RequestMessage(); | |
| 253 | + msg.setDeviceId(deviceId); | |
| 254 | + msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS); | |
| 255 | + msg.setData(json); | |
| 256 | + deferredResultHolder.invokeResult(msg); | |
| 257 | + | |
| 258 | + if (offLineDetector.isOnline(deviceId)) { | |
| 259 | + publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); | |
| 260 | + } else { | |
| 261 | + } | |
| 233 | 262 | } |
| 234 | 263 | } |
| 264 | + | |
| 235 | 265 | } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { |
| 236 | 266 | e.printStackTrace(); |
| 237 | 267 | } |
| ... | ... | @@ -263,6 +293,51 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { |
| 263 | 293 | deferredResultHolder.invokeResult(msg); |
| 264 | 294 | } else { |
| 265 | 295 | // 此处是上级发出的DeviceControl指令 |
| 296 | + String platformId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); | |
| 297 | + String targetGBId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); | |
| 298 | + // 远程启动功能 | |
| 299 | + if (!XmlUtil.isEmpty(XmlUtil.getText(rootElement, "TeleBoot"))) { | |
| 300 | + if (deviceId.equals(targetGBId)) { | |
| 301 | + // 远程启动功能:需要在重新启动程序后先对SipStack解绑 | |
| 302 | + logger.info("执行远程启动本平台命令"); | |
| 303 | + ParentPlatform parentPlatform = storager.queryParentPlatById(platformId); | |
| 304 | + cmderFroPlatform.unregister(parentPlatform, null, null); | |
| 305 | + | |
| 306 | + Thread restartThread = new Thread(new Runnable() { | |
| 307 | + @Override | |
| 308 | + public void run() { | |
| 309 | + try { | |
| 310 | + Thread.sleep(3000); | |
| 311 | + SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider"); | |
| 312 | + SipStackImpl stack = (SipStackImpl)up.getSipStack(); | |
| 313 | + stack.stop(); | |
| 314 | + Iterator listener = stack.getListeningPoints(); | |
| 315 | + while (listener.hasNext()) { | |
| 316 | + stack.deleteListeningPoint((ListeningPoint) listener.next()); | |
| 317 | + } | |
| 318 | + Iterator providers = stack.getSipProviders(); | |
| 319 | + while (providers.hasNext()) { | |
| 320 | + stack.deleteSipProvider((SipProvider) providers.next()); | |
| 321 | + } | |
| 322 | + VManageBootstrap.restart(); | |
| 323 | + } catch (InterruptedException ignored) { | |
| 324 | + } catch (ObjectInUseException e) { | |
| 325 | + e.printStackTrace(); | |
| 326 | + } | |
| 327 | + } | |
| 328 | + }); | |
| 329 | + | |
| 330 | + restartThread.setDaemon(false); | |
| 331 | + restartThread.start(); | |
| 332 | + } else { | |
| 333 | + // 远程启动指定设备 | |
| 334 | + } | |
| 335 | + } | |
| 336 | + if (!XmlUtil.isEmpty(XmlUtil.getText(rootElement,"PTZCmd")) && !deviceId.equals(targetGBId)) { | |
| 337 | + String cmdString = XmlUtil.getText(rootElement,"PTZCmd"); | |
| 338 | + Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(platformId, deviceId); | |
| 339 | + cmder.fronEndCmd(device, deviceId, cmdString); | |
| 340 | + } | |
| 266 | 341 | } |
| 267 | 342 | } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { |
| 268 | 343 | e.printStackTrace(); |
| ... | ... | @@ -374,9 +449,21 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { |
| 374 | 449 | Element deviceIdElement = rootElement.element("DeviceID"); |
| 375 | 450 | String deviceId = deviceIdElement.getTextTrim().toString(); |
| 376 | 451 | if (requestName.equals("Query")) { |
| 377 | - // 回复200 OK | |
| 378 | - responseAck(evt); | |
| 452 | + logger.info("接收到DeviceInfo查询消息"); | |
| 453 | + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); | |
| 454 | + String platformId = ((SipUri) fromHeader.getAddress().getURI()).getUser(); | |
| 455 | + if (platformId == null) { | |
| 456 | + response404Ack(evt); | |
| 457 | + return; | |
| 458 | + } else { | |
| 459 | + // 回复200 OK | |
| 460 | + responseAck(evt); | |
| 461 | + String sn = rootElement.element("SN").getText(); | |
| 462 | + ParentPlatform parentPlatform = storager.queryParentPlatById(platformId); | |
| 463 | + cmderFroPlatform.deviceInfoResponse(parentPlatform, sn, fromHeader.getTag()); | |
| 464 | + } | |
| 379 | 465 | } else { |
| 466 | + logger.info("接收到DeviceInfo应答消息"); | |
| 380 | 467 | Device device = storager.queryVideoDevice(deviceId); |
| 381 | 468 | if (device == null) { |
| 382 | 469 | return; | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/RegisterResponseProcessor.java
| ... | ... | @@ -60,16 +60,17 @@ public class RegisterResponseProcessor implements ISIPResponseProcessor { |
| 60 | 60 | logger.info(String.format("未找到callId: %s 的注册/注销平台id", callId )); |
| 61 | 61 | return; |
| 62 | 62 | } |
| 63 | - logger.info(String.format("收到 %s 的注册/注销%S响应", platformGBId, response.getStatusCode() )); | |
| 64 | 63 | |
| 65 | 64 | ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(platformGBId); |
| 66 | 65 | if (parentPlatformCatch == null) { |
| 67 | 66 | logger.warn(String.format("收到 %s 的注册/注销%S请求, 但是平台缓存信息未查询到!!!", platformGBId, response.getStatusCode())); |
| 68 | 67 | return; |
| 69 | 68 | } |
| 69 | + String action = parentPlatformCatch.getParentPlatform().getExpires().equals("0") ? "注销" : "注册"; | |
| 70 | + logger.info(String.format("收到 %s %s的%S响应", platformGBId, action, response.getStatusCode() )); | |
| 70 | 71 | ParentPlatform parentPlatform = parentPlatformCatch.getParentPlatform(); |
| 71 | 72 | if (parentPlatform == null) { |
| 72 | - logger.warn(String.format("收到 %s 的注册/注销%S请求, 但是平台信息未查询到!!!", platformGBId, response.getStatusCode())); | |
| 73 | + logger.warn(String.format("收到 %s %s的%S请求, 但是平台信息未查询到!!!", platformGBId, action, response.getStatusCode())); | |
| 73 | 74 | return; |
| 74 | 75 | } |
| 75 | 76 | |
| ... | ... | @@ -77,11 +78,16 @@ public class RegisterResponseProcessor implements ISIPResponseProcessor { |
| 77 | 78 | WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME); |
| 78 | 79 | sipCommanderForPlatform.register(parentPlatform, callId, www, null, null); |
| 79 | 80 | }else if (response.getStatusCode() == 200){ |
| 80 | - // 注册成功 | |
| 81 | - logger.info(String.format("%s 注册成功", platformGBId )); | |
| 81 | + // 注册/注销成功 | |
| 82 | + logger.info(String.format("%s %s成功", platformGBId, action)); | |
| 82 | 83 | redisCatchStorage.delPlatformRegisterInfo(callId); |
| 83 | 84 | parentPlatform.setStatus(true); |
| 85 | + // 取回Expires设置,避免注销过程中被置为0 | |
| 86 | + ParentPlatform parentPlatformTmp = storager.queryParentPlatById(platformGBId); | |
| 87 | + String expires = parentPlatformTmp.getExpires(); | |
| 88 | + parentPlatform.setExpires(expires); | |
| 84 | 89 | storager.updateParentPlatform(parentPlatform); |
| 90 | + | |
| 85 | 91 | redisCatchStorage.updatePlatformRegister(parentPlatform); |
| 86 | 92 | |
| 87 | 93 | redisCatchStorage.updatePlatformKeepalive(parentPlatform); | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
| ... | ... | @@ -267,20 +267,25 @@ public class ZLMHttpHookListener { |
| 267 | 267 | } |
| 268 | 268 | |
| 269 | 269 | String streamId = json.getString("stream"); |
| 270 | - | |
| 271 | - cmder.streamByeCmd(streamId); | |
| 272 | 270 | StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); |
| 273 | - if (streamInfo!=null){ | |
| 274 | - redisCatchStorage.stopPlay(streamInfo); | |
| 275 | - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | |
| 271 | + | |
| 272 | + JSONObject ret = new JSONObject(); | |
| 273 | + ret.put("code", 0); | |
| 274 | + ret.put("close", true); | |
| 275 | + | |
| 276 | + if (streamInfo != null) { | |
| 277 | + if (redisCatchStorage.isChannelSendingRTP(streamInfo.getChannelId())) { | |
| 278 | + ret.put("close", false); | |
| 279 | + } else { | |
| 280 | + cmder.streamByeCmd(streamId); | |
| 281 | + redisCatchStorage.stopPlay(streamInfo); | |
| 282 | + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | |
| 283 | + } | |
| 276 | 284 | }else{ |
| 285 | + cmder.streamByeCmd(streamId); | |
| 277 | 286 | streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); |
| 278 | 287 | redisCatchStorage.stopPlayback(streamInfo); |
| 279 | 288 | } |
| 280 | - | |
| 281 | - JSONObject ret = new JSONObject(); | |
| 282 | - ret.put("code", 0); | |
| 283 | - ret.put("close", true); | |
| 284 | 289 | return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
| 285 | 290 | } |
| 286 | 291 | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
| ... | ... | @@ -153,6 +153,16 @@ public class ZLMRTPServerFactory { |
| 153 | 153 | } |
| 154 | 154 | |
| 155 | 155 | /** |
| 156 | + * 查询转推的流是否有其它观看者 | |
| 157 | + * @param streamId | |
| 158 | + * @return | |
| 159 | + */ | |
| 160 | + public int totalReaderCount(String streamId) { | |
| 161 | + JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId); | |
| 162 | + return mediaInfo.getInteger("totalReaderCount"); | |
| 163 | + } | |
| 164 | + | |
| 165 | + /** | |
| 156 | 166 | * 调用zlm RESTful API —— stopSendRtp |
| 157 | 167 | */ |
| 158 | 168 | public Boolean stopSendRtpStream(Map<String, Object>param) { | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
| ... | ... | @@ -89,4 +89,17 @@ public interface IRedisCatchStorage { |
| 89 | 89 | */ |
| 90 | 90 | SendRtpItem querySendRTPServer(String platformGbId, String channelId); |
| 91 | 91 | |
| 92 | + /** | |
| 93 | + * 删除RTP推送信息缓存 | |
| 94 | + * @param platformGbId | |
| 95 | + * @param channelId | |
| 96 | + */ | |
| 97 | + void deleteSendRTPServer(String platformGbId, String channelId); | |
| 98 | + | |
| 99 | + /** | |
| 100 | + * 查询某个通道是否存在上级点播(RTP推送) | |
| 101 | + * @param channelId | |
| 102 | + */ | |
| 103 | + boolean isChannelSendingRTP(String channelId); | |
| 104 | + | |
| 92 | 105 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
| ... | ... | @@ -225,4 +225,30 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { |
| 225 | 225 | return (SendRtpItem)redis.get(key); |
| 226 | 226 | } |
| 227 | 227 | |
| 228 | + /** | |
| 229 | + * 删除RTP推送信息缓存 | |
| 230 | + * @param platformGbId | |
| 231 | + * @param channelId | |
| 232 | + */ | |
| 233 | + @Override | |
| 234 | + public void deleteSendRTPServer(String platformGbId, String channelId) { | |
| 235 | + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + platformGbId + "_" + channelId; | |
| 236 | + redis.del(key); | |
| 237 | + } | |
| 238 | + | |
| 239 | + /** | |
| 240 | + * 查询某个通道是否存在上级点播(RTP推送) | |
| 241 | + * @param channelId | |
| 242 | + */ | |
| 243 | + @Override | |
| 244 | + public boolean isChannelSendingRTP(String channelId) { | |
| 245 | + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + "*_" + channelId; | |
| 246 | + List<Object> RtpStreams = redis.scan(key); | |
| 247 | + if (RtpStreams.size() > 0) { | |
| 248 | + return true; | |
| 249 | + } else { | |
| 250 | + return false; | |
| 251 | + } | |
| 252 | + } | |
| 253 | + | |
| 228 | 254 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/platform/PlatformController.java
| ... | ... | @@ -60,7 +60,7 @@ public class PlatformController { |
| 60 | 60 | public ResponseEntity<String> savePlatform(@RequestBody ParentPlatform parentPlatform){ |
| 61 | 61 | |
| 62 | 62 | if (logger.isDebugEnabled()) { |
| 63 | - logger.debug("查询所有上级设备API调用"); | |
| 63 | + logger.debug("保存上级平台信息API调用"); | |
| 64 | 64 | } |
| 65 | 65 | if (StringUtils.isEmpty(parentPlatform.getName()) |
| 66 | 66 | ||StringUtils.isEmpty(parentPlatform.getServerGBId()) |
| ... | ... | @@ -87,13 +87,13 @@ public class PlatformController { |
| 87 | 87 | if (parentPlatform.isEnable()) { |
| 88 | 88 | // 只要保存就发送注册 |
| 89 | 89 | commanderForPlatform.register(parentPlatform); |
| 90 | - }else if (parentPlatformOld != null && parentPlatformOld.isEnable() && !parentPlatform.isEnable()){ // 关闭启用时注销 | |
| 90 | + } else if (parentPlatformOld != null && parentPlatformOld.isEnable() && !parentPlatform.isEnable()){ // 关闭启用时注销 | |
| 91 | 91 | commanderForPlatform.unregister(parentPlatform, null, null); |
| 92 | 92 | } |
| 93 | 93 | |
| 94 | - | |
| 94 | + | |
| 95 | 95 | return new ResponseEntity<>("success", HttpStatus.OK); |
| 96 | - }else { | |
| 96 | + } else { | |
| 97 | 97 | return new ResponseEntity<>("fail", HttpStatus.OK); |
| 98 | 98 | } |
| 99 | 99 | } |
| ... | ... | @@ -103,7 +103,7 @@ public class PlatformController { |
| 103 | 103 | public ResponseEntity<String> deletePlatform(@RequestBody ParentPlatform parentPlatform){ |
| 104 | 104 | |
| 105 | 105 | if (logger.isDebugEnabled()) { |
| 106 | - logger.debug("查询所有上级设备API调用"); | |
| 106 | + logger.debug("删除上级平台API调用"); | |
| 107 | 107 | } |
| 108 | 108 | if (StringUtils.isEmpty(parentPlatform.getServerGBId()) |
| 109 | 109 | ){ |
| ... | ... | @@ -138,7 +138,7 @@ public class PlatformController { |
| 138 | 138 | public ResponseEntity<String> exitPlatform(@PathVariable String deviceGbId){ |
| 139 | 139 | |
| 140 | 140 | if (logger.isDebugEnabled()) { |
| 141 | - logger.debug("查询所有上级设备API调用"); | |
| 141 | + logger.debug("查询上级平台是否存在API调用:" + deviceGbId); | |
| 142 | 142 | } |
| 143 | 143 | ParentPlatform parentPlatform = storager.queryParentPlatById(deviceGbId); |
| 144 | 144 | return new ResponseEntity<>(String.valueOf(parentPlatform != null), HttpStatus.OK); |
| ... | ... | @@ -184,7 +184,7 @@ public class PlatformController { |
| 184 | 184 | public ResponseEntity<String> delChannelForGB(@RequestBody UpdateChannelParam param){ |
| 185 | 185 | |
| 186 | 186 | if (logger.isDebugEnabled()) { |
| 187 | - logger.debug("给上级平台添加国标通道API调用"); | |
| 187 | + logger.debug("给上级平台删除国标通道API调用"); | |
| 188 | 188 | } |
| 189 | 189 | int result = storager.delChannelForGB(param.getPlatformId(), param.getChannelReduces()); |
| 190 | 190 | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java
| ... | ... | @@ -86,6 +86,9 @@ public class PlayServiceImpl implements IPlayService { |
| 86 | 86 | msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); |
| 87 | 87 | msg.setData(JSON.toJSONString(streamInfo)); |
| 88 | 88 | resultHolder.invokeResult(msg); |
| 89 | + if (hookEvent != null) { | |
| 90 | + hookEvent.response(JSONObject.parseObject(JSON.toJSONString(streamInfo))); | |
| 91 | + } | |
| 89 | 92 | } else { |
| 90 | 93 | redisCatchStorage.stopPlay(streamInfo); |
| 91 | 94 | storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | ... | ... |