Commit d46fc9de827fe85a48f447cf1550444573a6f1a5
1 parent
1f02cb91
优化下级平台自定义ssrc的情况,优化国标录像下载流程
Showing
9 changed files
with
141 additions
and
20 deletions
src/main/java/com/genersoft/iot/vmp/common/CommonCallback.java
0 → 100644
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; | ... | ... |