Просмотр исходного кода

完成下载文件的前后调试

648540858 1 год назад
Родитель
Сommit
7e136c9ac7

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

@@ -761,7 +761,7 @@ public class ZLMHttpHookListener {
         taskExecutor.execute(() -> {
             JSONObject json = (JSONObject) JSON.toJSON(param);
             List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
-            if (subscribes != null && subscribes.size() > 0) {
+            if (subscribes != null && !subscribes.isEmpty()) {
                 for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
                     subscribe.response(null, param);
                 }
@@ -780,7 +780,14 @@ public class ZLMHttpHookListener {
         logger.info("[ZLM HOOK] 录像完成事件:{}->{}", param.getMediaServerId(), param.getFile_path());
 
         taskExecutor.execute(() -> {
+            List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_record_mp4);
+            if (subscribes != null && !subscribes.isEmpty()) {
+                for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
+                    subscribe.response(null, param);
+                }
+            }
             cloudRecordService.addRecord(param);
+
         });
 
         return HookResult.SUCCESS();

+ 0 - 1
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java

@@ -45,5 +45,4 @@ public interface IPlayService {
 
     void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback);
 
-    void getFilePath(String deviceId, String channelId, String stream, ErrorCallback<DownloadFileInfo> callback);
 }

+ 27 - 64
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java

@@ -49,9 +49,11 @@ import java.io.File;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.ParseException;
+import java.time.Instant;
 import java.util.List;
 import java.util.UUID;
 import java.util.Vector;
+import java.util.concurrent.TimeUnit;
 
 @SuppressWarnings(value = {"rawtypes", "unchecked"})
 @Service
@@ -718,6 +720,28 @@ public class PlayServiceImpl implements IPlayService {
                         // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题
                         InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId,
                                 downLoadTimeOutTaskKey, callback, inviteInfo, InviteSessionType.DOWNLOAD);
+
+                        // 注册录像回调事件,录像下载结束后写入下载地址
+                        ZlmHttpHookSubscribe.Event hookEventForRecord = (mediaServerItemInuse, hookParam) -> {
+                            logger.info("[录像下载] 收到录像写入磁盘消息: , {}/{}-{}",
+                                    inviteInfo.getDeviceId(), inviteInfo.getChannelId(), ssrcInfo.getStream());
+                            logger.info("[录像下载] 收到录像写入磁盘消息内容: " + hookParam);
+                            OnRecordMp4HookParam recordMp4HookParam = (OnRecordMp4HookParam)hookParam;
+                            String filePath = recordMp4HookParam.getFile_path();
+                            DownloadFileInfo downloadFileInfo = getDownloadFilePath(mediaServerItem, filePath);
+                            InviteInfo inviteInfoForNew = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId()
+                                    , inviteInfo.getChannelId(), inviteInfo.getStream());
+                            inviteInfoForNew.getStreamInfo().setDownLoadFilePath(downloadFileInfo);
+                            inviteStreamService.updateInviteInfo(inviteInfoForNew);
+                        };
+                        HookSubscribeForRecordMp4 hookSubscribe = HookSubscribeFactory.on_record_mp4(
+                                mediaServerItem.getId(), "rtp", ssrcInfo.getStream());
+
+                        // 设置过期时间,下载失败时自动处理订阅数据
+//                        long difference = DateUtil.getDifference(startTime, endTime)/1000;
+//                        Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(difference * 2));
+//                        hookSubscribe.setExpires(expiresInstant);
+                        subscribe.addSubscribe(hookSubscribe, hookEventForRecord);
                     });
         } catch (InvalidArgumentException | SipException | ParseException e) {
             logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
@@ -791,76 +815,15 @@ public class PlayServiceImpl implements IPlayService {
             BigDecimal totalCount = new BigDecimal((end - start) * 1000);
             BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP);
             double process = divide.doubleValue();
+            if (process > 0.999) {
+                process = 1.0;
+            }
             inviteInfo.getStreamInfo().setProgress(process);
         }
         inviteStreamService.updateInviteInfo(inviteInfo);
         return inviteInfo.getStreamInfo();
     }
 
-    @Override
-    public void getFilePath(String deviceId, String channelId, String stream, ErrorCallback<DownloadFileInfo> callback) {
-        InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, stream);
-        if (inviteInfo == null || inviteInfo.getStreamInfo() == null) {
-            logger.warn("[获取录像下载文件地址] 未查询到录像下载的信息, {}/{}-{}", deviceId, channelId, stream);
-            callback.run(ErrorCode.ERROR100.getCode(), "未查询到录像下载的信息", null);
-            return ;
-        }
-
-        if (!ObjectUtils.isEmpty(inviteInfo.getStreamInfo().getDownLoadFilePath())) {
-            callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(),
-                    inviteInfo.getStreamInfo().getDownLoadFilePath());
-            return;
-        }
-
-        StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo("rtp", stream);
-        if (streamAuthorityInfo == null) {
-            logger.warn("[获取录像下载文件地址] 未查询到录像的视频信息, {}/{}-{}", deviceId, channelId, stream);
-            callback.run(ErrorCode.ERROR100.getCode(), "未查询到录像的视频信息", null);
-            return ;
-        }
-
-        // 获取当前已下载时长
-        String mediaServerId = inviteInfo.getStreamInfo().getMediaServerId();
-        MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
-        if (mediaServerItem == null) {
-            logger.warn("[获取录像下载文件地址] 查询录像信息时发现节点不存在, {}/{}-{}", deviceId, channelId, stream);
-            callback.run(ErrorCode.ERROR100.getCode(), "查询录像信息时发现节点不存在", null);
-            return ;
-        }
-
-        List<CloudRecordItem> cloudRecordItemList =  cloudRecordServiceMapper.getListByCallId(streamAuthorityInfo.getCallId());
-        if (!cloudRecordItemList.isEmpty()) {
-            String filePath = cloudRecordItemList.get(0).getFilePath();
-
-            DownloadFileInfo downloadFileInfo = getDownloadFilePath(mediaServerItem, filePath);
-            inviteInfo.getStreamInfo().setDownLoadFilePath(downloadFileInfo);
-            inviteStreamService.updateInviteInfo(inviteInfo);
-            callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), downloadFileInfo);
-        }else {
-            // 可能尚未生成,那就监听hook等着收到对应的录像通知
-            ZlmHttpHookSubscribe.Event hookEvent = (mediaServerItemInuse, hookParam) -> {
-                logger.info("[录像下载]收到订阅消息: , {}/{}-{}", deviceId, channelId, stream);
-                logger.info("[录像下载]收到订阅消息内容: " + hookParam);
-                dynamicTask.stop(streamAuthorityInfo.getCallId());
-                OnRecordMp4HookParam recordMp4HookParam = (OnRecordMp4HookParam)hookParam;
-                String filePath = recordMp4HookParam.getFile_path();
-                DownloadFileInfo downloadFileInfo = getDownloadFilePath(mediaServerItem, filePath);
-                inviteInfo.getStreamInfo().setDownLoadFilePath(downloadFileInfo);
-                inviteStreamService.updateInviteInfo(inviteInfo);
-                callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), downloadFileInfo);
-            };
-            HookSubscribeForRecordMp4 hookSubscribe = HookSubscribeFactory.on_record_mp4(mediaServerId, "rtp", stream);
-            subscribe.addSubscribe(hookSubscribe, hookEvent);
-
-            // 设置超时,超时结束监听
-            dynamicTask.startDelay(streamAuthorityInfo.getCallId(), ()->{
-                logger.info("[录像下载] 接收hook超时, {}/{}-{}", deviceId, channelId, stream);
-                subscribe.removeSubscribe(hookSubscribe);
-                callback.run(ErrorCode.ERROR100.getCode(), "接收hook超时", null);
-            }, 10000);
-        }
-    }
-
     private DownloadFileInfo getDownloadFilePath(MediaServerItem mediaServerItem, String filePath) {
         DownloadFileInfo downloadFileInfo = new DownloadFileInfo();
 

+ 16 - 0
src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java

@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.vmanager.bean;
 
 import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
 import io.swagger.v3.oas.annotations.media.Schema;
 
 @Schema(description = "流信息")
@@ -93,6 +94,9 @@ public class StreamContent {
     @Schema(description = "结束时间")
     private String endTime;
 
+    @Schema(description = "文件下载地址(录像下载使用)")
+    private DownloadFileInfo downLoadFilePath;
+
     private double progress;
 
     public StreamContent(StreamInfo streamInfo) {
@@ -170,6 +174,10 @@ public class StreamContent {
         this.startTime = streamInfo.getStartTime();
         this.endTime = streamInfo.getEndTime();
         this.progress = streamInfo.getProgress();
+
+        if (streamInfo.getDownLoadFilePath() != null) {
+            this.downLoadFilePath = streamInfo.getDownLoadFilePath();
+        }
     }
 
     public String getApp() {
@@ -411,4 +419,12 @@ public class StreamContent {
     public void setProgress(double progress) {
         this.progress = progress;
     }
+
+    public DownloadFileInfo getDownLoadFilePath() {
+        return downLoadFilePath;
+    }
+
+    public void setDownLoadFilePath(DownloadFileInfo downLoadFilePath) {
+        this.downLoadFilePath = downLoadFilePath;
+    }
 }

+ 0 - 28
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java

@@ -212,32 +212,4 @@ public class GBRecordController {
 		}
 		return new StreamContent(downLoadInfo);
 	}
-
-	@Operation(summary = "获取历史媒体下载文件地址")
-	@Parameter(name = "deviceId", description = "设备国标编号", required = true)
-	@Parameter(name = "channelId", description = "通道国标编号", required = true)
-	@Parameter(name = "stream", description = "流ID", required = true)
-	@GetMapping("/download/file/path/{deviceId}/{channelId}/{stream}")
-	public DeferredResult<WVPResult<DownloadFileInfo>> getDownloadFilePath(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) {
-
-		DeferredResult<WVPResult<DownloadFileInfo>> result = new DeferredResult<>();
-
-		result.onTimeout(()->{
-			WVPResult<DownloadFileInfo> wvpResult = new WVPResult<>();
-			wvpResult.setCode(ErrorCode.ERROR100.getCode());
-			wvpResult.setMsg("timeout");
-			result.setResult(wvpResult);
-		});
-
-		playService.getFilePath(deviceId, channelId, stream, (code, msg, data)->{
-			WVPResult<DownloadFileInfo> wvpResult = new WVPResult<>();
-			wvpResult.setCode(code);
-			wvpResult.setMsg(msg);
-			wvpResult.setData(data);
-			result.setResult(wvpResult);
-		});
-
-
-		return result;
-	}
 }

+ 2 - 4
web_src/build/webpack.dev.conf.js

@@ -10,7 +10,6 @@ const HtmlWebpackPlugin = require('html-webpack-plugin')
 const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
 const portfinder = require('portfinder')
 
-const HOST = process.env.HOST
 const PORT = process.env.PORT && Number(process.env.PORT)
 
 const devWebpackConfig = merge(baseWebpackConfig, {
@@ -31,9 +30,8 @@ const devWebpackConfig = merge(baseWebpackConfig, {
     hot: true,
     contentBase: false, // since we use CopyWebpackPlugin.
     compress: true,
-    host: HOST || config.dev.host,
-    // host:'127.0.0.1',
-    port: PORT || config.dev.port,
+    host: config.dev.host,
+    port: config.dev.port,
     open: config.dev.autoOpenBrowser,
     overlay: config.dev.errorOverlay
       ? { warnings: false, errors: true }

+ 24 - 84
web_src/src/components/dialog/recordDownload.vue

@@ -38,7 +38,6 @@ export default {
           streamInfo: null,
           taskId: null,
           getProgressRun: false,
-          getProgressForFileRun: false,
           timer: null,
           downloadFile: null,
 
@@ -61,7 +60,7 @@ export default {
             return;
           }
           if (this.percentage == 100 ) {
-            this.getFileDownload();
+
             return;
           }
           setTimeout( ()=>{
@@ -74,7 +73,6 @@ export default {
             method: 'get',
             url: `/api/gb_record/download/progress/${this.deviceId}/${this.channelId}/${this.stream}`
           }).then((res)=> {
-            console.log(res)
               if (res.data.code === 0) {
                 this.streamInfo = res.data.data;
                 if (parseFloat(res.data.progress) == 1) {
@@ -82,6 +80,15 @@ export default {
                 }else {
                   this.percentage = (parseFloat(res.data.data.progress)*100).toFixed(1);
                 }
+                if (this.streamInfo.downLoadFilePath) {
+                  if (location.protocol === "https:") {
+                    this.downloadFile = this.streamInfo.downLoadFilePath.httpsPath;
+                  }else {
+                    this.downloadFile = this.streamInfo.downLoadFilePath.httpPath;
+                  }
+                  this.getProgressRun = false;
+                  this.downloadFileClientEvent()
+                }
                 if (callback)callback();
               }else {
                 this.$message({
@@ -107,24 +114,11 @@ export default {
           }
           this.showDialog=false;
           this.getProgressRun = false;
-          this.getProgressForFileRun = false;
         },
         gbScale: function (scale){
           this.scale = scale;
         },
-        download: function (){
-          this.getProgressRun = false;
-          if (this.streamInfo != null ) {
-            if (this.streamInfo.progress < 1) {
-              // 发送停止缓存
-              this.stopDownloadRecord((res)=>{
-                  this.getFileDownload()
-              })
-            }else {
-              this.getFileDownload()
-            }
-          }
-        },
+
         stopDownloadRecord: function (callback) {
           this.$axios({
             method: 'get',
@@ -133,74 +127,20 @@ export default {
             if (callback) callback(res)
           });
         },
-        getFileDownload: function (){
-          this.$axios({
-            method: 'get',
-            url:`/api/cloud/record/task/add`,
-            params: {
-              app: this.app,
-              stream: this.stream,
-              mediaServerId: this.mediaServerId,
-              startTime: null,
-              endTime: null,
-            }
-          }).then((res) =>{
-            if (res.data.code === 0 ) {
-              // 查询进度
-              this.title = "录像文件处理中..."
-              this.taskId = res.data.data;
-              this.percentage = 0.0;
-              this.getProgressForFileRun = true;
-              this.getProgressForFileTimer();
-            }
-          }).catch(function (error) {
-            console.log(error);
-          });
-        },
-        getProgressForFileTimer: function (){
-          if (!this.getProgressForFileRun || this.percentage == 100) {
-            return;
-          }
-          setTimeout( ()=>{
-            if (!this.showDialog) return;
-            this.getProgressForFile(this.getProgressForFileTimer)
-          }, 1000)
-        },
-        getProgressForFile: function (callback){
-          this.$axios({
-            method: 'get',
-            url:`/api/cloud/record/task/list`,
-            params: {
-              mediaServerId: this.mediaServerId,
-              taskId: this.taskId,
-              isEnd: true,
-            }
-          }).then((res) => {
-            console.log(res)
-            if (res.data.code === 0) {
-              if (res.data.data.length === 0){
-                this.percentage = 0
-                // 往往在多次请求后(实验五分钟的视频是三次请求),才会返回数据,第一次请求通常是返回空数组
-                if (callback)callback()
-                return
-              }
-              // res.data.data应是数组类型
-                this.percentage = parseFloat(res.data.data[0].percentage)*100
-                 if (res.data.data[0].percentage === '1') {
-                   this.getProgressForFileRun = false;
-                   this.downloadFile = res.data.data[0].downloadFile
-                   this.title = "文件处理完成,点击按扭下载"
-                   // window.open(res.data.data[0].downloadFile)
-                 }else {
-                   if (callback)callback()
-                 }
-            }
-          }).catch(function (error) {
-            console.log(error);
-          });
-        },
       downloadFileClientEvent: function (){
-        window.open(this.downloadFile )
+        // window.open(this.downloadFile )
+
+        let x = new XMLHttpRequest();
+        x.open("GET", this.downloadFile, true);
+        x.responseType = 'blob';
+        x.onload=(e)=> {
+          let url = window.URL.createObjectURL(x.response)
+          let a = document.createElement('a');
+          a.href = url
+          a.download = this.deviceId + "-" + this.channelId + ".mp4";
+          a.click()
+        }
+        x.send();
       }
     },
     destroyed() {