Commit 82fd369ce554467f794c9aa66e453adecefc73a9

Authored by 648540858
2 parents 6a2ff995 7918f037

Merge branch 'wvp-28181-2.0'

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
Showing 31 changed files with 498 additions and 357 deletions
sql/mysql.sql
... ... @@ -277,7 +277,6 @@ CREATE TABLE `media_server` (
277 277 `rtspSSLPort` int NOT NULL,
278 278 `autoConfig` int NOT NULL,
279 279 `secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
280   - `streamNoneReaderDelayMS` int NOT NULL,
281 280 `rtpEnable` int NOT NULL,
282 281 `rtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
283 282 `sendRtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
... ...
sql/update.sql
  1 +alter table wvp.media_server
  2 + drop column streamNoneReaderDelayMS;
  3 +
  4 +alter table stream_proxy
  5 + add enable_disable_none_reader bit(1) default null;
... ...
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
1 1 package com.genersoft.iot.vmp.common;
2 2  
  3 +import io.swagger.v3.oas.annotations.media.Schema;
3 4  
  5 +@Schema(description = "流信息")
4 6 public class StreamInfo {
5 7  
  8 + @Schema(description = "应用名")
6 9 private String app;
  10 + @Schema(description = "流ID")
7 11 private String stream;
  12 + @Schema(description = "设备编号")
8 13 private String deviceID;
  14 + @Schema(description = "通道编号")
9 15 private String channelId;
  16 + @Schema(description = "HTTP-FLV流地址")
10 17 private String flv;
11 18  
  19 + @Schema(description = "IP")
12 20 private String ip;
13   -
  21 + @Schema(description = "HTTPS-FLV流地址")
14 22 private String https_flv;
  23 + @Schema(description = "Websocket-FLV流地址")
15 24 private String ws_flv;
  25 + @Schema(description = "Websockets-FLV流地址")
16 26 private String wss_flv;
  27 + @Schema(description = "HTTP-FMP4流地址")
17 28 private String fmp4;
  29 + @Schema(description = "HTTPS-FMP4流地址")
18 30 private String https_fmp4;
  31 + @Schema(description = "Websocket-FMP4流地址")
19 32 private String ws_fmp4;
  33 + @Schema(description = "Websockets-FMP4流地址")
20 34 private String wss_fmp4;
  35 + @Schema(description = "HLS流地址")
21 36 private String hls;
  37 + @Schema(description = "HTTPS-HLS流地址")
22 38 private String https_hls;
  39 + @Schema(description = "Websocket-HLS流地址")
23 40 private String ws_hls;
  41 + @Schema(description = "Websockets-HLS流地址")
24 42 private String wss_hls;
  43 + @Schema(description = "HTTP-TS流地址")
25 44 private String ts;
  45 + @Schema(description = "HTTPS-TS流地址")
26 46 private String https_ts;
  47 + @Schema(description = "Websocket-TS流地址")
27 48 private String ws_ts;
  49 + @Schema(description = "Websockets-TS流地址")
28 50 private String wss_ts;
  51 + @Schema(description = "RTMP流地址")
29 52 private String rtmp;
  53 + @Schema(description = "RTMPS流地址")
30 54 private String rtmps;
  55 + @Schema(description = "RTSP流地址")
31 56 private String rtsp;
  57 + @Schema(description = "RTSPS流地址")
32 58 private String rtsps;
  59 + @Schema(description = "RTC流地址")
33 60 private String rtc;
34 61  
  62 + @Schema(description = "RTCS流地址")
35 63 private String rtcs;
  64 + @Schema(description = "流媒体ID")
36 65 private String mediaServerId;
  66 + @Schema(description = "流编码信息")
37 67 private Object tracks;
  68 + @Schema(description = "开始时间")
38 69 private String startTime;
  70 + @Schema(description = "结束时间")
39 71 private String endTime;
  72 + @Schema(description = "进度(录像下载使用)")
40 73 private double progress;
41 74  
  75 + @Schema(description = "是否暂停(录像回放使用)")
42 76 private boolean pause;
43 77  
44 78 public static class TransactionInfo{
... ...
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
... ... @@ -69,9 +69,6 @@ public class MediaConfig{
69 69 @Value("${media.secret}")
70 70 private String secret;
71 71  
72   - @Value("${media.stream-none-reader-delay-ms:15000}")
73   - private int streamNoneReaderDelayMS = 15000;
74   -
75 72 @Value("${media.rtp.enable}")
76 73 private boolean rtpEnable;
77 74  
... ... @@ -151,10 +148,6 @@ public class MediaConfig{
151 148 return secret;
152 149 }
153 150  
154   - public int getStreamNoneReaderDelayMS() {
155   - return streamNoneReaderDelayMS;
156   - }
157   -
158 151 public boolean isRtpEnable() {
159 152 return rtpEnable;
160 153 }
... ... @@ -219,7 +212,6 @@ public class MediaConfig{
219 212 mediaServerItem.setRtspSSLPort(rtspSSLPort);
220 213 mediaServerItem.setAutoConfig(autoConfig);
221 214 mediaServerItem.setSecret(secret);
222   - mediaServerItem.setStreamNoneReaderDelayMS(streamNoneReaderDelayMS);
223 215 mediaServerItem.setRtpEnable(rtpEnable);
224 216 mediaServerItem.setRtpPortRange(rtpPortRange);
225 217 mediaServerItem.setSendRtpPortRange(sendRtpPortRange);
... ...
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
... ... @@ -33,6 +33,8 @@ public class UserSetting {
33 33  
34 34 private Boolean usePushingAsStatus = Boolean.TRUE;
35 35  
  36 + private Boolean streamOnDemand = Boolean.TRUE;
  37 +
36 38 private String serverId = "000000";
37 39  
38 40 private String thirdPartyGBIdReg = "[\\s\\S]*";
... ... @@ -146,4 +148,12 @@ public class UserSetting {
146 148 public void setUsePushingAsStatus(Boolean usePushingAsStatus) {
147 149 this.usePushingAsStatus = usePushingAsStatus;
148 150 }
  151 +
  152 + public Boolean getStreamOnDemand() {
  153 + return streamOnDemand;
  154 + }
  155 +
  156 + public void setStreamOnDemand(Boolean streamOnDemand) {
  157 + this.streamOnDemand = streamOnDemand;
  158 + }
149 159 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
... ... @@ -162,7 +162,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
162 162 if (requesterId == null) {
163 163 logger.info("无法从FromHeader的Address中获取到平台/设备id,返回400");
164 164 // 参数不全, 发400,请求错误
165   - responseAck(serverTransaction, Response.BAD_REQUEST);
  165 + try {
  166 + responseAck(serverTransaction, Response.BAD_REQUEST);
  167 + } catch (SipException | InvalidArgumentException | ParseException e) {
  168 + logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage());
  169 + }
166 170 return;
167 171 }
168 172 String ssrc = null;
... ... @@ -209,7 +213,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
209 213 // return;
210 214 // }
211 215 // 通道存在,发100,TRYING
212   - responseAck(serverTransaction, Response.TRYING);
  216 + try {
  217 + responseAck(serverTransaction, Response.TRYING);
  218 + } catch (SipException | InvalidArgumentException | ParseException e) {
  219 + logger.error("[命令发送失败] invite TRYING: {}", e.getMessage());
  220 + }
213 221 } else if (channel == null && gbStream != null) {
214 222  
215 223 String mediaServerId = gbStream.getMediaServerId();
... ... @@ -217,13 +225,21 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
217 225 if (mediaServerItem == null) {
218 226 if ("proxy".equals(gbStream.getStreamType())) {
219 227 logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
220   - responseAck(serverTransaction, Response.GONE);
  228 + try {
  229 + responseAck(serverTransaction, Response.GONE);
  230 + } catch (SipException | InvalidArgumentException | ParseException e) {
  231 + logger.error("[命令发送失败] invite GONE: {}", e.getMessage());
  232 + }
221 233 return;
222 234 } else {
223 235 streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream());
224 236 if (streamPushItem == null || streamPushItem.getServerId().equals(userSetting.getServerId())) {
225 237 logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
226   - responseAck(serverTransaction, Response.GONE);
  238 + try {
  239 + responseAck(serverTransaction, Response.GONE);
  240 + } catch (SipException | InvalidArgumentException | ParseException e) {
  241 + logger.error("[命令发送失败] invite GONE: {}", e.getMessage());
  242 + }
227 243 return;
228 244 }
229 245 }
... ... @@ -232,25 +248,47 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
232 248 streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream());
233 249 if (streamPushItem == null) {
234 250 logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
235   - responseAck(serverTransaction, Response.GONE);
  251 + try {
  252 + responseAck(serverTransaction, Response.GONE);
  253 + } catch (SipException | InvalidArgumentException | ParseException e) {
  254 + logger.error("[命令发送失败] invite GONE: {}", e.getMessage());
  255 + }
236 256 return;
237 257 }
238 258 }else if("proxy".equals(gbStream.getStreamType())){
239 259 proxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(gbStream.getApp(), gbStream.getStream());
240 260 if (proxyByAppAndStream == null) {
241 261 logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
242   - responseAck(serverTransaction, Response.GONE);
  262 + try {
  263 + responseAck(serverTransaction, Response.GONE);
  264 + } catch (SipException | InvalidArgumentException | ParseException e) {
  265 + logger.error("[命令发送失败] invite GONE: {}", e.getMessage());
  266 + }
243 267 return;
244 268 }
245 269 }
246 270 }
247   - responseAck(serverTransaction, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
  271 + try {
  272 + responseAck(serverTransaction, Response.CALL_IS_BEING_FORWARDED);
  273 + } catch (SipException | InvalidArgumentException | ParseException e) {
  274 + logger.error("[命令发送失败] invite CALL_IS_BEING_FORWARDED: {}", e.getMessage());
  275 + }
248 276 } else if (catalog != null) {
249   - responseAck(serverTransaction, Response.BAD_REQUEST, "catalog channel can not play"); // 目录不支持点播
  277 + try {
  278 + // 目录不支持点播
  279 + responseAck(serverTransaction, Response.BAD_REQUEST, "catalog channel can not play");
  280 + } catch (SipException | InvalidArgumentException | ParseException e) {
  281 + logger.error("[命令发送失败] invite 目录不支持点播: {}", e.getMessage());
  282 + }
250 283 return;
251 284 } else {
252 285 logger.info("通道不存在,返回404");
253   - responseAck(serverTransaction, Response.NOT_FOUND); // 通道不存在,发404,资源不存在
  286 + try {
  287 + // 通道不存在,发404,资源不存在
  288 + responseAck(serverTransaction, Response.NOT_FOUND);
  289 + } catch (SipException | InvalidArgumentException | ParseException e) {
  290 + logger.error("[命令发送失败] invite 通道不存在: {}", e.getMessage());
  291 + }
254 292 return;
255 293 }
256 294 if (sdp == null || ssrc == null) {
... ... @@ -320,7 +358,12 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
320 358 if (port == -1) {
321 359 logger.info("不支持的媒体格式,返回415");
322 360 // 回复不支持的格式
323   - responseAck(serverTransaction, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
  361 + try {
  362 + // 不支持的格式,发415
  363 + responseAck(serverTransaction, Response.UNSUPPORTED_MEDIA_TYPE);
  364 + } catch (SipException | InvalidArgumentException | ParseException e) {
  365 + logger.error("[命令发送失败] invite 不支持的格式: {}", e.getMessage());
  366 + }
324 367 return;
325 368 }
326 369 String username = sdp.getOrigin().getUsername();
... ... @@ -333,13 +376,21 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
333 376 device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId);
334 377 if (device == null) {
335 378 logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel);
336   - responseAck(serverTransaction, Response.SERVER_INTERNAL_ERROR);
  379 + try {
  380 + responseAck(serverTransaction, Response.SERVER_INTERNAL_ERROR);
  381 + } catch (SipException | InvalidArgumentException | ParseException e) {
  382 + logger.error("[命令发送失败] invite 未找到设备信息: {}", e.getMessage());
  383 + }
337 384 return;
338 385 }
339 386 mediaServerItem = playService.getNewMediaServerItem(device);
340 387 if (mediaServerItem == null) {
341 388 logger.warn("未找到可用的zlm");
342   - responseAck(serverTransaction, Response.BUSY_HERE);
  389 + try {
  390 + responseAck(serverTransaction, Response.BUSY_HERE);
  391 + } catch (SipException | InvalidArgumentException | ParseException e) {
  392 + logger.error("[命令发送失败] invite BUSY_HERE: {}", e.getMessage());
  393 + }
343 394 return;
344 395 }
345 396 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
... ... @@ -351,7 +402,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
351 402 }
352 403 if (sendRtpItem == null) {
353 404 logger.warn("服务器端口资源不足");
354   - responseAck(serverTransaction, Response.BUSY_HERE);
  405 + try {
  406 + responseAck(serverTransaction, Response.BUSY_HERE);
  407 + } catch (SipException | InvalidArgumentException | ParseException e) {
  408 + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage());
  409 + }
355 410 return;
356 411 }
357 412 sendRtpItem.setCallId(callIdHeader.getCallId());
... ... @@ -526,13 +581,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
526 581 }
527 582 }
528 583 }
529   -
530   - } catch (SipException | InvalidArgumentException | ParseException e) {
531   - e.printStackTrace();
532   - logger.warn("sdp解析错误");
533   - e.printStackTrace();
534 584 } catch (SdpParseException e) {
535   - e.printStackTrace();
  585 + logger.error("sdp解析错误", e);
536 586 } catch (SdpException e) {
537 587 e.printStackTrace();
538 588 }
... ... @@ -544,7 +594,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
544 594 private void pushProxyStream(RequestEvent evt, ServerTransaction serverTransaction, GbStream gbStream, ParentPlatform platform,
545 595 CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
546 596 int port, Boolean tcpActive, boolean mediaTransmissionTCP,
547   - String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException {
  597 + String channelId, String addressStr, String ssrc, String requesterId) {
548 598 Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
549 599 if (streamReady) {
550 600 // 自平台内容
... ... @@ -554,7 +604,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
554 604  
555 605 if (sendRtpItem == null) {
556 606 logger.warn("服务器端口资源不足");
557   - responseAck(serverTransaction, Response.BUSY_HERE);
  607 + try {
  608 + responseAck(serverTransaction, Response.BUSY_HERE);
  609 + } catch (SipException | InvalidArgumentException | ParseException e) {
  610 + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage());
  611 + }
558 612 return;
559 613 }
560 614 if (tcpActive != null) {
... ... @@ -579,7 +633,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
579 633 private void pushStream(RequestEvent evt, ServerTransaction serverTransaction, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
580 634 CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
581 635 int port, Boolean tcpActive, boolean mediaTransmissionTCP,
582   - String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException {
  636 + String channelId, String addressStr, String ssrc, String requesterId) {
583 637 // 推流
584 638 if (streamPushItem.isSelf()) {
585 639 Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
... ... @@ -591,7 +645,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
591 645  
592 646 if (sendRtpItem == null) {
593 647 logger.warn("服务器端口资源不足");
594   - responseAck(serverTransaction, Response.BUSY_HERE);
  648 + try {
  649 + responseAck(serverTransaction, Response.BUSY_HERE);
  650 + } catch (SipException | InvalidArgumentException | ParseException e) {
  651 + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage());
  652 + }
595 653 return;
596 654 }
597 655 if (tcpActive != null) {
... ... @@ -629,15 +687,23 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
629 687 private void notifyStreamOnline(RequestEvent evt, ServerTransaction serverTransaction, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
630 688 CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
631 689 int port, Boolean tcpActive, boolean mediaTransmissionTCP,
632   - String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException {
  690 + String channelId, String addressStr, String ssrc, String requesterId) {
633 691 if ("proxy".equals(gbStream.getStreamType())) {
634 692 // TODO 控制启用以使设备上线
635 693 logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", gbStream.getApp(), gbStream.getStream());
636   - responseAck(serverTransaction, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
  694 + try {
  695 + responseAck(serverTransaction, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
  696 + } catch (SipException | InvalidArgumentException | ParseException e) {
  697 + logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
  698 + }
637 699 } else if ("push".equals(gbStream.getStreamType())) {
638 700 if (!platform.isStartOfflinePush()) {
639 701 // 平台设置中关闭了拉起离线的推流则直接回复
640   - responseAck(serverTransaction, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing");
  702 + try {
  703 + responseAck(serverTransaction, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing");
  704 + } catch (SipException | InvalidArgumentException | ParseException e) {
  705 + logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
  706 + }
641 707 return;
642 708 }
643 709 // 发送redis消息以使设备上线
... ... @@ -765,38 +831,28 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
765 831 }
766 832 redisCatchStorage.updateSendRTPSever(sendRtpItem);
767 833 }, (wvpResult) -> {
768   - try {
769   - // 错误
770   - if (wvpResult.getCode() == RedisGbPlayMsgListener.ERROR_CODE_OFFLINE) {
771   - // 离线
772   - // 查询是否在本机上线了
773   - StreamPushItem currentStreamPushItem = streamPushService.getPush(streamPushItem.getApp(), streamPushItem.getStream());
774   - if (currentStreamPushItem.isPushIng()) {
775   - // 在线状态
776   - pushStream(evt, serverTransaction, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
777   - mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
778 834  
779   - } else {
780   - // 不在线 拉起
781   - notifyStreamOnline(evt, serverTransaction, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
782   - mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
783   - }
  835 + // 错误
  836 + if (wvpResult.getCode() == RedisGbPlayMsgListener.ERROR_CODE_OFFLINE) {
  837 + // 离线
  838 + // 查询是否在本机上线了
  839 + StreamPushItem currentStreamPushItem = streamPushService.getPush(streamPushItem.getApp(), streamPushItem.getStream());
  840 + if (currentStreamPushItem.isPushIng()) {
  841 + // 在线状态
  842 + pushStream(evt, serverTransaction, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  843 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  844 +
  845 + } else {
  846 + // 不在线 拉起
  847 + notifyStreamOnline(evt, serverTransaction, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  848 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
784 849 }
785   - } catch (InvalidArgumentException | ParseException | SipException e) {
786   - logger.error("[命令发送失败] 国标级联 点播回复: {}", e.getMessage());
787 850 }
788   -
789   -
790 851 try {
791 852 responseAck(serverTransaction, Response.BUSY_HERE);
792   - } catch (SipException e) {
793   - e.printStackTrace();
794   - } catch (InvalidArgumentException e) {
795   - e.printStackTrace();
796   - } catch (ParseException e) {
797   - e.printStackTrace();
  853 + } catch (InvalidArgumentException | ParseException | SipException e) {
  854 + logger.error("[命令发送失败] 国标级联 点播回复 BUSY_HERE: {}", e.getMessage());
798 855 }
799   - return;
800 856 });
801 857 }
802 858  
... ... @@ -834,7 +890,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
834 890 return null;
835 891 }
836 892  
837   - public void inviteFromDeviceHandle(ServerTransaction serverTransaction, String requesterId, String channelId) throws InvalidArgumentException, ParseException, SipException, SdpException {
  893 + public void inviteFromDeviceHandle(ServerTransaction serverTransaction, String requesterId, String channelId) {
838 894 // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备)
839 895 Device device = redisCatchStorage.getDevice(requesterId);
840 896 AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(requesterId, channelId);
... ... @@ -846,8 +902,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
846 902 Request request = serverTransaction.getRequest();
847 903 if (device != null) {
848 904 logger.info("收到设备" + requesterId + "的语音广播Invite请求");
849   - responseAck(serverTransaction, Response.TRYING);
850   -
  905 + try {
  906 + responseAck(serverTransaction, Response.TRYING);
  907 + } catch (SipException | InvalidArgumentException | ParseException e) {
  908 + logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage());
  909 + }
851 910 String contentString = new String(serverTransaction.getRequest().getRawContent());
852 911 // jainSip不支持y=字段, 移除移除以解析。
853 912 String substring = contentString;
... ... @@ -944,7 +1003,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
944 1003 }
945 1004 } else {
946 1005 logger.warn("来自无效设备/平台的请求");
947   - responseAck(serverTransaction, Response.BAD_REQUEST);
  1006 + try {
  1007 + responseAck(serverTransaction, Response.BAD_REQUEST);; // 不支持的格式,发415
  1008 + } catch (SipException | InvalidArgumentException | ParseException e) {
  1009 + logger.error("[命令发送失败] invite 来自无效设备/平台的请求, {}", e.getMessage());
  1010 + }
948 1011 }
949 1012 }
950 1013  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
... ... @@ -93,46 +93,44 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
93 93  
94 94 @Override
95 95 public void process(RequestEvent evt) {
  96 + ServerTransaction serverTransaction = getServerTransaction(evt);
96 97 try {
97   - taskQueue.offer(new HandlerCatchData(evt, null, null));
98   - ServerTransaction serverTransaction = getServerTransaction(evt);
99 98 responseAck(serverTransaction, Response.OK);
100   - if (!taskQueueHandlerRun) {
101   - taskQueueHandlerRun = true;
102   - taskExecutor.execute(()-> {
103   - while (!taskQueue.isEmpty()) {
104   - try {
105   - HandlerCatchData take = taskQueue.poll();
106   - Element rootElement = getRootElement(take.getEvt());
107   - if (rootElement == null) {
108   - logger.error("处理NOTIFY消息时未获取到消息体,{}", take.getEvt().getRequest());
109   - continue;
110   - }
111   - String cmd = XmlUtil.getText(rootElement, "CmdType");
112   -
113   - if (CmdType.CATALOG.equals(cmd)) {
114   - logger.info("接收到Catalog通知");
115   - processNotifyCatalogList(take.getEvt());
116   - } else if (CmdType.ALARM.equals(cmd)) {
117   - logger.info("接收到Alarm通知");
118   - processNotifyAlarm(take.getEvt());
119   - } else if (CmdType.MOBILE_POSITION.equals(cmd)) {
120   - logger.info("接收到MobilePosition通知");
121   - processNotifyMobilePosition(take.getEvt());
122   - } else {
123   - logger.info("接收到消息:" + cmd);
124   - }
125   - } catch (DocumentException e) {
126   - logger.error("处理NOTIFY消息时错误", e);
  99 + }catch (SipException | InvalidArgumentException | ParseException e) {
  100 + e.printStackTrace();
  101 + }
  102 + taskQueue.offer(new HandlerCatchData(evt, null, null));
  103 + if (!taskQueueHandlerRun) {
  104 + taskQueueHandlerRun = true;
  105 + taskExecutor.execute(()-> {
  106 + while (!taskQueue.isEmpty()) {
  107 + try {
  108 + HandlerCatchData take = taskQueue.poll();
  109 + Element rootElement = getRootElement(take.getEvt());
  110 + if (rootElement == null) {
  111 + logger.error("处理NOTIFY消息时未获取到消息体,{}", take.getEvt().getRequest());
  112 + continue;
  113 + }
  114 + String cmd = XmlUtil.getText(rootElement, "CmdType");
  115 +
  116 + if (CmdType.CATALOG.equals(cmd)) {
  117 + logger.info("接收到Catalog通知");
  118 + processNotifyCatalogList(take.getEvt());
  119 + } else if (CmdType.ALARM.equals(cmd)) {
  120 + logger.info("接收到Alarm通知");
  121 + processNotifyAlarm(take.getEvt());
  122 + } else if (CmdType.MOBILE_POSITION.equals(cmd)) {
  123 + logger.info("接收到MobilePosition通知");
  124 + processNotifyMobilePosition(take.getEvt());
  125 + } else {
  126 + logger.info("接收到消息:" + cmd);
127 127 }
  128 + } catch (DocumentException e) {
  129 + logger.error("处理NOTIFY消息时错误", e);
128 130 }
129   - taskQueueHandlerRun = false;
130   - });
131   - }
132   - } catch (SipException | InvalidArgumentException | ParseException e) {
133   - e.printStackTrace();
134   - } finally {
135   - taskQueueHandlerRun = false;
  131 + }
  132 + taskQueueHandlerRun = false;
  133 + });
136 134 }
137 135 }
138 136  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
... ... @@ -112,10 +112,10 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
112 112 if (deviceForPlatform == null) {
113 113 try {
114 114 responseAck(serverTransaction, Response.NOT_FOUND);
115   - return;
116 115 } catch (SipException | InvalidArgumentException | ParseException e) {
117 116 logger.error("[命令发送失败] 错误信息: {}", e.getMessage());
118 117 }
  118 + return;
119 119 }
120 120 try {
121 121 cmder.fronEndCmd(deviceForPlatform, channelId, cmdString, eventResult -> {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
... ... @@ -52,35 +52,36 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
52 52 // 未注册的设备不做处理
53 53 return;
54 54 }
  55 + // 回复200 OK
55 56 try {
56   - // 判断RPort是否改变,改变则说明路由nat信息变化,修改设备信息
57   - // 获取到通信地址等信息
58   - ViaHeader viaHeader = (ViaHeader) evt.getRequest().getHeader(ViaHeader.NAME);
59   - String received = viaHeader.getReceived();
60   - int rPort = viaHeader.getRPort();
61   - // 解析本地地址替代
62   - if (ObjectUtils.isEmpty(received) || rPort == -1) {
63   - received = viaHeader.getHost();
64   - rPort = viaHeader.getPort();
65   - }
66   - if (device.getPort() != rPort) {
67   - device.setPort(rPort);
68   - device.setHostAddress(received.concat(":").concat(String.valueOf(rPort)));
69   - }
70   - device.setKeepaliveTime(DateUtil.getNow());
71   - // 回复200 OK
72 57 responseAck(getServerTransaction(evt), Response.OK);
73   - if (device.getOnline() == 1) {
74   - deviceService.updateDevice(device);
75   - }else {
76   - // 对于已经离线的设备判断他的注册是否已经过期
77   - if (!deviceService.expire(device)){
78   - deviceService.online(device);
79   - }
80   - }
81 58 } catch (SipException | InvalidArgumentException | ParseException e) {
82 59 logger.error("[命令发送失败] 国标级联 心跳回复: {}", e.getMessage());
83 60 }
  61 + // 判断RPort是否改变,改变则说明路由nat信息变化,修改设备信息
  62 + // 获取到通信地址等信息
  63 + ViaHeader viaHeader = (ViaHeader) evt.getRequest().getHeader(ViaHeader.NAME);
  64 + String received = viaHeader.getReceived();
  65 + int rPort = viaHeader.getRPort();
  66 + // 解析本地地址替代
  67 + if (ObjectUtils.isEmpty(received) || rPort == -1) {
  68 + received = viaHeader.getHost();
  69 + rPort = viaHeader.getPort();
  70 + }
  71 + if (device.getPort() != rPort) {
  72 + device.setPort(rPort);
  73 + device.setHostAddress(received.concat(":").concat(String.valueOf(rPort)));
  74 + }
  75 + device.setKeepaliveTime(DateUtil.getNow());
  76 +
  77 + if (device.getOnline() == 1) {
  78 + deviceService.updateDevice(device);
  79 + }else {
  80 + // 对于已经离线的设备判断他的注册是否已经过期
  81 + if (!deviceService.expire(device)){
  82 + deviceService.online(device);
  83 + }
  84 + }
84 85 }
85 86  
86 87 @Override
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java
... ... @@ -81,8 +81,12 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
81 81 try {
82 82 Element rootElementAfterCharset = getRootElement(sipMsgInfo.getEvt(), sipMsgInfo.getDevice().getCharset());
83 83 if (rootElementAfterCharset == null) {
84   - logger.warn("[ 移动设备位置数据通知 ] content cannot be null, {}", sipMsgInfo.getEvt().getRequest());
85   - responseAck(getServerTransaction(sipMsgInfo.getEvt()), Response.BAD_REQUEST);
  84 + try {
  85 + logger.warn("[ 移动设备位置数据通知 ] content cannot be null, {}", sipMsgInfo.getEvt().getRequest());
  86 + responseAck(getServerTransaction(sipMsgInfo.getEvt()), Response.BAD_REQUEST);
  87 + } catch (SipException | InvalidArgumentException | ParseException e) {
  88 + logger.error("[命令发送失败] 移动设备位置数据通知 内容为空: {}", e.getMessage());
  89 + }
86 90 continue;
87 91 }
88 92 MobilePosition mobilePosition = new MobilePosition();
... ... @@ -133,7 +137,11 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
133 137 }
134 138 storager.updateChannelPosition(deviceChannel);
135 139 //回复 200 OK
136   - responseAck(getServerTransaction(sipMsgInfo.getEvt()), Response.OK);
  140 + try {
  141 + responseAck(getServerTransaction(sipMsgInfo.getEvt()), Response.OK);
  142 + } catch (SipException | InvalidArgumentException | ParseException e) {
  143 + logger.error("[命令发送失败] 移动设备位置数据回复200: {}", e.getMessage());
  144 + }
137 145  
138 146 // 发送redis消息。 通知位置信息的变化
139 147 JSONObject jsonObject = new JSONObject();
... ... @@ -147,7 +155,7 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
147 155 jsonObject.put("speed", mobilePosition.getSpeed());
148 156 redisCatchStorage.sendMobilePositionMsg(jsonObject);
149 157  
150   - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  158 + } catch (DocumentException e) {
151 159 e.printStackTrace();
152 160 }
153 161  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java
... ... @@ -67,33 +67,37 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem
67 67 try {
68 68 // 回复200 OK
69 69 responseAck(getServerTransaction(evt), Response.OK);
70   - Element snElement = rootElement.element("SN");
71   - String sn = snElement.getText();
72   - // 准备回复通道信息
73   - List<DeviceChannel> deviceChannelInPlatforms = storager.queryChannelWithCatalog(parentPlatform.getServerGBId());
74   - // 查询关联的直播通道
75   - List<DeviceChannel> gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId());
76   - // 回复目录信息
77   - List<DeviceChannel> catalogs = storager.queryCatalogInPlatform(parentPlatform.getServerGBId());
78   -
79   - List<DeviceChannel> allChannels = new ArrayList<>();
80   -
81   - // 回复平台
  70 + } catch (SipException | InvalidArgumentException | ParseException e) {
  71 + logger.error("[命令发送失败] 国标级联 目录查询回复200OK: {}", e.getMessage());
  72 + }
  73 + Element snElement = rootElement.element("SN");
  74 + String sn = snElement.getText();
  75 + // 准备回复通道信息
  76 + List<DeviceChannel> deviceChannelInPlatforms = storager.queryChannelWithCatalog(parentPlatform.getServerGBId());
  77 + // 查询关联的直播通道
  78 + List<DeviceChannel> gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId());
  79 + // 回复目录信息
  80 + List<DeviceChannel> catalogs = storager.queryCatalogInPlatform(parentPlatform.getServerGBId());
  81 +
  82 + List<DeviceChannel> allChannels = new ArrayList<>();
  83 +
  84 + // 回复平台
82 85 // DeviceChannel deviceChannel = getChannelForPlatform(parentPlatform);
83 86 // allChannels.add(deviceChannel);
84 87  
85   - // 回复目录
86   - if (catalogs.size() > 0) {
87   - allChannels.addAll(catalogs);
88   - }
89   - // 回复级联的通道
90   - if (deviceChannelInPlatforms.size() > 0) {
91   - allChannels.addAll(deviceChannelInPlatforms);
92   - }
93   - // 回复直播的通道
94   - if (gbStreams.size() > 0) {
95   - allChannels.addAll(gbStreams);
96   - }
  88 + // 回复目录
  89 + if (catalogs.size() > 0) {
  90 + allChannels.addAll(catalogs);
  91 + }
  92 + // 回复级联的通道
  93 + if (deviceChannelInPlatforms.size() > 0) {
  94 + allChannels.addAll(deviceChannelInPlatforms);
  95 + }
  96 + // 回复直播的通道
  97 + if (gbStreams.size() > 0) {
  98 + allChannels.addAll(gbStreams);
  99 + }
  100 + try {
97 101 if (allChannels.size() > 0) {
98 102 cmderFroPlatform.catalogQuery(allChannels, parentPlatform, sn, fromHeader.getTag());
99 103 }else {
... ... @@ -101,9 +105,11 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem
101 105 cmderFroPlatform.catalogQuery(null, parentPlatform, sn, fromHeader.getTag(), 0);
102 106 }
103 107 } catch (SipException | InvalidArgumentException | ParseException e) {
104   - logger.error("[命令发送失败] 国标级联 目录查询: {}", e.getMessage());
  108 + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
105 109 }
106 110  
  111 +
  112 +
107 113 }
108 114  
109 115 private DeviceChannel getChannelForPlatform(ParentPlatform platform) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java
... ... @@ -53,19 +53,20 @@ public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorPar
53 53 try {
54 54 // 回复200 OK
55 55 responseAck(getServerTransaction(evt), Response.OK);
56   - // 此处是对本平台发出DeviceControl指令的应答
57   - JSONObject json = new JSONObject();
58   - XmlUtil.node2Json(element, json);
59   - if (logger.isDebugEnabled()) {
60   - logger.debug(json.toJSONString());
61   - }
62   - RequestMessage msg = new RequestMessage();
63   - msg.setKey(key);
64   - msg.setData(json);
65   - deferredResultHolder.invokeAllResult(msg);
66 56 } catch (SipException | InvalidArgumentException | ParseException e) {
67   - logger.error("[命令发送失败] 国标级联 设备配置查询: {}", e.getMessage());
  57 + logger.error("[命令发送失败] 设备配置查询: {}", e.getMessage());
68 58 }
  59 + // 此处是对本平台发出DeviceControl指令的应答
  60 + JSONObject json = new JSONObject();
  61 + XmlUtil.node2Json(element, json);
  62 + if (logger.isDebugEnabled()) {
  63 + logger.debug(json.toJSONString());
  64 + }
  65 + RequestMessage msg = new RequestMessage();
  66 + msg.setKey(key);
  67 + msg.setData(json);
  68 + deferredResultHolder.invokeAllResult(msg);
  69 +
69 70  
70 71 }
71 72  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java
... ... @@ -47,20 +47,21 @@ public class DeviceControlResponseMessageHandler extends SIPRequestProcessorPare
47 47 // 此处是对本平台发出DeviceControl指令的应答
48 48 try {
49 49 responseAck(getServerTransaction(evt), Response.OK);
50   - JSONObject json = new JSONObject();
51   - String channelId = getText(element, "DeviceID");
52   - XmlUtil.node2Json(element, json);
53   - if (logger.isDebugEnabled()) {
54   - logger.debug(json.toJSONString());
55   - }
56   - RequestMessage msg = new RequestMessage();
57   - String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + device.getDeviceId() + channelId;
58   - msg.setKey(key);
59   - msg.setData(json);
60   - deferredResultHolder.invokeAllResult(msg);
61 50 } catch (SipException | InvalidArgumentException | ParseException e) {
62 51 logger.error("[命令发送失败] 国标级联 设备控制: {}", e.getMessage());
63 52 }
  53 + JSONObject json = new JSONObject();
  54 + String channelId = getText(element, "DeviceID");
  55 + XmlUtil.node2Json(element, json);
  56 + if (logger.isDebugEnabled()) {
  57 + logger.debug(json.toJSONString());
  58 + }
  59 + RequestMessage msg = new RequestMessage();
  60 + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + device.getDeviceId() + channelId;
  61 + msg.setKey(key);
  62 + msg.setData(json);
  63 + deferredResultHolder.invokeAllResult(msg);
  64 +
64 65 }
65 66  
66 67 @Override
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java
... ... @@ -78,9 +78,14 @@ public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent
78 78 ServerTransaction serverTransaction = getServerTransaction(evt);
79 79 try {
80 80 rootElement = getRootElement(evt, device.getCharset());
81   - if (rootElement == null) {
  81 +
  82 + if (rootElement == null) {
82 83 logger.warn("[ 接收到DeviceInfo应答消息 ] content cannot be null, {}", evt.getRequest());
83   - responseAck(serverTransaction, Response.BAD_REQUEST);
  84 + try {
  85 + responseAck(serverTransaction, Response.BAD_REQUEST);
  86 + } catch (SipException | InvalidArgumentException | ParseException e) {
  87 + logger.error("[命令发送失败] DeviceInfo应答消息 BAD_REQUEST: {}", e.getMessage());
  88 + }
84 89 return;
85 90 }
86 91 Element deviceIdElement = rootElement.element("DeviceID");
... ... @@ -100,17 +105,16 @@ public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent
100 105 msg.setKey(key);
101 106 msg.setData(device);
102 107 deferredResultHolder.invokeAllResult(msg);
  108 + } catch (DocumentException e) {
  109 + throw new RuntimeException(e);
  110 + }
  111 + try {
103 112 // 回复200 OK
104 113 responseAck(serverTransaction, Response.OK);
105   - } catch (DocumentException e) {
106   - e.printStackTrace();
107   - } catch (InvalidArgumentException e) {
108   - e.printStackTrace();
109   - } catch (ParseException e) {
110   - e.printStackTrace();
111   - } catch (SipException e) {
112   - e.printStackTrace();
  114 + } catch (SipException | InvalidArgumentException | ParseException e) {
  115 + logger.error("[命令发送失败] DeviceInfo应答消息 200: {}", e.getMessage());
113 116 }
  117 +
114 118 }
115 119  
116 120 @Override
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java
... ... @@ -71,7 +71,11 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
71 71 rootElement = getRootElement(evt, device.getCharset());
72 72 if (rootElement == null) {
73 73 logger.warn("[ 移动设备位置数据查询回复 ] content cannot be null, {}", evt.getRequest());
74   - responseAck(serverTransaction, Response.BAD_REQUEST);
  74 + try {
  75 + responseAck(serverTransaction, Response.BAD_REQUEST);
  76 + } catch (SipException | InvalidArgumentException | ParseException e) {
  77 + logger.error("[命令发送失败] 移动设备位置数据查询 BAD_REQUEST: {}", e.getMessage());
  78 + }
75 79 return;
76 80 }
77 81 MobilePosition mobilePosition = new MobilePosition();
... ... @@ -133,8 +137,13 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
133 137 jsonObject.put("speed", mobilePosition.getSpeed());
134 138 redisCatchStorage.sendMobilePositionMsg(jsonObject);
135 139 //回复 200 OK
136   - responseAck(serverTransaction, Response.OK);
137   - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  140 + try {
  141 + responseAck(serverTransaction, Response.OK);
  142 + } catch (SipException | InvalidArgumentException | ParseException e) {
  143 + logger.error("[命令发送失败] 移动设备位置数据查询 200: {}", e.getMessage());
  144 + }
  145 +
  146 + } catch (DocumentException e) {
138 147 e.printStackTrace();
139 148 }
140 149 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java
... ... @@ -58,7 +58,11 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent
58 58  
59 59 if (rootElement == null) {
60 60 logger.warn("[ 设备预置位查询应答 ] content cannot be null, {}", evt.getRequest());
61   - responseAck(serverTransaction, Response.BAD_REQUEST);
  61 + try {
  62 + responseAck(serverTransaction, Response.BAD_REQUEST);
  63 + } catch (InvalidArgumentException | ParseException | SipException e) {
  64 + logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage());
  65 + }
62 66 return;
63 67 }
64 68 Element presetListNumElement = rootElement.element("PresetList");
... ... @@ -67,7 +71,11 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent
67 71 String deviceId = getText(rootElement, "DeviceID");
68 72 String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + deviceId;
69 73 if (snElement == null || presetListNumElement == null) {
70   - responseAck(serverTransaction, Response.BAD_REQUEST, "xml error");
  74 + try {
  75 + responseAck(serverTransaction, Response.BAD_REQUEST, "xml error");
  76 + } catch (InvalidArgumentException | ParseException | SipException e) {
  77 + logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage());
  78 + }
71 79 return;
72 80 }
73 81 int sumNum = Integer.parseInt(presetListNumElement.attributeValue("Num"));
... ... @@ -94,11 +102,13 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent
94 102 requestMessage.setKey(key);
95 103 requestMessage.setData(presetQuerySipReqList);
96 104 deferredResultHolder.invokeAllResult(requestMessage);
97   - responseAck(serverTransaction, Response.OK);
  105 + try {
  106 + responseAck(serverTransaction, Response.OK);
  107 + } catch (InvalidArgumentException | ParseException | SipException e) {
  108 + logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage());
  109 + }
98 110 } catch (DocumentException e) {
99 111 logger.error("[解析xml]失败: ", e);
100   - } catch (InvalidArgumentException | ParseException | SipException e) {
101   - logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage());
102 112 }
103 113 }
104 114  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
... ... @@ -69,95 +69,91 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
69 69  
70 70 @Override
71 71 public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
72   -
73   - // 回复200 OK
74 72 try {
  73 + // 回复200 OK
75 74 responseAck(getServerTransaction(evt), Response.OK);
76   - taskQueue.offer(new HandlerCatchData(evt, device, rootElement));
77   - if (!taskQueueHandlerRun) {
78   - taskQueueHandlerRun = true;
79   - taskExecutor.execute(()->{
80   - while (!taskQueue.isEmpty()) {
81   - try {
82   - HandlerCatchData take = taskQueue.poll();
83   - Element rootElementForCharset = getRootElement(take.getEvt(), take.getDevice().getCharset());
84   - if (rootElement == null) {
85   - logger.warn("[ 国标录像 ] content cannot be null, {}", evt.getRequest());
86   - continue;
87   - }
88   - String sn = getText(rootElementForCharset, "SN");
89   - String channelId = getText(rootElementForCharset, "DeviceID");
90   - RecordInfo recordInfo = new RecordInfo();
91   - recordInfo.setChannelId(channelId);
92   - recordInfo.setDeviceId(take.getDevice().getDeviceId());
93   - recordInfo.setSn(sn);
94   - recordInfo.setName(getText(rootElementForCharset, "Name"));
95   - String sumNumStr = getText(rootElementForCharset, "SumNum");
96   - int sumNum = 0;
97   - if (!ObjectUtils.isEmpty(sumNumStr)) {
98   - sumNum = Integer.parseInt(sumNumStr);
99   - }
100   - recordInfo.setSumNum(sumNum);
101   - Element recordListElement = rootElementForCharset.element("RecordList");
102   - if (recordListElement == null || sumNum == 0) {
103   - logger.info("无录像数据");
104   - eventPublisher.recordEndEventPush(recordInfo);
105   - recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, new ArrayList<>());
106   - releaseRequest(take.getDevice().getDeviceId(), sn);
107   - } else {
108   - Iterator<Element> recordListIterator = recordListElement.elementIterator();
109   - if (recordListIterator != null) {
110   - List<RecordItem> recordList = new ArrayList<>();
111   - // 遍历DeviceList
112   - while (recordListIterator.hasNext()) {
113   - Element itemRecord = recordListIterator.next();
114   - Element recordElement = itemRecord.element("DeviceID");
115   - if (recordElement == null) {
116   - logger.info("记录为空,下一个...");
117   - continue;
118   - }
119   - RecordItem record = new RecordItem();
120   - record.setDeviceId(getText(itemRecord, "DeviceID"));
121   - record.setName(getText(itemRecord, "Name"));
122   - record.setFilePath(getText(itemRecord, "FilePath"));
123   - record.setFileSize(getText(itemRecord, "FileSize"));
124   - record.setAddress(getText(itemRecord, "Address"));
125   -
126   - String startTimeStr = getText(itemRecord, "StartTime");
127   - record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr));
128   -
129   - String endTimeStr = getText(itemRecord, "EndTime");
130   - record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr));
131   -
132   - record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
133   - : Integer.parseInt(getText(itemRecord, "Secrecy")));
134   - record.setType(getText(itemRecord, "Type"));
135   - record.setRecorderId(getText(itemRecord, "RecorderID"));
136   - recordList.add(record);
  75 + }catch (SipException | InvalidArgumentException | ParseException e) {
  76 + logger.error("[命令发送失败] 国标级联 国标录像: {}", e.getMessage());
  77 + }
  78 + taskQueue.offer(new HandlerCatchData(evt, device, rootElement));
  79 + if (!taskQueueHandlerRun) {
  80 + taskQueueHandlerRun = true;
  81 + taskExecutor.execute(()->{
  82 + while (!taskQueue.isEmpty()) {
  83 + try {
  84 + HandlerCatchData take = taskQueue.poll();
  85 + Element rootElementForCharset = getRootElement(take.getEvt(), take.getDevice().getCharset());
  86 + if (rootElement == null) {
  87 + logger.warn("[ 国标录像 ] content cannot be null, {}", evt.getRequest());
  88 + continue;
  89 + }
  90 + String sn = getText(rootElementForCharset, "SN");
  91 + String channelId = getText(rootElementForCharset, "DeviceID");
  92 + RecordInfo recordInfo = new RecordInfo();
  93 + recordInfo.setChannelId(channelId);
  94 + recordInfo.setDeviceId(take.getDevice().getDeviceId());
  95 + recordInfo.setSn(sn);
  96 + recordInfo.setName(getText(rootElementForCharset, "Name"));
  97 + String sumNumStr = getText(rootElementForCharset, "SumNum");
  98 + int sumNum = 0;
  99 + if (!ObjectUtils.isEmpty(sumNumStr)) {
  100 + sumNum = Integer.parseInt(sumNumStr);
  101 + }
  102 + recordInfo.setSumNum(sumNum);
  103 + Element recordListElement = rootElementForCharset.element("RecordList");
  104 + if (recordListElement == null || sumNum == 0) {
  105 + logger.info("无录像数据");
  106 + eventPublisher.recordEndEventPush(recordInfo);
  107 + recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, new ArrayList<>());
  108 + releaseRequest(take.getDevice().getDeviceId(), sn);
  109 + } else {
  110 + Iterator<Element> recordListIterator = recordListElement.elementIterator();
  111 + if (recordListIterator != null) {
  112 + List<RecordItem> recordList = new ArrayList<>();
  113 + // 遍历DeviceList
  114 + while (recordListIterator.hasNext()) {
  115 + Element itemRecord = recordListIterator.next();
  116 + Element recordElement = itemRecord.element("DeviceID");
  117 + if (recordElement == null) {
  118 + logger.info("记录为空,下一个...");
  119 + continue;
137 120 }
138   - recordInfo.setRecordList(recordList);
139   - // 发送消息,如果是上级查询此录像,则会通过这里通知给上级
140   - eventPublisher.recordEndEventPush(recordInfo);
141   - int count = recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, recordList);
142   - logger.info("[国标录像], {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum);
  121 + RecordItem record = new RecordItem();
  122 + record.setDeviceId(getText(itemRecord, "DeviceID"));
  123 + record.setName(getText(itemRecord, "Name"));
  124 + record.setFilePath(getText(itemRecord, "FilePath"));
  125 + record.setFileSize(getText(itemRecord, "FileSize"));
  126 + record.setAddress(getText(itemRecord, "Address"));
  127 +
  128 + String startTimeStr = getText(itemRecord, "StartTime");
  129 + record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr));
  130 +
  131 + String endTimeStr = getText(itemRecord, "EndTime");
  132 + record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr));
  133 +
  134 + record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
  135 + : Integer.parseInt(getText(itemRecord, "Secrecy")));
  136 + record.setType(getText(itemRecord, "Type"));
  137 + record.setRecorderId(getText(itemRecord, "RecorderID"));
  138 + recordList.add(record);
143 139 }
  140 + recordInfo.setRecordList(recordList);
  141 + // 发送消息,如果是上级查询此录像,则会通过这里通知给上级
  142 + eventPublisher.recordEndEventPush(recordInfo);
  143 + int count = recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, recordList);
  144 + logger.info("[国标录像], {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum);
  145 + }
144 146  
145   - if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){
146   - releaseRequest(take.getDevice().getDeviceId(), sn);
147   - }
  147 + if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){
  148 + releaseRequest(take.getDevice().getDeviceId(), sn);
148 149 }
149   - } catch (DocumentException e) {
150   - logger.error("xml解析异常: ", e);
151 150 }
  151 + } catch (DocumentException e) {
  152 + logger.error("xml解析异常: ", e);
152 153 }
153   - taskQueueHandlerRun = false;
154   - });
155   - }
156   -
157   - } catch (SipException | InvalidArgumentException | ParseException e) {
158   - logger.error("[命令发送失败] 国标级联 国标录像: {}", e.getMessage());
159   - } finally {
160   - taskQueueHandlerRun = false;
  154 + }
  155 + taskQueueHandlerRun = false;
  156 + });
161 157 }
162 158 }
163 159  
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -695,9 +695,12 @@ public class ZLMHttpHookListener {
695 695 String app = json.getString("app");
696 696 JSONObject ret = new JSONObject();
697 697 ret.put("code", 0);
  698 + // 录像下载
  699 + ret.put("close", userSetting.getStreamOnDemand());
698 700 if ("rtp".equals(app)){
699   - ret.put("close", true);
  701 + // 国标流, 点播/录像回放/录像下载
700 702 StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(streamId);
  703 + // 点播
701 704 if (streamInfoForPlayCatch != null) {
702 705 // 收到无人观看说明流也没有在往上级推送
703 706 if (redisCatchStorage.isChannelSendingRTP(streamInfoForPlayCatch.getChannelId())) {
... ... @@ -727,40 +730,39 @@ public class ZLMHttpHookListener {
727 730  
728 731 redisCatchStorage.stopPlay(streamInfoForPlayCatch);
729 732 storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
730   - }else{
731   - StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, streamId, null);
732   - if (streamInfoForPlayBackCatch != null ) {
733   - if (streamInfoForPlayBackCatch.isPause()) {
734   - ret.put("close", false);
735   - }else {
736   - Device device = deviceService.queryDevice(streamInfoForPlayBackCatch.getDeviceID());
737   - if (device != null) {
738   - try {
739   - cmder.streamByeCmd(device,streamInfoForPlayBackCatch.getChannelId(),
740   - streamInfoForPlayBackCatch.getStream(), null);
741   - } catch (InvalidArgumentException | ParseException | SipException |
742   - SsrcTransactionNotFoundException e) {
743   - logger.error("[无人观看]回放, 发送BYE失败 {}", e.getMessage());
744   - }
745   - }
746   - redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),
747   - streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
748   - }
749   -
  733 + return ret;
  734 + }
  735 + // 录像回放
  736 + StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, streamId, null);
  737 + if (streamInfoForPlayBackCatch != null ) {
  738 + if (streamInfoForPlayBackCatch.isPause()) {
  739 + ret.put("close", false);
750 740 }else {
751   - StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, streamId, null);
752   - // 进行录像下载时无人观看不断流
753   - if (streamInfoForDownload != null) {
754   - ret.put("close", false);
  741 + Device device = deviceService.queryDevice(streamInfoForPlayBackCatch.getDeviceID());
  742 + if (device != null) {
  743 + try {
  744 + cmder.streamByeCmd(device,streamInfoForPlayBackCatch.getChannelId(),
  745 + streamInfoForPlayBackCatch.getStream(), null);
  746 + } catch (InvalidArgumentException | ParseException | SipException |
  747 + SsrcTransactionNotFoundException e) {
  748 + logger.error("[无人观看]回放, 发送BYE失败 {}", e.getMessage());
  749 + }
755 750 }
  751 + redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),
  752 + streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
756 753 }
  754 + return ret;
757 755 }
758   - MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
759   - if (mediaServerItem != null && mediaServerItem.getStreamNoneReaderDelayMS() == -1) {
  756 + // 录像下载
  757 + StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, streamId, null);
  758 + // 进行录像下载时无人观看不断流
  759 + if (streamInfoForDownload != null) {
760 760 ret.put("close", false);
  761 + return ret;
761 762 }
762   - return ret;
763 763 }else {
  764 + // 非国标流 推流/拉流代理
  765 + // 拉流代理
764 766 StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(app, streamId);
765 767 if (streamProxyItem != null ) {
766 768 if (streamProxyItem.isEnable_remove_none_reader()) {
... ... @@ -772,12 +774,21 @@ public class ZLMHttpHookListener {
772 774 }else if (streamProxyItem.isEnable_disable_none_reader()) {
773 775 // 无人观看停用
774 776 ret.put("close", true);
  777 + // 修改数据
  778 + streamProxyService.stop(app, streamId);
775 779 }else {
776 780 ret.put("close", false);
777 781 }
  782 + return ret;
778 783 }
779   - return ret;
  784 + // 推流具有主动性,暂时不做处理
  785 +// StreamPushItem streamPushItem = streamPushService.getPush(app, streamId);
  786 +// if (streamPushItem != null) {
  787 +// // TODO 发送停止
  788 +//
  789 +// }
780 790 }
  791 + return ret;
781 792 }
782 793  
783 794 /**
... ... @@ -792,19 +803,27 @@ public class ZLMHttpHookListener {
792 803 }
793 804 String mediaServerId = json.getString("mediaServerId");
794 805 MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
795   - if (userSetting.isAutoApplyPlay() && mediaInfo != null && mediaInfo.isRtpEnable()) {
  806 + if (userSetting.isAutoApplyPlay() && mediaInfo != null) {
796 807 String app = json.getString("app");
797 808 String streamId = json.getString("stream");
798 809 if ("rtp".equals(app)) {
799   - String[] s = streamId.split("_");
800   - if (s.length == 2) {
801   - String deviceId = s[0];
802   - String channelId = s[1];
803   - Device device = redisCatchStorage.getDevice(deviceId);
804   - if (device != null) {
805   - playService.play(mediaInfo,deviceId, channelId, null, null, null);
  810 + if (mediaInfo.isRtpEnable()) {
  811 + String[] s = streamId.split("_");
  812 + if (s.length == 2) {
  813 + String deviceId = s[0];
  814 + String channelId = s[1];
  815 + Device device = redisCatchStorage.getDevice(deviceId);
  816 + if (device != null) {
  817 + playService.play(mediaInfo,deviceId, channelId, null, null, null);
  818 + }
806 819 }
807 820 }
  821 + }else {
  822 + // 拉流代理
  823 + StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(app, streamId);
  824 + if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) {
  825 + streamProxyService.start(app, streamId);
  826 + }
808 827 }
809 828 }
810 829  
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
... ... @@ -54,9 +54,6 @@ public class MediaServerItem{
54 54 @Schema(description = "ZLM鉴权参数")
55 55 private String secret;
56 56  
57   - @Schema(description = "某个流无人观看时,触发hook.on_stream_none_reader事件的最大等待时间,单位毫秒")
58   - private int streamNoneReaderDelayMS;
59   -
60 57 @Schema(description = "keepalive hook触发间隔,单位秒")
61 58 private int hookAliveInterval;
62 59  
... ... @@ -119,7 +116,6 @@ public class MediaServerItem{
119 116 rtspSSLPort = zlmServerConfig.getRtspSSlport();
120 117 autoConfig = true; // 默认值true;
121 118 secret = zlmServerConfig.getApiSecret();
122   - streamNoneReaderDelayMS = zlmServerConfig.getGeneralStreamNoneReaderDelayMS();
123 119 hookAliveInterval = zlmServerConfig.getHookAliveInterval();
124 120 rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口
125 121 rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号
... ... @@ -240,14 +236,6 @@ public class MediaServerItem{
240 236 this.secret = secret;
241 237 }
242 238  
243   - public int getStreamNoneReaderDelayMS() {
244   - return streamNoneReaderDelayMS;
245   - }
246   -
247   - public void setStreamNoneReaderDelayMS(int streamNoneReaderDelayMS) {
248   - this.streamNoneReaderDelayMS = streamNoneReaderDelayMS;
249   - }
250   -
251 239 public boolean isRtpEnable() {
252 240 return rtpEnable;
253 241 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java
... ... @@ -38,7 +38,7 @@ public class StreamProxyItem extends GbStream {
38 38 @Schema(description = "是否 无人观看时删除")
39 39 private boolean enable_remove_none_reader;
40 40  
41   - @Schema(description = "是否 无人观看时不启用")
  41 + @Schema(description = "是否 无人观看时自动停用")
42 42 private boolean enable_disable_none_reader;
43 43 @Schema(description = "上级平台国标ID")
44 44 private String platformGbId;
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
... ... @@ -547,7 +547,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
547 547 param.put("hook.on_record_mp4","");
548 548 }
549 549 param.put("hook.timeoutSec","20");
550   - param.put("general.streamNoneReaderDelayMS",mediaServerItem.getStreamNoneReaderDelayMS()==-1?"3600000":mediaServerItem.getStreamNoneReaderDelayMS() );
551 550 // 推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。
552 551 // 置0关闭此特性(推流断开会导致立即断开播放器)
553 552 // 此参数不应大于播放器超时时间
... ... @@ -612,7 +611,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
612 611 mediaServerItem.setStreamIp(ip);
613 612 mediaServerItem.setHookIp(sipConfig.getIp());
614 613 mediaServerItem.setSdpIp(ip);
615   - mediaServerItem.setStreamNoneReaderDelayMS(zlmServerConfig.getGeneralStreamNoneReaderDelayMS());
616 614 return mediaServerItem;
617 615 }
618 616  
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
... ... @@ -143,7 +143,7 @@ public class PlayServiceImpl implements IPlayService {
143 143 String uuid = UUID.randomUUID().toString();
144 144 msg.setId(uuid);
145 145 playResult.setUuid(uuid);
146   - DeferredResult<WVPResult<String>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
  146 + DeferredResult<WVPResult<StreamInfo>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
147 147 playResult.setResult(result);
148 148 // 录像查询以channelId作为deviceId查询
149 149 resultHolder.put(key, uuid, result);
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
... ... @@ -26,7 +26,6 @@ public interface MediaServerMapper {
26 26 "rtspSSLPort, " +
27 27 "autoConfig, " +
28 28 "secret, " +
29   - "streamNoneReaderDelayMS, " +
30 29 "rtpEnable, " +
31 30 "rtpPortRange, " +
32 31 "sendRtpPortRange, " +
... ... @@ -51,7 +50,6 @@ public interface MediaServerMapper {
51 50 "${rtspSSLPort}, " +
52 51 "${autoConfig}, " +
53 52 "'${secret}', " +
54   - "${streamNoneReaderDelayMS}, " +
55 53 "${rtpEnable}, " +
56 54 "'${rtpPortRange}', " +
57 55 "'${sendRtpPortRange}', " +
... ... @@ -77,7 +75,6 @@ public interface MediaServerMapper {
77 75 "<if test=\"rtspPort != null\">, rtspPort=${rtspPort}</if>" +
78 76 "<if test=\"rtspSSLPort != null\">, rtspSSLPort=${rtspSSLPort}</if>" +
79 77 "<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" +
80   - "<if test=\"streamNoneReaderDelayMS != null\">, streamNoneReaderDelayMS=${streamNoneReaderDelayMS}</if>" +
81 78 "<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" +
82 79 "<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" +
83 80 "<if test=\"sendRtpPortRange != null\">, sendRtpPortRange='${sendRtpPortRange}'</if>" +
... ... @@ -102,7 +99,6 @@ public interface MediaServerMapper {
102 99 "<if test=\"rtspPort != null\">, rtspPort=${rtspPort}</if>" +
103 100 "<if test=\"rtspSSLPort != null\">, rtspSSLPort=${rtspSSLPort}</if>" +
104 101 "<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" +
105   - "<if test=\"streamNoneReaderDelayMS != null\">, streamNoneReaderDelayMS=${streamNoneReaderDelayMS}</if>" +
106 102 "<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" +
107 103 "<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" +
108 104 "<if test=\"sendRtpPortRange != null\">, sendRtpPortRange='${sendRtpPortRange}'</if>" +
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java
... ... @@ -11,10 +11,10 @@ import java.util.List;
11 11 public interface StreamProxyMapper {
12 12  
13 13 @Insert("INSERT INTO stream_proxy (type, name, app, stream,mediaServerId, url, src_url, dst_url, " +
14   - "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_hls, enable_mp4, enable, status, enable_remove_none_reader, createTime) VALUES" +
  14 + "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_hls, enable_mp4, enable, status, enable_remove_none_reader, enable_disable_none_reader, createTime) VALUES" +
15 15 "('${type}','${name}', '${app}', '${stream}', '${mediaServerId}','${url}', '${src_url}', '${dst_url}', " +
16 16 "'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_hls}, ${enable_mp4}, ${enable}, ${status}, " +
17   - "${enable_remove_none_reader}, '${createTime}' )")
  17 + "${enable_remove_none_reader}, ${enable_disable_none_reader}, '${createTime}' )")
18 18 int add(StreamProxyItem streamProxyDto);
19 19  
20 20 @Update("UPDATE stream_proxy " +
... ... @@ -33,6 +33,7 @@ public interface StreamProxyMapper {
33 33 "enable=#{enable}, " +
34 34 "status=#{status}, " +
35 35 "enable_remove_none_reader=#{enable_remove_none_reader}, " +
  36 + "enable_disable_none_reader=#{enable_disable_none_reader}, " +
36 37 "enable_mp4=#{enable_mp4} " +
37 38 "WHERE app=#{app} AND stream=#{stream}")
38 39 int update(StreamProxyItem streamProxyDto);
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
... ... @@ -87,7 +87,7 @@ public class PlayController {
87 87 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
88 88 @Parameter(name = "channelId", description = "通道国标编号", required = true)
89 89 @GetMapping("/start/{deviceId}/{channelId}")
90   - public DeferredResult<WVPResult<String>> play(@PathVariable String deviceId,
  90 + public DeferredResult<WVPResult<StreamInfo>> play(@PathVariable String deviceId,
91 91 @PathVariable String channelId) {
92 92  
93 93 // 获取可用的zlm
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/PlayResult.java
1 1 package com.genersoft.iot.vmp.vmanager.gb28181.play.bean;
2 2  
  3 +import com.genersoft.iot.vmp.common.StreamInfo;
3 4 import com.genersoft.iot.vmp.gb28181.bean.Device;
4 5 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
5 6 import org.springframework.http.ResponseEntity;
... ... @@ -7,16 +8,16 @@ import org.springframework.web.context.request.async.DeferredResult;
7 8  
8 9 public class PlayResult {
9 10  
10   - private DeferredResult<WVPResult<String>> result;
  11 + private DeferredResult<WVPResult<StreamInfo>> result;
11 12 private String uuid;
12 13  
13 14 private Device device;
14 15  
15   - public DeferredResult<WVPResult<String>> getResult() {
  16 + public DeferredResult<WVPResult<StreamInfo>> getResult() {
16 17 return result;
17 18 }
18 19  
19   - public void setResult(DeferredResult<WVPResult<String>> result) {
  20 + public void setResult(DeferredResult<WVPResult<StreamInfo>> result) {
20 21 this.result = result;
21 22 }
22 23  
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
... ... @@ -125,8 +125,8 @@ public class UserController {
125 125 }
126 126 }
127 127  
128   - @DeleteMapping("/删除用户")
129   - @Operation(summary = "停止视频回放")
  128 + @DeleteMapping("/delete")
  129 + @Operation(summary = "删除用户")
130 130 @Parameter(name = "id", description = "用户Id", required = true)
131 131 public void delete(@RequestParam Integer id){
132 132 // 获取当前登录用户id
... ...
src/main/resources/all-application.yml
... ... @@ -146,8 +146,6 @@ media:
146 146 auto-config: true
147 147 # [可选] zlm服务器的hook.admin_params=secret
148 148 secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
149   - # [可选] zlm服务器的general.streamNoneReaderDelayMS
150   - stream-none-reader-delay-ms: 18000 # 无人观看多久自动关闭流, -1表示永不自动关闭,即 关闭按需拉流
151 149 # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试
152 150 rtp:
153 151 # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
... ... @@ -190,6 +188,8 @@ user-settings:
190 188 logInDatebase: true
191 189 # 使用推流状态作为推流通道状态
192 190 use-pushing-as-status: true
  191 + # 按需拉流, true:有人观看拉流,无人观看释放, false:拉起后不自动释放
  192 + stream-on-demand: true
193 193  
194 194 # 关闭在线文档(生产环境建议关闭)
195 195 springdoc:
... ...
web_src/src/components/dialog/MediaServerEdit.vue
... ... @@ -41,10 +41,6 @@
41 41 <el-input v-if="currentStep === 2" v-model="mediaServerForm.httpPort" disabled :disabled="mediaServerForm.defaultServer"></el-input>
42 42 <el-input v-if="currentStep === 3" v-model="mediaServerForm.httpPort" :disabled="mediaServerForm.defaultServer"></el-input>
43 43 </el-form-item>
44   - <el-form-item label="SECRET" prop="secret">
45   - <el-input v-if="currentStep === 2" v-model="mediaServerForm.secret" disabled :disabled="mediaServerForm.defaultServer"></el-input>
46   - <el-input v-if="currentStep === 3" v-model="mediaServerForm.secret" :disabled="mediaServerForm.defaultServer"></el-input>
47   - </el-form-item>
48 44 <el-form-item label="HOOK IP" prop="ip">
49 45 <el-input v-model="mediaServerForm.hookIp" placeholder="媒体服务HOOK_IP" clearable :disabled="mediaServerForm.defaultServer"></el-input>
50 46 </el-form-item>
... ... @@ -74,6 +70,10 @@
74 70 <el-form-item label="RTMPS PORT" prop="rtmpSSlPort">
75 71 <el-input v-model="mediaServerForm.rtmpSSlPort" placeholder="媒体服务RTMPS_PORT" clearable :disabled="mediaServerForm.defaultServer"></el-input>
76 72 </el-form-item>
  73 + <el-form-item label="SECRET" prop="secret">
  74 + <el-input v-if="currentStep === 2" v-model="mediaServerForm.secret" disabled :disabled="mediaServerForm.defaultServer"></el-input>
  75 + <el-input v-if="currentStep === 3" v-model="mediaServerForm.secret" :disabled="mediaServerForm.defaultServer"></el-input>
  76 + </el-form-item>
77 77 <el-form-item label="自动配置媒体服务" >
78 78 <el-switch v-model="mediaServerForm.autoConfig" :disabled="mediaServerForm.defaultServer"></el-switch>
79 79 </el-form-item>
... ... @@ -94,9 +94,6 @@
94 94 -
95 95 <el-input v-model="sendRtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="sendRtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input>
96 96 </el-form-item>
97   - <el-form-item label="无人观看多久后停止拉流" >
98   - <el-input v-model.number="mediaServerForm.streamNoneReaderDelayMS" clearable :disabled="mediaServerForm.defaultServer"></el-input>
99   - </el-form-item>
100 97 <el-form-item label="录像管理服务端口" prop="recordAssistPort">
101 98 <el-input v-model.number="mediaServerForm.recordAssistPort" :disabled="mediaServerForm.defaultServer">
102 99 <!-- <el-button v-if="mediaServerForm.recordAssistPort > 0" slot="append" type="primary" @click="checkRecordServer">测试</el-button>-->
... ... @@ -172,7 +169,6 @@ export default {
172 169 hookIp: "",
173 170 sdpIp: "",
174 171 streamIp: "",
175   - streamNoneReaderDelayMS: "",
176 172 secret: "035c73f7-bb6b-4889-a715-d9eb2d1925cc",
177 173 httpPort: "",
178 174 httpSSlPort: "",
... ... @@ -332,7 +328,6 @@ export default {
332 328 hookIp: "",
333 329 sdpIp: "",
334 330 streamIp: "",
335   - streamNoneReaderDelayMS: "",
336 331 secret: "035c73f7-bb6b-4889-a715-d9eb2d1925cc",
337 332 httpPort: "",
338 333 httpSSlPort: "",
... ...
web_src/src/components/dialog/StreamProxyEdit.vue
... ... @@ -105,7 +105,9 @@
105 105 <el-checkbox label="启用" v-model="proxyParam.enable" ></el-checkbox>
106 106 <el-checkbox label="转HLS" v-model="proxyParam.enable_hls" ></el-checkbox>
107 107 <el-checkbox label="MP4录制" v-model="proxyParam.enable_mp4" ></el-checkbox>
108   - <el-checkbox label="无人观看自动删除" v-model="proxyParam.enable_remove_none_reader" ></el-checkbox>
  108 + <el-checkbox label="无人观看自动删除" v-model="proxyParam.enable_remove_none_reader" @change="removeNoneReader"></el-checkbox>
  109 + <el-checkbox label="无人观看停止拉流" v-model="proxyParam.enable_disable_none_reader" @change="disableNoneReaderHandType"></el-checkbox>
  110 +
109 111 </div>
110 112  
111 113 </el-form-item>
... ... @@ -170,6 +172,7 @@ export default {
170 172 enable_hls: true,
171 173 enable_mp4: false,
172 174 enable_remove_none_reader: false,
  175 + enable_disable_none_reader: true,
173 176 platformGbId: null,
174 177 mediaServerId: null,
175 178 },
... ... @@ -276,6 +279,12 @@ export default {
276 279 if (this.platform.enable && this.platform.expires == "0") {
277 280 this.platform.expires = "300";
278 281 }
  282 + },
  283 + removeNoneReader: function(checked) {
  284 + this.proxyParam.enable_disable_none_reader = !checked;
  285 + },
  286 + disableNoneReaderHandType: function(checked) {
  287 + this.proxyParam.enable_remove_none_reader = !checked;
279 288 }
280 289 },
281 290 };
... ...
web_src/src/components/setting/Media.vue
... ... @@ -42,9 +42,6 @@
42 42 <el-form-item label="接口密钥" prop="secret">
43 43 <el-input v-model="form.secret" clearable></el-input>
44 44 </el-form-item>
45   - <el-form-item label="无人观看触发时长">
46   - <el-input v-model.number="form.streamNoneReaderDelayMS" clearable></el-input>
47   - </el-form-item>
48 45 <el-form-item label="自动配置">
49 46 <el-switch v-model="form.autoConfig"></el-switch>
50 47 </el-form-item>
... ...