Bläddra i källkod

支持级联国标录像下载

648540858 2 år sedan
förälder
incheckning
a2da81f79a

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamType.java

@@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.gb28181.bean;
 
 public enum InviteStreamType {
 
-    PLAY,PLAYBACK,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY
+    PLAY,PLAYBACK,DOWNLOAD,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY
 
 
 }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java

@@ -540,7 +540,7 @@ public class SIPCommander implements ISIPCommander {
 
         content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
         logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc());
-        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
+        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
         // 添加订阅
         CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
         String callId= newCallIdHeader.getCallId();

+ 35 - 3
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java

@@ -429,8 +429,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     InviteErrorCallback<Object> errorEvent = ((statusCode, msg, data) -> {
                         // 未知错误。直接转发设备点播的错误
                         try {
-                            Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
-                            sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
+                            if (statusCode > 0) {
+                                Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
+                                sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
+                            }
                         } catch (ParseException | SipException  e) {
                             logger.error("未处理的异常 ", e);
                         }
@@ -455,7 +457,37 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                                         errorEvent.run(code, msg, data);
                                     }
                                 });
-                    } else {
+                    }else if ("Download".equalsIgnoreCase(sessionName)) {
+                        // 获取指定的下载速度
+                        Vector sdpMediaDescriptions = sdp.getMediaDescriptions(true);
+                        MediaDescription mediaDescription = null;
+                        String downloadSpeed = "1";
+                        if (sdpMediaDescriptions.size() > 0) {
+                            mediaDescription = (MediaDescription)sdpMediaDescriptions.get(0);
+                        }
+                        if (mediaDescription != null) {
+                            downloadSpeed = mediaDescription.getAttribute("downloadspeed");
+                        }
+
+                        sendRtpItem.setPlayType(InviteStreamType.DOWNLOAD);
+                        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam());
+                        sendRtpItem.setStreamId(ssrcInfo.getStream());
+                        // 写入redis, 超时时回复
+                        redisCatchStorage.updateSendRTPSever(sendRtpItem);
+                        playService.download(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start),
+                                DateUtil.formatter.format(end), Integer.parseInt(downloadSpeed),
+                                (code, msg, data) -> {
+                                    if (code == InviteErrorCode.SUCCESS.getCode()){
+                                        hookEvent.run(code, msg, data);
+                                    }else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()){
+                                        logger.info("[录像下载]超时, 用户:{}, 通道:{}", username, channelId);
+                                        redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
+                                        errorEvent.run(code, msg, data);
+                                    }else {
+                                        errorEvent.run(code, msg, data);
+                                    }
+                                });
+                    }else {
                         sendRtpItem.setPlayType(InviteStreamType.PLAY);
                         String streamId = null;
                         if (mediaServerItem.isRtpEnable()) {

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java

@@ -438,7 +438,7 @@ public class ZLMHttpHookListener {
     @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
     public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) {
 
-        logger.info("[ZLM HOOK]流无人观看:{]->{}->{}/{}" + param.getMediaServerId(), param.getSchema(),
+        logger.info("[ZLM HOOK]流无人观看:{}->{}->{}/{}",  param.getMediaServerId(), param.getSchema(),
                 param.getApp(), param.getStream());
         JSONObject ret = new JSONObject();
         ret.put("code", 0);

+ 4 - 2
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java

@@ -501,8 +501,10 @@ public class DeviceServiceImpl implements IDeviceService {
             node.setBasicData(channel);
             node.setParent(false);
             if (channel.getChannelId().length() > 8) {
-                String gbCodeType = channel.getChannelId().substring(10, 13);
-                node.setParent(gbCodeType.equals(ChannelIdType.BUSINESS_GROUP) || gbCodeType.equals(ChannelIdType.VIRTUAL_ORGANIZATION) );
+                if (channel.getChannelId().length() > 13) {
+                    String gbCodeType = channel.getChannelId().substring(10, 13);
+                    node.setParent(gbCodeType.equals(ChannelIdType.BUSINESS_GROUP) || gbCodeType.equals(ChannelIdType.VIRTUAL_ORGANIZATION) );
+                }
             }else {
                 node.setParent(true);
             }

+ 4 - 3
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java

@@ -757,7 +757,7 @@ public class PlayServiceImpl implements IPlayService {
                     null);
             return;
         }
-        logger.info("[录像下载] deviceId: {}, channelId: {},收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
+        logger.info("[录像下载] deviceId: {}, channelId: {}, 下载速度:{}, 收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, downloadSpeed, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
         // 初始化redis中的invite消息状态
         InviteInfo inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo,
                 mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.DOWNLOAD,
@@ -888,7 +888,6 @@ public class PlayServiceImpl implements IPlayService {
                                         cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);
                                     } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
                                         logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());
-
                                     }
 
                                     dynamicTask.stop(downLoadTimeOutTaskKey);
@@ -970,10 +969,12 @@ public class PlayServiceImpl implements IPlayService {
     private StreamInfo onPublishHandlerForDownload(MediaServerItem mediaServerItemInuse, JSONObject response, String deviceId, String channelId, String startTime, String endTime) {
         StreamInfo streamInfo = onPublishHandler(mediaServerItemInuse, response, deviceId, channelId);
         if (streamInfo != null) {
+            streamInfo.setProgress(0);
             streamInfo.setStartTime(startTime);
             streamInfo.setEndTime(endTime);
-            InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.DOWNLOAD, deviceId, channelId);
+            InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, streamInfo.getStream());
             if (inviteInfo != null) {
+                logger.info("[录像下载] 更新invite消息中的stream信息");
                 inviteInfo.setStatus(InviteSessionStatus.ok);
                 inviteInfo.setStreamInfo(streamInfo);
                 inviteStreamService.updateInviteInfo(inviteInfo);

+ 21 - 9
web_src/src/components/GBRecordDetail.vue

@@ -182,9 +182,11 @@
       this.playerStyle["height"] = this.winHeight + "px";
       this.chooseDate = moment().format('YYYY-MM-DD')
       this.dateChange();
+      window.addEventListener('beforeunload', this.stopPlayRecord)
 		},
 		destroyed() {
 			this.$destroy('recordVideoPlayer');
+      window.removeEventListener('beforeunload', this.stopPlayRecord)
 		},
 		methods: {
       dateChange(){
@@ -338,14 +340,18 @@
         });
       },
       stopPlayRecord: function (callback) {
-        this.$refs["recordVideoPlayer"].pause();
-        this.videoUrl = '';
-        this.$axios({
-          method: 'get',
-          url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId
-        }).then(function (res) {
-          if (callback) callback()
-        });
+        console.log("停止录像回放")
+        if (this.streamId !== "") {
+          this.$refs["recordVideoPlayer"].pause();
+          this.videoUrl = '';
+          this.$axios({
+            method: 'get',
+            url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId
+          }).then(function (res) {
+            if (callback) callback()
+          });
+        }
+
       },
       getDataWidth(item){
         let timeForFile = this.getTimeForFile(item);
@@ -423,8 +429,14 @@
         return hStr + ":" + mStr + ":" + sStr
       },
       goBack(){
+        // 如果正在进行录像回放则,发送停止
+        if (this.streamId !== "") {
+          this.stopPlayRecord(()=> {
+            this.streamId = "";
+          })
+        }
         window.history.go(-1);
-      }
+      },
 		}
 	};
 </script>

+ 4 - 1
web_src/src/components/dialog/recordDownload.vue

@@ -21,7 +21,7 @@ import moment from "moment";
 export default {
     name: 'recordDownload',
     created() {
-
+      window.addEventListener('beforeunload', this.stopDownloadRecord)
 
     },
     data() {
@@ -197,6 +197,9 @@ export default {
             console.log(error);
           });
         }
+    },
+    destroyed() {
+      window.removeEventListener('beforeunload', this.stopDownloadRecord)
     }
 };
 </script>