Commit d46fc9de827fe85a48f447cf1550444573a6f1a5

Authored by 648540858
1 parent 1f02cb91

优化下级平台自定义ssrc的情况,优化国标录像下载流程

src/main/java/com/genersoft/iot/vmp/common/CommonCallback.java 0 → 100644
  1 +package com.genersoft.iot.vmp.common;
  2 +
  3 +public interface CommonCallback<T>{
  4 + public void run(T t);
  5 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
... ... @@ -546,7 +546,7 @@ public class SIPCommander implements ISIPCommander {
546 546 HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
547 547 // 添加订阅
548 548 CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
549   - String callId=newCallIdHeader.getCallId();
  549 + String callId= newCallIdHeader.getCallId();
550 550 subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
551 551 logger.debug("sipc 添加订阅===callId {}",callId);
552 552 hookEvent.call(new InviteStreamInfo(mediaServerItem, json,callId, "rtp", ssrcInfo.getStream()));
... ... @@ -558,7 +558,7 @@ public class SIPCommander implements ISIPCommander {
558 558 (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd) -> {
559 559 logger.info("[录像]下载结束, 发送BYE");
560 560 try {
561   - streamByeCmd(device, channelId, ssrcInfo.getStream(),callId);
  561 + streamByeCmd(device, channelId, ssrcInfo.getStream(), callId);
562 562 } catch (InvalidArgumentException | ParseException | SipException |
563 563 SsrcTransactionNotFoundException e) {
564 564 logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage());
... ... @@ -580,8 +580,6 @@ public class SIPCommander implements ISIPCommander {
580 580 if (ssrcIndex >= 0) {
581 581 ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
582 582 }
583   - logger.debug("接收到的下载响应ssrc====>{}",ssrcInfo.getSsrc());
584   - logger.debug("接收到的下载响应ssrc====>{}",ssrc);
585 583 streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
586 584 okEvent.response(event);
587 585 });
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java
... ... @@ -12,6 +12,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
12 12 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
13 13 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
14 14 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
  15 +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
  16 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
  17 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
15 18 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
16 19 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
17 20 import gov.nist.javax.sip.message.SIPRequest;
... ... @@ -58,6 +61,9 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
58 61 @Autowired
59 62 private VideoStreamSessionManager sessionManager;
60 63  
  64 + @Autowired
  65 + private ZlmHttpHookSubscribe subscribe;
  66 +
61 67 @Override
62 68 public void afterPropertiesSet() throws Exception {
63 69 notifyMessageHandler.addHandler(cmdType, this);
... ... @@ -93,6 +99,9 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
93 99 } catch (InvalidArgumentException | ParseException | SsrcTransactionNotFoundException | SipException e) {
94 100 logger.error("[录像流]推送完毕,收到关流通知, 发送BYE失败 {}", e.getMessage());
95 101 }
  102 + // 去除监听流注销自动停止下载的监听
  103 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcTransaction.getStream(), false, "rtsp", ssrcTransaction.getMediaServerId());
  104 + subscribe.removeSubscribe(hookSubscribe);
96 105  
97 106 // 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题,需要将点播CallId进行上下级绑定
98 107 SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null);
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
... ... @@ -276,6 +276,10 @@ public class ZLMRESTfulUtils {
276 276 return sendPost(mediaServerItem, "closeRtpServer",param, null);
277 277 }
278 278  
  279 + public void closeRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param, RequestCallback callback) {
  280 + sendPost(mediaServerItem, "closeRtpServer",param, callback);
  281 + }
  282 +
279 283 public JSONObject listRtpServer(MediaServerItem mediaServerItem) {
280 284 return sendPost(mediaServerItem, "listRtpServer",null, null);
281 285 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
... ... @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.media.zlm;
3 3 import com.alibaba.fastjson2.JSON;
4 4 import com.alibaba.fastjson2.JSONArray;
5 5 import com.alibaba.fastjson2.JSONObject;
  6 +import com.genersoft.iot.vmp.common.CommonCallback;
6 7 import com.genersoft.iot.vmp.conf.UserSetting;
7 8 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
8 9 import com.genersoft.iot.vmp.media.zlm.dto.*;
... ... @@ -164,6 +165,31 @@ public class ZLMRTPServerFactory {
164 165 return result;
165 166 }
166 167  
  168 + public void closeRtpServer(MediaServerItem serverItem, String streamId, CommonCallback<Boolean> callback) {
  169 + if (serverItem == null) {
  170 + callback.run(false);
  171 + return;
  172 + }
  173 + Map<String, Object> param = new HashMap<>();
  174 + param.put("stream_id", streamId);
  175 + zlmresTfulUtils.closeRtpServer(serverItem, param, jsonObject -> {
  176 + if (jsonObject != null ) {
  177 + if (jsonObject.getInteger("code") == 0) {
  178 + callback.run(jsonObject.getInteger("hit") == 1);
  179 + return;
  180 + }else {
  181 + logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg"));
  182 + }
  183 + }else {
  184 + // 检查ZLM状态
  185 + logger.error("关闭RTP Server 失败: 请检查ZLM服务");
  186 + }
  187 + callback.run(false);
  188 + });
  189 +
  190 +
  191 + }
  192 +
167 193  
168 194 /**
169 195 * 创建一个国标推流
... ...
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
1 1 package com.genersoft.iot.vmp.service;
2 2  
  3 +import com.genersoft.iot.vmp.common.CommonCallback;
3 4 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
4 5 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
5 6 import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData;
... ... @@ -51,6 +52,8 @@ public interface IMediaServerService {
51 52  
52 53 void closeRTPServer(MediaServerItem mediaServerItem, String streamId);
53 54  
  55 + void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback<Boolean> callback);
  56 +
54 57 void closeRTPServer(String mediaServerId, String streamId);
55 58  
56 59 void clearRTPServer(MediaServerItem mediaServerItem);
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
... ... @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.service.impl;
3 3 import com.alibaba.fastjson2.JSON;
4 4 import com.alibaba.fastjson2.JSONArray;
5 5 import com.alibaba.fastjson2.JSONObject;
  6 +import com.genersoft.iot.vmp.common.CommonCallback;
6 7 import com.genersoft.iot.vmp.common.VideoManagerConstants;
7 8 import com.genersoft.iot.vmp.conf.DynamicTask;
8 9 import com.genersoft.iot.vmp.conf.SipConfig;
... ... @@ -173,6 +174,15 @@ public class MediaServerServiceImpl implements IMediaServerService {
173 174 }
174 175  
175 176 @Override
  177 + public void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback<Boolean> callback) {
  178 + if (mediaServerItem == null) {
  179 + callback.run(false);
  180 + return;
  181 + }
  182 + zlmrtpServerFactory.closeRtpServer(mediaServerItem, streamId, callback);
  183 + }
  184 +
  185 + @Override
176 186 public void closeRTPServer(String mediaServerId, String streamId) {
177 187 MediaServerItem mediaServerItem = this.getOne(mediaServerId);
178 188 closeRTPServer(mediaServerItem, streamId);
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
... ... @@ -328,9 +328,30 @@ public class PlayServiceImpl implements IPlayService {
328 328 });
329 329 }
330 330 // 关闭rtp server
331   - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
332   - // 重新开启ssrc server
333   - mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, ssrcInfo.getPort());
  331 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream(), result->{
  332 + if (result) {
  333 + // 重新开启ssrc server
  334 + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, ssrcInfo.getPort());
  335 + }else {
  336 + try {
  337 + logger.warn("[停止点播] {}/{}", device.getDeviceId(), channelId);
  338 + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);
  339 + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
  340 + logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());
  341 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
  342 + }
  343 +
  344 + dynamicTask.stop(timeOutTaskKey);
  345 + // 释放ssrc
  346 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  347 +
  348 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  349 + event.msg = "下级自定义了ssrc,重新设置收流信息失败";
  350 + event.statusCode = 500;
  351 + errorEvent.response(event);
  352 + }
  353 + });
  354 +
334 355  
335 356 }
336 357 }
... ... @@ -472,7 +493,7 @@ public class PlayServiceImpl implements IPlayService {
472 493 if (device == null) {
473 494 throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备: " + deviceId + "不存在");
474 495 }
475   -
  496 + logger.info("[回放消息] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
476 497 PlayBackResult<StreamInfo> playBackResult = new PlayBackResult<>();
477 498 String playBackTimeOutTaskKey = UUID.randomUUID().toString();
478 499 dynamicTask.startDelay(playBackTimeOutTaskKey, () -> {
... ... @@ -546,6 +567,7 @@ public class PlayServiceImpl implements IPlayService {
546 567 if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) {
547 568 // ssrc 不可用
548 569 // 释放ssrc
  570 + dynamicTask.stop(playBackTimeOutTaskKey);
549 571 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
550 572 streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
551 573 eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用";
... ... @@ -568,10 +590,31 @@ public class PlayServiceImpl implements IPlayService {
568 590 hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream()));
569 591 });
570 592 }
  593 +
571 594 // 关闭rtp server
572   - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
573   - // 重新开启ssrc server
574   - mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
  595 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream(), result->{
  596 + if (result) {
  597 + // 重新开启ssrc server
  598 + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
  599 + }else {
  600 + try {
  601 + logger.warn("[回放消息]停止 {}/{}", device.getDeviceId(), channelId);
  602 + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);
  603 + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
  604 + logger.error("[命令发送失败] 停止点播 停止, 发送BYE: {}", e.getMessage());
  605 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
  606 + }
  607 +
  608 + dynamicTask.stop(playBackTimeOutTaskKey);
  609 + // 释放ssrc
  610 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  611 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  612 + errorEvent.response(eventResult);
  613 + eventResult.msg = "下级自定义了ssrc,重新设置收流信息失败";
  614 + eventResult.statusCode = 500;
  615 + errorEvent.response(eventResult);
  616 + }
  617 + });
575 618 }
576 619 }
577 620 }
... ... @@ -619,7 +662,7 @@ public class PlayServiceImpl implements IPlayService {
619 662 throw new ControllerException(ErrorCode.ERROR400.getCode(), "设备:" + deviceId + "不存在");
620 663 }
621 664 PlayBackResult<StreamInfo> downloadResult = new PlayBackResult<>();
622   -
  665 + logger.info("[录像下载] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
623 666 String downLoadTimeOutTaskKey = UUID.randomUUID().toString();
624 667 dynamicTask.startDelay(downLoadTimeOutTaskKey, () -> {
625 668 logger.warn(String.format("录像下载请求超时,deviceId:%s ,channelId:%s", deviceId, channelId));
... ... @@ -648,7 +691,7 @@ public class PlayServiceImpl implements IPlayService {
648 691 streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
649 692 };
650 693 InviteStreamCallback hookEvent = (InviteStreamInfo inviteStreamInfo) -> {
651   - logger.info("收到订阅消息: " + inviteStreamInfo.getCallId());
  694 + logger.info("[录像下载]收到订阅消息: " + inviteStreamInfo.getCallId());
652 695 dynamicTask.stop(downLoadTimeOutTaskKey);
653 696 StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
654 697 streamInfo.setStartTime(startTime);
... ... @@ -678,9 +721,9 @@ public class PlayServiceImpl implements IPlayService {
678 721 if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
679 722 return;
680 723 }
681   - logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
  724 + logger.info("[录像下载] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
682 725 if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
683   - logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
  726 + logger.info("[录像下载] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
684 727  
685 728 if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) {
686 729 // ssrc 不可用
... ... @@ -707,14 +750,34 @@ public class PlayServiceImpl implements IPlayService {
707 750 hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream()));
708 751 });
709 752 }
  753 +
710 754 // 关闭rtp server
711   - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
712   - // 重新开启ssrc server
713   - mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
  755 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream(), result->{
  756 + if (result) {
  757 + // 重新开启ssrc server
  758 + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
  759 + }else {
  760 + try {
  761 + logger.warn("[录像下载] 停止{}/{}", device.getDeviceId(), channelId);
  762 + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);
  763 + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
  764 + logger.error("[命令发送失败] 录像下载停止, 发送BYE: {}", e.getMessage());
  765 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
  766 + }
  767 +
  768 + dynamicTask.stop(downLoadTimeOutTaskKey);
  769 + // 释放ssrc
  770 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  771 +
  772 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  773 + eventResult.msg = "下级自定义了ssrc,重新设置收流信息失败";
  774 + eventResult.statusCode = 500;
  775 + errorEvent.response(eventResult);
  776 + }
  777 + });
714 778 }
715 779 }
716 780 }
717   -
718 781 });
719 782 } catch (InvalidArgumentException | SipException | ParseException e) {
720 783 logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
... ...
web_src/src/components/dialog/recordDownload.vue
... ... @@ -96,7 +96,10 @@ export default {
96 96 });
97 97 },
98 98 close: function (){
99   - this.stopDownloadRecord();
  99 + if (this.streamInfo.progress < 1) {
  100 + this.stopDownloadRecord();
  101 + }
  102 +
100 103 if (this.timer !== null) {
101 104 window.clearTimeout(this.timer);
102 105 this.timer = null;
... ...