Commit a2da81f79a40f275704e10789903ffe85d0c0be8
1 parent
affbd89f
支持级联国标录像下载
Showing
8 changed files
with
71 additions
and
21 deletions
src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamType.java
| @@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.gb28181.bean; | @@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.gb28181.bean; | ||
| 2 | 2 | ||
| 3 | public enum InviteStreamType { | 3 | public enum InviteStreamType { |
| 4 | 4 | ||
| 5 | - PLAY,PLAYBACK,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY | 5 | + PLAY,PLAYBACK,DOWNLOAD,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY |
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | } | 8 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| @@ -540,7 +540,7 @@ public class SIPCommander implements ISIPCommander { | @@ -540,7 +540,7 @@ public class SIPCommander implements ISIPCommander { | ||
| 540 | 540 | ||
| 541 | content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc | 541 | content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc |
| 542 | logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc()); | 542 | logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc()); |
| 543 | - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId()); | 543 | + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); |
| 544 | // 添加订阅 | 544 | // 添加订阅 |
| 545 | CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport()); | 545 | CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport()); |
| 546 | String callId= newCallIdHeader.getCallId(); | 546 | String callId= newCallIdHeader.getCallId(); |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
| @@ -429,8 +429,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -429,8 +429,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 429 | InviteErrorCallback<Object> errorEvent = ((statusCode, msg, data) -> { | 429 | InviteErrorCallback<Object> errorEvent = ((statusCode, msg, data) -> { |
| 430 | // 未知错误。直接转发设备点播的错误 | 430 | // 未知错误。直接转发设备点播的错误 |
| 431 | try { | 431 | try { |
| 432 | - Response response = getMessageFactory().createResponse(statusCode, evt.getRequest()); | ||
| 433 | - sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); | 432 | + if (statusCode > 0) { |
| 433 | + Response response = getMessageFactory().createResponse(statusCode, evt.getRequest()); | ||
| 434 | + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); | ||
| 435 | + } | ||
| 434 | } catch (ParseException | SipException e) { | 436 | } catch (ParseException | SipException e) { |
| 435 | logger.error("未处理的异常 ", e); | 437 | logger.error("未处理的异常 ", e); |
| 436 | } | 438 | } |
| @@ -455,7 +457,37 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -455,7 +457,37 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 455 | errorEvent.run(code, msg, data); | 457 | errorEvent.run(code, msg, data); |
| 456 | } | 458 | } |
| 457 | }); | 459 | }); |
| 458 | - } else { | 460 | + }else if ("Download".equalsIgnoreCase(sessionName)) { |
| 461 | + // 获取指定的下载速度 | ||
| 462 | + Vector sdpMediaDescriptions = sdp.getMediaDescriptions(true); | ||
| 463 | + MediaDescription mediaDescription = null; | ||
| 464 | + String downloadSpeed = "1"; | ||
| 465 | + if (sdpMediaDescriptions.size() > 0) { | ||
| 466 | + mediaDescription = (MediaDescription)sdpMediaDescriptions.get(0); | ||
| 467 | + } | ||
| 468 | + if (mediaDescription != null) { | ||
| 469 | + downloadSpeed = mediaDescription.getAttribute("downloadspeed"); | ||
| 470 | + } | ||
| 471 | + | ||
| 472 | + sendRtpItem.setPlayType(InviteStreamType.DOWNLOAD); | ||
| 473 | + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam()); | ||
| 474 | + sendRtpItem.setStreamId(ssrcInfo.getStream()); | ||
| 475 | + // 写入redis, 超时时回复 | ||
| 476 | + redisCatchStorage.updateSendRTPSever(sendRtpItem); | ||
| 477 | + playService.download(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start), | ||
| 478 | + DateUtil.formatter.format(end), Integer.parseInt(downloadSpeed), | ||
| 479 | + (code, msg, data) -> { | ||
| 480 | + if (code == InviteErrorCode.SUCCESS.getCode()){ | ||
| 481 | + hookEvent.run(code, msg, data); | ||
| 482 | + }else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()){ | ||
| 483 | + logger.info("[录像下载]超时, 用户:{}, 通道:{}", username, channelId); | ||
| 484 | + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); | ||
| 485 | + errorEvent.run(code, msg, data); | ||
| 486 | + }else { | ||
| 487 | + errorEvent.run(code, msg, data); | ||
| 488 | + } | ||
| 489 | + }); | ||
| 490 | + }else { | ||
| 459 | sendRtpItem.setPlayType(InviteStreamType.PLAY); | 491 | sendRtpItem.setPlayType(InviteStreamType.PLAY); |
| 460 | String streamId = null; | 492 | String streamId = null; |
| 461 | if (mediaServerItem.isRtpEnable()) { | 493 | if (mediaServerItem.isRtpEnable()) { |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
| @@ -438,7 +438,7 @@ public class ZLMHttpHookListener { | @@ -438,7 +438,7 @@ public class ZLMHttpHookListener { | ||
| 438 | @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8") | 438 | @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8") |
| 439 | public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) { | 439 | public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) { |
| 440 | 440 | ||
| 441 | - logger.info("[ZLM HOOK]流无人观看:{]->{}->{}/{}" + param.getMediaServerId(), param.getSchema(), | 441 | + logger.info("[ZLM HOOK]流无人观看:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), |
| 442 | param.getApp(), param.getStream()); | 442 | param.getApp(), param.getStream()); |
| 443 | JSONObject ret = new JSONObject(); | 443 | JSONObject ret = new JSONObject(); |
| 444 | ret.put("code", 0); | 444 | ret.put("code", 0); |
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
| @@ -501,8 +501,10 @@ public class DeviceServiceImpl implements IDeviceService { | @@ -501,8 +501,10 @@ public class DeviceServiceImpl implements IDeviceService { | ||
| 501 | node.setBasicData(channel); | 501 | node.setBasicData(channel); |
| 502 | node.setParent(false); | 502 | node.setParent(false); |
| 503 | if (channel.getChannelId().length() > 8) { | 503 | if (channel.getChannelId().length() > 8) { |
| 504 | - String gbCodeType = channel.getChannelId().substring(10, 13); | ||
| 505 | - node.setParent(gbCodeType.equals(ChannelIdType.BUSINESS_GROUP) || gbCodeType.equals(ChannelIdType.VIRTUAL_ORGANIZATION) ); | 504 | + if (channel.getChannelId().length() > 13) { |
| 505 | + String gbCodeType = channel.getChannelId().substring(10, 13); | ||
| 506 | + node.setParent(gbCodeType.equals(ChannelIdType.BUSINESS_GROUP) || gbCodeType.equals(ChannelIdType.VIRTUAL_ORGANIZATION) ); | ||
| 507 | + } | ||
| 506 | }else { | 508 | }else { |
| 507 | node.setParent(true); | 509 | node.setParent(true); |
| 508 | } | 510 | } |
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
| @@ -757,7 +757,7 @@ public class PlayServiceImpl implements IPlayService { | @@ -757,7 +757,7 @@ public class PlayServiceImpl implements IPlayService { | ||
| 757 | null); | 757 | null); |
| 758 | return; | 758 | return; |
| 759 | } | 759 | } |
| 760 | - logger.info("[录像下载] deviceId: {}, channelId: {},收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); | 760 | + logger.info("[录像下载] deviceId: {}, channelId: {}, 下载速度:{}, 收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, downloadSpeed, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); |
| 761 | // 初始化redis中的invite消息状态 | 761 | // 初始化redis中的invite消息状态 |
| 762 | InviteInfo inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, | 762 | InviteInfo inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, |
| 763 | mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.DOWNLOAD, | 763 | mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.DOWNLOAD, |
| @@ -888,7 +888,6 @@ public class PlayServiceImpl implements IPlayService { | @@ -888,7 +888,6 @@ public class PlayServiceImpl implements IPlayService { | ||
| 888 | cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null); | 888 | cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null); |
| 889 | } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { | 889 | } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { |
| 890 | logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); | 890 | logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); |
| 891 | - | ||
| 892 | } | 891 | } |
| 893 | 892 | ||
| 894 | dynamicTask.stop(downLoadTimeOutTaskKey); | 893 | dynamicTask.stop(downLoadTimeOutTaskKey); |
| @@ -970,10 +969,12 @@ public class PlayServiceImpl implements IPlayService { | @@ -970,10 +969,12 @@ public class PlayServiceImpl implements IPlayService { | ||
| 970 | private StreamInfo onPublishHandlerForDownload(MediaServerItem mediaServerItemInuse, JSONObject response, String deviceId, String channelId, String startTime, String endTime) { | 969 | private StreamInfo onPublishHandlerForDownload(MediaServerItem mediaServerItemInuse, JSONObject response, String deviceId, String channelId, String startTime, String endTime) { |
| 971 | StreamInfo streamInfo = onPublishHandler(mediaServerItemInuse, response, deviceId, channelId); | 970 | StreamInfo streamInfo = onPublishHandler(mediaServerItemInuse, response, deviceId, channelId); |
| 972 | if (streamInfo != null) { | 971 | if (streamInfo != null) { |
| 972 | + streamInfo.setProgress(0); | ||
| 973 | streamInfo.setStartTime(startTime); | 973 | streamInfo.setStartTime(startTime); |
| 974 | streamInfo.setEndTime(endTime); | 974 | streamInfo.setEndTime(endTime); |
| 975 | - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.DOWNLOAD, deviceId, channelId); | 975 | + InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, streamInfo.getStream()); |
| 976 | if (inviteInfo != null) { | 976 | if (inviteInfo != null) { |
| 977 | + logger.info("[录像下载] 更新invite消息中的stream信息"); | ||
| 977 | inviteInfo.setStatus(InviteSessionStatus.ok); | 978 | inviteInfo.setStatus(InviteSessionStatus.ok); |
| 978 | inviteInfo.setStreamInfo(streamInfo); | 979 | inviteInfo.setStreamInfo(streamInfo); |
| 979 | inviteStreamService.updateInviteInfo(inviteInfo); | 980 | inviteStreamService.updateInviteInfo(inviteInfo); |
web_src/src/components/GBRecordDetail.vue
| @@ -182,9 +182,11 @@ | @@ -182,9 +182,11 @@ | ||
| 182 | this.playerStyle["height"] = this.winHeight + "px"; | 182 | this.playerStyle["height"] = this.winHeight + "px"; |
| 183 | this.chooseDate = moment().format('YYYY-MM-DD') | 183 | this.chooseDate = moment().format('YYYY-MM-DD') |
| 184 | this.dateChange(); | 184 | this.dateChange(); |
| 185 | + window.addEventListener('beforeunload', this.stopPlayRecord) | ||
| 185 | }, | 186 | }, |
| 186 | destroyed() { | 187 | destroyed() { |
| 187 | this.$destroy('recordVideoPlayer'); | 188 | this.$destroy('recordVideoPlayer'); |
| 189 | + window.removeEventListener('beforeunload', this.stopPlayRecord) | ||
| 188 | }, | 190 | }, |
| 189 | methods: { | 191 | methods: { |
| 190 | dateChange(){ | 192 | dateChange(){ |
| @@ -338,14 +340,18 @@ | @@ -338,14 +340,18 @@ | ||
| 338 | }); | 340 | }); |
| 339 | }, | 341 | }, |
| 340 | stopPlayRecord: function (callback) { | 342 | stopPlayRecord: function (callback) { |
| 341 | - this.$refs["recordVideoPlayer"].pause(); | ||
| 342 | - this.videoUrl = ''; | ||
| 343 | - this.$axios({ | ||
| 344 | - method: 'get', | ||
| 345 | - url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId | ||
| 346 | - }).then(function (res) { | ||
| 347 | - if (callback) callback() | ||
| 348 | - }); | 343 | + console.log("停止录像回放") |
| 344 | + if (this.streamId !== "") { | ||
| 345 | + this.$refs["recordVideoPlayer"].pause(); | ||
| 346 | + this.videoUrl = ''; | ||
| 347 | + this.$axios({ | ||
| 348 | + method: 'get', | ||
| 349 | + url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId | ||
| 350 | + }).then(function (res) { | ||
| 351 | + if (callback) callback() | ||
| 352 | + }); | ||
| 353 | + } | ||
| 354 | + | ||
| 349 | }, | 355 | }, |
| 350 | getDataWidth(item){ | 356 | getDataWidth(item){ |
| 351 | let timeForFile = this.getTimeForFile(item); | 357 | let timeForFile = this.getTimeForFile(item); |
| @@ -423,8 +429,14 @@ | @@ -423,8 +429,14 @@ | ||
| 423 | return hStr + ":" + mStr + ":" + sStr | 429 | return hStr + ":" + mStr + ":" + sStr |
| 424 | }, | 430 | }, |
| 425 | goBack(){ | 431 | goBack(){ |
| 432 | + // 如果正在进行录像回放则,发送停止 | ||
| 433 | + if (this.streamId !== "") { | ||
| 434 | + this.stopPlayRecord(()=> { | ||
| 435 | + this.streamId = ""; | ||
| 436 | + }) | ||
| 437 | + } | ||
| 426 | window.history.go(-1); | 438 | window.history.go(-1); |
| 427 | - } | 439 | + }, |
| 428 | } | 440 | } |
| 429 | }; | 441 | }; |
| 430 | </script> | 442 | </script> |
web_src/src/components/dialog/recordDownload.vue
| @@ -21,7 +21,7 @@ import moment from "moment"; | @@ -21,7 +21,7 @@ import moment from "moment"; | ||
| 21 | export default { | 21 | export default { |
| 22 | name: 'recordDownload', | 22 | name: 'recordDownload', |
| 23 | created() { | 23 | created() { |
| 24 | - | 24 | + window.addEventListener('beforeunload', this.stopDownloadRecord) |
| 25 | 25 | ||
| 26 | }, | 26 | }, |
| 27 | data() { | 27 | data() { |
| @@ -197,6 +197,9 @@ export default { | @@ -197,6 +197,9 @@ export default { | ||
| 197 | console.log(error); | 197 | console.log(error); |
| 198 | }); | 198 | }); |
| 199 | } | 199 | } |
| 200 | + }, | ||
| 201 | + destroyed() { | ||
| 202 | + window.removeEventListener('beforeunload', this.stopDownloadRecord) | ||
| 200 | } | 203 | } |
| 201 | }; | 204 | }; |
| 202 | </script> | 205 | </script> |