From fd306d8ede695e4cea8b1c9c2d264b78da45c6e0 Mon Sep 17 00:00:00 2001 From: lin <648540858@qq.com> Date: Fri, 19 Sep 2025 18:02:53 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BA=91=E7=AB=AF=E5=BD=95=E5=83=8F=E5=9B=9E?= =?UTF-8?q?=E6=94=BE=E4=BD=BF=E7=94=A8=E5=8D=95=E6=96=87=E4=BB=B6=E6=92=AD?= =?UTF-8?q?=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../genersoft/iot/vmp/common/StreamInfo.java | 2 + .../media/abl/ABLMediaNodeServerService.java | 16 +-- .../iot/vmp/media/abl/ABLRESTfulUtils.java | 2 +- .../iot/vmp/media/abl/bean/ABLResult.java | 1 + .../service/IMediaNodeServerService.java | 4 +- .../media/service/IMediaServerService.java | 4 +- .../service/impl/MediaServerServiceImpl.java | 16 ++- .../media/zlm/ZLMMediaNodeServerService.java | 31 +++++- .../iot/vmp/service/ICloudRecordService.java | 4 +- .../service/impl/CloudRecordServiceImpl.java | 33 +++++- .../iot/vmp/vmanager/bean/StreamContent.java | 4 + .../cloudRecord/CloudRecordController.java | 8 +- web/src/api/cloudRecord.js | 4 +- web/src/views/cloudRecord/detail.vue | 104 ++++++++++++------ 14 files changed, 171 insertions(+), 62 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java index f2d1df4cb..78c93e523 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java @@ -79,6 +79,8 @@ public class StreamInfo implements Serializable, Cloneable{ private String startTime; @Schema(description = "结束时间") private String endTime; + @Schema(description = "时长(回放时使用)") + private Double duration; @Schema(description = "进度(录像下载使用)") private double progress; @Schema(description = "文件下载地址(录像下载使用)") diff --git a/src/main/java/com/genersoft/iot/vmp/media/abl/ABLMediaNodeServerService.java b/src/main/java/com/genersoft/iot/vmp/media/abl/ABLMediaNodeServerService.java index 3c1333bc1..f8e0bc600 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/abl/ABLMediaNodeServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/media/abl/ABLMediaNodeServerService.java @@ -232,9 +232,6 @@ public class ABLMediaNodeServerService implements IMediaNodeServerService { }else { streamInfoResult.setWsFlv(addr, mediaServer.getWsFlvPort(),null, flvFile); } - - streamInfoResult.setWsFlv(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), flvFile); - String mp4File = String.format("%s/%s.mp4%s", app, stream, callIdParam); if ((mediaServer.getMp4Port() & 1) == 1) { // 奇数端口 默认ssl端口 @@ -243,7 +240,6 @@ public class ABLMediaNodeServerService implements IMediaNodeServerService { streamInfoResult.setFmp4(addr, mediaServer.getMp4Port(), null, mp4File); } - streamInfoResult.setHls(addr, mediaServer.getHttpPort(), mediaServer.getHttpSSlPort(), app, stream, callIdParam); streamInfoResult.setTs(addr, mediaServer.getHttpPort(), mediaServer.getHttpSSlPort(), app, stream, callIdParam); streamInfoResult.setRtc(addr, mediaServer.getHttpPort(), mediaServer.getHttpSSlPort(), app, stream, callIdParam, isPlay); @@ -463,7 +459,14 @@ public class ABLMediaNodeServerService implements IMediaNodeServerService { } @Override - public void loadMP4File(MediaServer mediaServer, String app, String stream, String date, String dateDir, ErrorCallback callback) { + public void loadMP4File(MediaServer mediaServer, String app, String stream, String filePath, String fileName, ErrorCallback callback) { + String buildStream = String.format("%s__ReplayFMP4RecordFile__%s", stream, fileName); + StreamInfo streamInfo = getStreamInfoByAppAndStream(mediaServer, app, buildStream, null, null, null, true); + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); + } + + @Override + public void loadMP4FileForDate(MediaServer mediaServer, String app, String stream, String date, String dateDir, ErrorCallback callback) { // 解析为 LocalDate LocalDate localDate = LocalDate.parse(date, DateUtil.DateFormatter); LocalDateTime startOfDay = localDate.atStartOfDay(); @@ -478,7 +481,6 @@ public class ABLMediaNodeServerService implements IMediaNodeServerService { String resultApp = ablResult.getApp(); String resultStream = ablResult.getStream(); StreamInfo streamInfo = getStreamInfoByAppAndStream(mediaServer, resultApp, resultStream, null, null,null, true); - System.out.println(streamInfo.getRtsp()); streamInfo.setKey(ablResult.getKey()); if (callback != null) { callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); @@ -487,7 +489,7 @@ public class ABLMediaNodeServerService implements IMediaNodeServerService { @Override public void seekRecordStamp(MediaServer mediaServer, String app, String stream, String key, Double stamp, String schema) { - ABLResult ablResult = ablresTfulUtils.controlRecordPlay(mediaServer, key, "seek", ((int)(stamp/1000)) + ""); + ABLResult ablResult = ablresTfulUtils.controlRecordPlay(mediaServer, key, "seek", "120"); if (ablResult.getCode() != 0) { log.warn("[abl-seek] 失败:{}", ablResult.getMemo()); } diff --git a/src/main/java/com/genersoft/iot/vmp/media/abl/ABLRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/abl/ABLRESTfulUtils.java index 5a95c9f84..d71b88cab 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/abl/ABLRESTfulUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/media/abl/ABLRESTfulUtils.java @@ -526,7 +526,7 @@ public class ABLRESTfulUtils { Map param = new HashMap<>(); param.put("key", key); param.put("command", command); - param.put("value", value); + param.put("value", Long.valueOf(value)); String response = sendGet(mediaServer, "controlRecordPlay", param); ABLResult ablResult = JSON.parseObject(response, ABLResult.class); if (ablResult == null) { diff --git a/src/main/java/com/genersoft/iot/vmp/media/abl/bean/ABLResult.java b/src/main/java/com/genersoft/iot/vmp/media/abl/bean/ABLResult.java index d10965550..523e8b16e 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/abl/bean/ABLResult.java +++ b/src/main/java/com/genersoft/iot/vmp/media/abl/bean/ABLResult.java @@ -20,6 +20,7 @@ public class ABLResult { private String stream; private String starttime; private String endtime; + private Long duration; private ABLUrls url; private List recordFileList; diff --git a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java index f1e38a29c..4e77b51bd 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java @@ -77,7 +77,7 @@ public interface IMediaNodeServerService { List listRtpServer(MediaServer mediaServer); - void loadMP4File(MediaServer mediaServer, String app, String stream, String datePath, String dateDir, ErrorCallback callback); + void loadMP4FileForDate(MediaServer mediaServer, String app, String stream, String datePath, String dateDir, ErrorCallback callback); void seekRecordStamp(MediaServer mediaServer, String app, String stream, String key, Double stamp, String schema); @@ -86,4 +86,6 @@ public interface IMediaNodeServerService { DownloadFileInfo getDownloadFilePath(MediaServer mediaServer, RecordInfo recordInfo); StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServer, String app, String stream, MediaInfo mediaInfo, String addr, String callId, boolean isPlay); + + void loadMP4File(MediaServer mediaServer, String app, String stream, String filePath, String fileName, ErrorCallback callback); } diff --git a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java index 1f2e1b204..2e447c077 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java @@ -164,11 +164,13 @@ public interface IMediaServerService { List listRtpServer(MediaServer mediaServer); - void loadMP4File(MediaServer mediaServer, String app, String stream, String datePath, String dateDir, ErrorCallback callback); + void loadMP4FileForDate(MediaServer mediaServer, String app, String stream, String datePath, String dateDir, ErrorCallback callback); void seekRecordStamp(MediaServer mediaServer, String app, String stream, String key, Double stamp, String schema); void setRecordSpeed(MediaServer mediaServer, String app, String stream, String key, Integer speed, String schema); DownloadFileInfo getDownloadFilePath(MediaServer mediaServer, RecordInfo recordInfo); + + void loadMP4File(MediaServer mediaServer, String app, String stream, String filePath, String fileName, ErrorCallback callback); } diff --git a/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java index c60b3ce1c..17b8d522a 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java @@ -952,14 +952,24 @@ public class MediaServerServiceImpl implements IMediaServerService { } @Override - public void loadMP4File(MediaServer mediaServer, String app, String stream, String date, String dateDir, ErrorCallback callback) { + public void loadMP4FileForDate(MediaServer mediaServer, String app, String stream, String date, String dateDir, ErrorCallback callback) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + log.info("[loadMP4FileForDate] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类"); + } + mediaNodeServerService.loadMP4FileForDate(mediaServer, app, stream, date, dateDir, callback); + + } + + @Override + public void loadMP4File(MediaServer mediaServer, String app, String stream, String filePath, String fileName, ErrorCallback callback) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { log.info("[loadMP4File] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类"); } - mediaNodeServerService.loadMP4File(mediaServer, app, stream, date, dateDir, callback); - + mediaNodeServerService.loadMP4File(mediaServer, app, stream, filePath, fileName, callback); } @Override diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java index 572258655..ee7a2ade4 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java @@ -22,14 +22,12 @@ import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.RandomStringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; @Slf4j @Service("zlm") @@ -515,7 +513,30 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { } @Override - public void loadMP4File(MediaServer mediaServer, String app, String stream, String date, String dateDir, ErrorCallback callback) { + public void loadMP4File(MediaServer mediaServer, String app, String stream, String filePath, String fileName, ErrorCallback callback) { + String buildApp = "mp4_record"; + String buildStream = app + "_" + stream + "_" + fileName + "_" + RandomStringUtils.randomAlphabetic(6).toLowerCase(); + + Hook hook = Hook.getInstance(HookType.on_media_arrival, buildApp, buildStream, mediaServer.getServerId()); + subscribe.addSubscribe(hook, (hookData) -> { + StreamInfo streamInfo = getStreamInfoByAppAndStream(mediaServer, buildApp, buildStream, hookData.getMediaInfo(), null, null, true); + if (callback != null) { + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); + } + }); + + ZLMResult zlmResult = zlmresTfulUtils.loadMP4File(mediaServer, buildApp, buildStream, filePath); + + if (zlmResult == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "请求失败"); + } + if (zlmResult.getCode() != 0) { + throw new ControllerException(zlmResult.getCode(), zlmResult.getMsg()); + } + } + + @Override + public void loadMP4FileForDate(MediaServer mediaServer, String app, String stream, String date, String dateDir, ErrorCallback callback) { String buildApp = "mp4_record"; String buildStream = app + "_" + stream + "_" + date; MediaInfo mediaInfo = getMediaInfo(mediaServer, buildApp, buildStream); diff --git a/src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java b/src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java index 5b9031e66..6d33274e9 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java @@ -59,11 +59,13 @@ public interface ICloudRecordService { /** * 加载录像文件,形成录像流 */ - void loadRecord(String app, String stream, String date, ErrorCallback callback); + void loadMP4FileForDate(String app, String stream, String date, ErrorCallback callback); void seekRecord(String mediaServerId,String app, String stream, String key, Double seek, String schema); void setRecordSpeed(String mediaServerId, String app, String stream, String key, Integer speed, String schema); void deleteFileByIds(Set ids); + + void loadMP4File(String app, String stream, int cloudRecordId, ErrorCallback callback); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java index 50c30e1f7..2482c1db9 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java @@ -281,7 +281,36 @@ public class CloudRecordServiceImpl implements ICloudRecordService { } @Override - public void loadRecord(String app, String stream, String date, ErrorCallback callback) { + public void loadMP4File(String app, String stream, int cloudRecordId, ErrorCallback callback) { + + CloudRecordItem recordItem = cloudRecordServiceMapper.queryOne(cloudRecordId); + if (recordItem == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "无录像"); + } + String mediaServerId = recordItem.getMediaServerId(); + MediaServer mediaServer = mediaServerService.getOne(mediaServerId); + if (mediaServer == null) { + log.warn("[云端录像] 播放 未找到录制的流媒体,将自动选择低负载流媒体使用"); + mediaServer = mediaServerService.getMediaServerForMinimumLoad(null); + } + if (mediaServer == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "无可用流媒体"); + } + String fileName = recordItem.getFileName().substring(0 , recordItem.getFileName().indexOf(".")); + String filePath = recordItem.getFilePath(); +// if (filePath != null) { +// fileName = filePath.substring(0, filePath.lastIndexOf("/")); +// } + mediaServerService.loadMP4File(mediaServer, app, stream, filePath, fileName, ((code, msg, streamInfo) -> { + if (code == ErrorCode.SUCCESS.getCode()) { + streamInfo.setDuration(recordItem.getTimeLen()); + } + callback.run(code, msg, streamInfo); + })); + } + + @Override + public void loadMP4FileForDate(String app, String stream, String date, ErrorCallback callback) { long startTimestamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(date + " 00:00:00"); long endTimestamp = startTimestamp + 24 * 60 * 60 * 1000; @@ -299,7 +328,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService { if (filePath != null) { dateDir = filePath.substring(0, filePath.lastIndexOf("/")); } - mediaServerService.loadMP4File(mediaServer, app, stream, date, dateDir, callback); + mediaServerService.loadMP4FileForDate(mediaServer, app, stream, date, dateDir, callback); } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java index 36a8837ed..ff7df7552 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java @@ -97,6 +97,9 @@ public class StreamContent { @Schema(description = "结束时间") private String endTime; + @Schema(description = "时长(回放时使用)") + private Double duration; + @Schema(description = "文件下载地址(录像下载使用)") private DownloadFileInfo downLoadFilePath; @@ -185,6 +188,7 @@ public class StreamContent { this.startTime = streamInfo.getStartTime(); this.endTime = streamInfo.getEndTime(); this.progress = streamInfo.getProgress(); + this.duration = streamInfo.getDuration(); this.key = streamInfo.getKey(); if (streamInfo.getDownLoadFilePath() != null) { diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java index 0e4ebdd58..267690c32 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java @@ -253,17 +253,17 @@ public class CloudRecordController { @Operation(summary = "加载录像文件形成播放地址") @Parameter(name = "app", description = "应用名", required = true) @Parameter(name = "stream", description = "流ID", required = true) - @Parameter(name = "date", description = "日期, 例如 2025-04-10", required = true) + @Parameter(name = "cloudRecordId", description = "云端录像ID", required = true) public DeferredResult> loadRecord( HttpServletRequest request, @RequestParam(required = true) String app, @RequestParam(required = true) String stream, - @RequestParam(required = true) String date + @RequestParam(required = true) int cloudRecordId ) { DeferredResult> result = new DeferredResult<>(); result.onTimeout(()->{ - log.info("[加载录像文件超时] app={}, stream={}, date={}", app, stream, date); + log.info("[加载录像文件超时] app={}, stream={}, cloudRecordId={}", app, stream, cloudRecordId); WVPResult wvpResult = new WVPResult<>(); wvpResult.setCode(ErrorCode.ERROR100.getCode()); wvpResult.setMsg("加载录像文件超时"); @@ -304,7 +304,7 @@ public class CloudRecordController { result.setResult(wvpResult); }; - cloudRecordService.loadRecord(app, stream, date, callback); + cloudRecordService.loadMP4File(app, stream, cloudRecordId, callback); return result; } diff --git a/web/src/api/cloudRecord.js b/web/src/api/cloudRecord.js index 2a52b8f20..7bc871e47 100644 --- a/web/src/api/cloudRecord.js +++ b/web/src/api/cloudRecord.js @@ -28,14 +28,14 @@ export function queryListByData(params) { } export function loadRecord(params) { - const { app, stream, date } = params + const { app, stream, cloudRecordId } = params return request({ method: 'get', url: `/api/cloud/record/loadRecord`, params: { app: app, stream: stream, - date: date + cloudRecordId: cloudRecordId } }) } diff --git a/web/src/views/cloudRecord/detail.vue b/web/src/views/cloudRecord/detail.vue index 36adfa70c..0f1324553 100755 --- a/web/src/views/cloudRecord/detail.vue +++ b/web/src/views/cloudRecord/detail.vue @@ -46,7 +46,7 @@
-
+
正在加载
@@ -54,16 +54,18 @@
- -
{{ showTimeValue }}
+
+ {{showPlayTimeValue}} +
+
+
+
+
+
+
+
+ {{showPlayTimeTotal}} +
@@ -111,14 +113,13 @@