Commit 7e136c9ac7265bedfdb79b4bca465965486e0541

Authored by 648540858
1 parent ad36354e

完成下载文件的前后调试

src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -761,7 +761,7 @@ public class ZLMHttpHookListener {
761 761 taskExecutor.execute(() -> {
762 762 JSONObject json = (JSONObject) JSON.toJSON(param);
763 763 List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
764   - if (subscribes != null && subscribes.size() > 0) {
  764 + if (subscribes != null && !subscribes.isEmpty()) {
765 765 for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
766 766 subscribe.response(null, param);
767 767 }
... ... @@ -780,7 +780,14 @@ public class ZLMHttpHookListener {
780 780 logger.info("[ZLM HOOK] 录像完成事件:{}->{}", param.getMediaServerId(), param.getFile_path());
781 781  
782 782 taskExecutor.execute(() -> {
  783 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_record_mp4);
  784 + if (subscribes != null && !subscribes.isEmpty()) {
  785 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  786 + subscribe.response(null, param);
  787 + }
  788 + }
783 789 cloudRecordService.addRecord(param);
  790 +
784 791 });
785 792  
786 793 return HookResult.SUCCESS();
... ...
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
... ... @@ -45,5 +45,4 @@ public interface IPlayService {
45 45  
46 46 void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback);
47 47  
48   - void getFilePath(String deviceId, String channelId, String stream, ErrorCallback<DownloadFileInfo> callback);
49 48 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
... ... @@ -49,9 +49,11 @@ import java.io.File;
49 49 import java.math.BigDecimal;
50 50 import java.math.RoundingMode;
51 51 import java.text.ParseException;
  52 +import java.time.Instant;
52 53 import java.util.List;
53 54 import java.util.UUID;
54 55 import java.util.Vector;
  56 +import java.util.concurrent.TimeUnit;
55 57  
56 58 @SuppressWarnings(value = {"rawtypes", "unchecked"})
57 59 @Service
... ... @@ -718,6 +720,28 @@ public class PlayServiceImpl implements IPlayService {
718 720 // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题
719 721 InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId,
720 722 downLoadTimeOutTaskKey, callback, inviteInfo, InviteSessionType.DOWNLOAD);
  723 +
  724 + // 注册录像回调事件,录像下载结束后写入下载地址
  725 + ZlmHttpHookSubscribe.Event hookEventForRecord = (mediaServerItemInuse, hookParam) -> {
  726 + logger.info("[录像下载] 收到录像写入磁盘消息: , {}/{}-{}",
  727 + inviteInfo.getDeviceId(), inviteInfo.getChannelId(), ssrcInfo.getStream());
  728 + logger.info("[录像下载] 收到录像写入磁盘消息内容: " + hookParam);
  729 + OnRecordMp4HookParam recordMp4HookParam = (OnRecordMp4HookParam)hookParam;
  730 + String filePath = recordMp4HookParam.getFile_path();
  731 + DownloadFileInfo downloadFileInfo = getDownloadFilePath(mediaServerItem, filePath);
  732 + InviteInfo inviteInfoForNew = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId()
  733 + , inviteInfo.getChannelId(), inviteInfo.getStream());
  734 + inviteInfoForNew.getStreamInfo().setDownLoadFilePath(downloadFileInfo);
  735 + inviteStreamService.updateInviteInfo(inviteInfoForNew);
  736 + };
  737 + HookSubscribeForRecordMp4 hookSubscribe = HookSubscribeFactory.on_record_mp4(
  738 + mediaServerItem.getId(), "rtp", ssrcInfo.getStream());
  739 +
  740 + // 设置过期时间,下载失败时自动处理订阅数据
  741 +// long difference = DateUtil.getDifference(startTime, endTime)/1000;
  742 +// Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(difference * 2));
  743 +// hookSubscribe.setExpires(expiresInstant);
  744 + subscribe.addSubscribe(hookSubscribe, hookEventForRecord);
721 745 });
722 746 } catch (InvalidArgumentException | SipException | ParseException e) {
723 747 logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
... ... @@ -791,76 +815,15 @@ public class PlayServiceImpl implements IPlayService {
791 815 BigDecimal totalCount = new BigDecimal((end - start) * 1000);
792 816 BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP);
793 817 double process = divide.doubleValue();
  818 + if (process > 0.999) {
  819 + process = 1.0;
  820 + }
794 821 inviteInfo.getStreamInfo().setProgress(process);
795 822 }
796 823 inviteStreamService.updateInviteInfo(inviteInfo);
797 824 return inviteInfo.getStreamInfo();
798 825 }
799 826  
800   - @Override
801   - public void getFilePath(String deviceId, String channelId, String stream, ErrorCallback<DownloadFileInfo> callback) {
802   - InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, stream);
803   - if (inviteInfo == null || inviteInfo.getStreamInfo() == null) {
804   - logger.warn("[获取录像下载文件地址] 未查询到录像下载的信息, {}/{}-{}", deviceId, channelId, stream);
805   - callback.run(ErrorCode.ERROR100.getCode(), "未查询到录像下载的信息", null);
806   - return ;
807   - }
808   -
809   - if (!ObjectUtils.isEmpty(inviteInfo.getStreamInfo().getDownLoadFilePath())) {
810   - callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(),
811   - inviteInfo.getStreamInfo().getDownLoadFilePath());
812   - return;
813   - }
814   -
815   - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo("rtp", stream);
816   - if (streamAuthorityInfo == null) {
817   - logger.warn("[获取录像下载文件地址] 未查询到录像的视频信息, {}/{}-{}", deviceId, channelId, stream);
818   - callback.run(ErrorCode.ERROR100.getCode(), "未查询到录像的视频信息", null);
819   - return ;
820   - }
821   -
822   - // 获取当前已下载时长
823   - String mediaServerId = inviteInfo.getStreamInfo().getMediaServerId();
824   - MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
825   - if (mediaServerItem == null) {
826   - logger.warn("[获取录像下载文件地址] 查询录像信息时发现节点不存在, {}/{}-{}", deviceId, channelId, stream);
827   - callback.run(ErrorCode.ERROR100.getCode(), "查询录像信息时发现节点不存在", null);
828   - return ;
829   - }
830   -
831   - List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.getListByCallId(streamAuthorityInfo.getCallId());
832   - if (!cloudRecordItemList.isEmpty()) {
833   - String filePath = cloudRecordItemList.get(0).getFilePath();
834   -
835   - DownloadFileInfo downloadFileInfo = getDownloadFilePath(mediaServerItem, filePath);
836   - inviteInfo.getStreamInfo().setDownLoadFilePath(downloadFileInfo);
837   - inviteStreamService.updateInviteInfo(inviteInfo);
838   - callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), downloadFileInfo);
839   - }else {
840   - // 可能尚未生成,那就监听hook等着收到对应的录像通知
841   - ZlmHttpHookSubscribe.Event hookEvent = (mediaServerItemInuse, hookParam) -> {
842   - logger.info("[录像下载]收到订阅消息: , {}/{}-{}", deviceId, channelId, stream);
843   - logger.info("[录像下载]收到订阅消息内容: " + hookParam);
844   - dynamicTask.stop(streamAuthorityInfo.getCallId());
845   - OnRecordMp4HookParam recordMp4HookParam = (OnRecordMp4HookParam)hookParam;
846   - String filePath = recordMp4HookParam.getFile_path();
847   - DownloadFileInfo downloadFileInfo = getDownloadFilePath(mediaServerItem, filePath);
848   - inviteInfo.getStreamInfo().setDownLoadFilePath(downloadFileInfo);
849   - inviteStreamService.updateInviteInfo(inviteInfo);
850   - callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), downloadFileInfo);
851   - };
852   - HookSubscribeForRecordMp4 hookSubscribe = HookSubscribeFactory.on_record_mp4(mediaServerId, "rtp", stream);
853   - subscribe.addSubscribe(hookSubscribe, hookEvent);
854   -
855   - // 设置超时,超时结束监听
856   - dynamicTask.startDelay(streamAuthorityInfo.getCallId(), ()->{
857   - logger.info("[录像下载] 接收hook超时, {}/{}-{}", deviceId, channelId, stream);
858   - subscribe.removeSubscribe(hookSubscribe);
859   - callback.run(ErrorCode.ERROR100.getCode(), "接收hook超时", null);
860   - }, 10000);
861   - }
862   - }
863   -
864 827 private DownloadFileInfo getDownloadFilePath(MediaServerItem mediaServerItem, String filePath) {
865 828 DownloadFileInfo downloadFileInfo = new DownloadFileInfo();
866 829  
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java
1 1 package com.genersoft.iot.vmp.vmanager.bean;
2 2  
3 3 import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
4 5 import io.swagger.v3.oas.annotations.media.Schema;
5 6  
6 7 @Schema(description = "流信息")
... ... @@ -93,6 +94,9 @@ public class StreamContent {
93 94 @Schema(description = "结束时间")
94 95 private String endTime;
95 96  
  97 + @Schema(description = "文件下载地址(录像下载使用)")
  98 + private DownloadFileInfo downLoadFilePath;
  99 +
96 100 private double progress;
97 101  
98 102 public StreamContent(StreamInfo streamInfo) {
... ... @@ -170,6 +174,10 @@ public class StreamContent {
170 174 this.startTime = streamInfo.getStartTime();
171 175 this.endTime = streamInfo.getEndTime();
172 176 this.progress = streamInfo.getProgress();
  177 +
  178 + if (streamInfo.getDownLoadFilePath() != null) {
  179 + this.downLoadFilePath = streamInfo.getDownLoadFilePath();
  180 + }
173 181 }
174 182  
175 183 public String getApp() {
... ... @@ -411,4 +419,12 @@ public class StreamContent {
411 419 public void setProgress(double progress) {
412 420 this.progress = progress;
413 421 }
  422 +
  423 + public DownloadFileInfo getDownLoadFilePath() {
  424 + return downLoadFilePath;
  425 + }
  426 +
  427 + public void setDownLoadFilePath(DownloadFileInfo downLoadFilePath) {
  428 + this.downLoadFilePath = downLoadFilePath;
  429 + }
414 430 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
... ... @@ -212,32 +212,4 @@ public class GBRecordController {
212 212 }
213 213 return new StreamContent(downLoadInfo);
214 214 }
215   -
216   - @Operation(summary = "获取历史媒体下载文件地址")
217   - @Parameter(name = "deviceId", description = "设备国标编号", required = true)
218   - @Parameter(name = "channelId", description = "通道国标编号", required = true)
219   - @Parameter(name = "stream", description = "流ID", required = true)
220   - @GetMapping("/download/file/path/{deviceId}/{channelId}/{stream}")
221   - public DeferredResult<WVPResult<DownloadFileInfo>> getDownloadFilePath(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) {
222   -
223   - DeferredResult<WVPResult<DownloadFileInfo>> result = new DeferredResult<>();
224   -
225   - result.onTimeout(()->{
226   - WVPResult<DownloadFileInfo> wvpResult = new WVPResult<>();
227   - wvpResult.setCode(ErrorCode.ERROR100.getCode());
228   - wvpResult.setMsg("timeout");
229   - result.setResult(wvpResult);
230   - });
231   -
232   - playService.getFilePath(deviceId, channelId, stream, (code, msg, data)->{
233   - WVPResult<DownloadFileInfo> wvpResult = new WVPResult<>();
234   - wvpResult.setCode(code);
235   - wvpResult.setMsg(msg);
236   - wvpResult.setData(data);
237   - result.setResult(wvpResult);
238   - });
239   -
240   -
241   - return result;
242   - }
243 215 }
... ...
web_src/build/webpack.dev.conf.js
... ... @@ -10,7 +10,6 @@ const HtmlWebpackPlugin = require(&#39;html-webpack-plugin&#39;)
10 10 const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
11 11 const portfinder = require('portfinder')
12 12  
13   -const HOST = process.env.HOST
14 13 const PORT = process.env.PORT && Number(process.env.PORT)
15 14  
16 15 const devWebpackConfig = merge(baseWebpackConfig, {
... ... @@ -31,9 +30,8 @@ const devWebpackConfig = merge(baseWebpackConfig, {
31 30 hot: true,
32 31 contentBase: false, // since we use CopyWebpackPlugin.
33 32 compress: true,
34   - host: HOST || config.dev.host,
35   - // host:'127.0.0.1',
36   - port: PORT || config.dev.port,
  33 + host: config.dev.host,
  34 + port: config.dev.port,
37 35 open: config.dev.autoOpenBrowser,
38 36 overlay: config.dev.errorOverlay
39 37 ? { warnings: false, errors: true }
... ...
web_src/src/components/dialog/recordDownload.vue
... ... @@ -38,7 +38,6 @@ export default {
38 38 streamInfo: null,
39 39 taskId: null,
40 40 getProgressRun: false,
41   - getProgressForFileRun: false,
42 41 timer: null,
43 42 downloadFile: null,
44 43  
... ... @@ -61,7 +60,7 @@ export default {
61 60 return;
62 61 }
63 62 if (this.percentage == 100 ) {
64   - this.getFileDownload();
  63 +
65 64 return;
66 65 }
67 66 setTimeout( ()=>{
... ... @@ -74,7 +73,6 @@ export default {
74 73 method: 'get',
75 74 url: `/api/gb_record/download/progress/${this.deviceId}/${this.channelId}/${this.stream}`
76 75 }).then((res)=> {
77   - console.log(res)
78 76 if (res.data.code === 0) {
79 77 this.streamInfo = res.data.data;
80 78 if (parseFloat(res.data.progress) == 1) {
... ... @@ -82,6 +80,15 @@ export default {
82 80 }else {
83 81 this.percentage = (parseFloat(res.data.data.progress)*100).toFixed(1);
84 82 }
  83 + if (this.streamInfo.downLoadFilePath) {
  84 + if (location.protocol === "https:") {
  85 + this.downloadFile = this.streamInfo.downLoadFilePath.httpsPath;
  86 + }else {
  87 + this.downloadFile = this.streamInfo.downLoadFilePath.httpPath;
  88 + }
  89 + this.getProgressRun = false;
  90 + this.downloadFileClientEvent()
  91 + }
85 92 if (callback)callback();
86 93 }else {
87 94 this.$message({
... ... @@ -107,24 +114,11 @@ export default {
107 114 }
108 115 this.showDialog=false;
109 116 this.getProgressRun = false;
110   - this.getProgressForFileRun = false;
111 117 },
112 118 gbScale: function (scale){
113 119 this.scale = scale;
114 120 },
115   - download: function (){
116   - this.getProgressRun = false;
117   - if (this.streamInfo != null ) {
118   - if (this.streamInfo.progress < 1) {
119   - // 发送停止缓存
120   - this.stopDownloadRecord((res)=>{
121   - this.getFileDownload()
122   - })
123   - }else {
124   - this.getFileDownload()
125   - }
126   - }
127   - },
  121 +
128 122 stopDownloadRecord: function (callback) {
129 123 this.$axios({
130 124 method: 'get',
... ... @@ -133,74 +127,20 @@ export default {
133 127 if (callback) callback(res)
134 128 });
135 129 },
136   - getFileDownload: function (){
137   - this.$axios({
138   - method: 'get',
139   - url:`/api/cloud/record/task/add`,
140   - params: {
141   - app: this.app,
142   - stream: this.stream,
143   - mediaServerId: this.mediaServerId,
144   - startTime: null,
145   - endTime: null,
146   - }
147   - }).then((res) =>{
148   - if (res.data.code === 0 ) {
149   - // 查询进度
150   - this.title = "录像文件处理中..."
151   - this.taskId = res.data.data;
152   - this.percentage = 0.0;
153   - this.getProgressForFileRun = true;
154   - this.getProgressForFileTimer();
155   - }
156   - }).catch(function (error) {
157   - console.log(error);
158   - });
159   - },
160   - getProgressForFileTimer: function (){
161   - if (!this.getProgressForFileRun || this.percentage == 100) {
162   - return;
163   - }
164   - setTimeout( ()=>{
165   - if (!this.showDialog) return;
166   - this.getProgressForFile(this.getProgressForFileTimer)
167   - }, 1000)
168   - },
169   - getProgressForFile: function (callback){
170   - this.$axios({
171   - method: 'get',
172   - url:`/api/cloud/record/task/list`,
173   - params: {
174   - mediaServerId: this.mediaServerId,
175   - taskId: this.taskId,
176   - isEnd: true,
177   - }
178   - }).then((res) => {
179   - console.log(res)
180   - if (res.data.code === 0) {
181   - if (res.data.data.length === 0){
182   - this.percentage = 0
183   - // 往往在多次请求后(实验五分钟的视频是三次请求),才会返回数据,第一次请求通常是返回空数组
184   - if (callback)callback()
185   - return
186   - }
187   - // res.data.data应是数组类型
188   - this.percentage = parseFloat(res.data.data[0].percentage)*100
189   - if (res.data.data[0].percentage === '1') {
190   - this.getProgressForFileRun = false;
191   - this.downloadFile = res.data.data[0].downloadFile
192   - this.title = "文件处理完成,点击按扭下载"
193   - // window.open(res.data.data[0].downloadFile)
194   - }else {
195   - if (callback)callback()
196   - }
197   - }
198   - }).catch(function (error) {
199   - console.log(error);
200   - });
201   - },
202 130 downloadFileClientEvent: function (){
203   - window.open(this.downloadFile )
  131 + // window.open(this.downloadFile )
  132 +
  133 + let x = new XMLHttpRequest();
  134 + x.open("GET", this.downloadFile, true);
  135 + x.responseType = 'blob';
  136 + x.onload=(e)=> {
  137 + let url = window.URL.createObjectURL(x.response)
  138 + let a = document.createElement('a');
  139 + a.href = url
  140 + a.download = this.deviceId + "-" + this.channelId + ".mp4";
  141 + a.click()
  142 + }
  143 + x.send();
204 144 }
205 145 },
206 146 destroyed() {
... ...