修复云端录像seek问题
This commit is contained in:
@@ -178,7 +178,7 @@ public class UserSetting {
|
||||
/**
|
||||
* 登录超时时间(分钟),
|
||||
*/
|
||||
private long loginTimeout = 30;
|
||||
private long loginTimeout = 60;
|
||||
|
||||
/**
|
||||
* jwk文件路径,若不指定则使用resources目录下的jwk.json
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.genersoft.iot.vmp.media.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RecordInfo {
|
||||
private String fileName;
|
||||
private String filePath;
|
||||
@@ -24,70 +26,6 @@ public class RecordInfo {
|
||||
return recordInfo;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public long getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public void setFileSize(long fileSize) {
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
|
||||
public String getFolder() {
|
||||
return folder;
|
||||
}
|
||||
|
||||
public void setFolder(String folder) {
|
||||
this.folder = folder;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(long startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public double getTimeLen() {
|
||||
return timeLen;
|
||||
}
|
||||
|
||||
public void setTimeLen(double timeLen) {
|
||||
this.timeLen = timeLen;
|
||||
}
|
||||
|
||||
public String getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public void setParams(String params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RecordInfo{" +
|
||||
|
||||
@@ -72,7 +72,7 @@ public interface IMediaNodeServerService {
|
||||
|
||||
void loadMP4File(MediaServer mediaServer, String app, String stream, String datePath);
|
||||
|
||||
void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp);
|
||||
void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema);
|
||||
|
||||
void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed);
|
||||
void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed, String schema);
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ public interface IMediaServerService {
|
||||
|
||||
StreamInfo loadMP4File(MediaServer mediaServer, String app, String stream, String datePath);
|
||||
|
||||
void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp);
|
||||
void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema);
|
||||
|
||||
void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed);
|
||||
void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed, String schema);
|
||||
}
|
||||
|
||||
@@ -983,22 +983,22 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp) {
|
||||
public void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema) {
|
||||
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
|
||||
if (mediaNodeServerService == null) {
|
||||
log.info("[seekRecordStamp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
|
||||
}
|
||||
mediaNodeServerService.seekRecordStamp(mediaServer, app, stream, stamp);
|
||||
mediaNodeServerService.seekRecordStamp(mediaServer, app, stream, stamp, schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed) {
|
||||
public void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed, String schema) {
|
||||
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
|
||||
if (mediaNodeServerService == null) {
|
||||
log.info("[setRecordSpeed] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
|
||||
}
|
||||
mediaNodeServerService.setRecordSpeed(mediaServer, app, stream, speed);
|
||||
mediaNodeServerService.setRecordSpeed(mediaServer, app, stream, speed, schema);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,6 @@ public class ZLMHttpHookListener {
|
||||
@ResponseBody
|
||||
@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
|
||||
public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) {
|
||||
|
||||
MediaServer mediaServer = mediaServerService.getOne(param.getMediaServerId());
|
||||
if (mediaServer == null) {
|
||||
return HookResult.SUCCESS();
|
||||
|
||||
@@ -562,8 +562,8 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp) {
|
||||
JSONObject jsonObject = zlmresTfulUtils.seekRecordStamp(mediaServer, app, stream, stamp, "ts");
|
||||
public void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema) {
|
||||
JSONObject jsonObject = zlmresTfulUtils.seekRecordStamp(mediaServer, app, stream, stamp, schema);
|
||||
if (jsonObject == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "请求失败");
|
||||
}
|
||||
@@ -573,8 +573,8 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed) {
|
||||
JSONObject jsonObject = zlmresTfulUtils.setRecordSpeed(mediaServer, app, stream, speed, "ts");
|
||||
public void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed, String schema) {
|
||||
JSONObject jsonObject = zlmresTfulUtils.setRecordSpeed(mediaServer, app, stream, speed, schema);
|
||||
if (jsonObject == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "请求失败");
|
||||
}
|
||||
|
||||
@@ -424,7 +424,6 @@ public class ZLMRESTfulUtils {
|
||||
param.put("stream", stream);
|
||||
param.put("file_path", datePath);
|
||||
param.put("file_repeat", "0");
|
||||
param.put("auto_close", "1");
|
||||
return sendPost(mediaServer, "loadMP4File",param, null);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,9 +61,9 @@ public interface ICloudRecordService {
|
||||
*/
|
||||
void loadRecord(String app, String stream, String date, ErrorCallback<StreamInfo> callback);
|
||||
|
||||
void seekRecord(String mediaServerId,String app, String stream, Double seek);
|
||||
void seekRecord(String mediaServerId,String app, String stream, Double seek, String schema);
|
||||
|
||||
void setRecordSpeed(String mediaServerId, String app, String stream, Integer speed);
|
||||
void setRecordSpeed(String mediaServerId, String app, String stream, Integer speed, String schema);
|
||||
|
||||
void deleteFileByIds(Set<Integer> ids);
|
||||
}
|
||||
|
||||
@@ -297,43 +297,44 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体节点不存在: " + mediaServerId);
|
||||
}
|
||||
String buildStream = stream + "_" + date;
|
||||
MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServer, app, buildStream);
|
||||
String buildApp = "mp4_record";
|
||||
String buildStream = app + "_" + stream + "_" + date;
|
||||
MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServer, buildApp, buildStream);
|
||||
if (mediaInfo != null) {
|
||||
if (callback != null) {
|
||||
StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(mediaServer, app, buildStream, mediaInfo, null);
|
||||
StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(mediaServer, buildApp, buildStream, mediaInfo, null);
|
||||
callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Hook hook = Hook.getInstance(HookType.on_media_arrival, app, buildStream, mediaServerId);
|
||||
Hook hook = Hook.getInstance(HookType.on_media_arrival, buildApp, buildStream, mediaServerId);
|
||||
subscribe.addSubscribe(hook, (hookData) -> {
|
||||
StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(mediaServer, app, buildStream, hookData.getMediaInfo(), null);
|
||||
StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(mediaServer, buildApp, buildStream, hookData.getMediaInfo(), null);
|
||||
if (callback != null) {
|
||||
callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
|
||||
}
|
||||
});
|
||||
String dateDir = recordItemList.get(0).getFilePath().substring(0, recordItemList.get(0).getFilePath().lastIndexOf("/"));
|
||||
mediaServerService.loadMP4File(mediaServer, app, buildStream, dateDir);
|
||||
mediaServerService.loadMP4File(mediaServer, buildApp, buildStream, dateDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekRecord(String mediaServerId,String app, String stream, Double seek) {
|
||||
public void seekRecord(String mediaServerId,String app, String stream, Double seek, String schema) {
|
||||
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体节点不存在: " + mediaServerId);
|
||||
}
|
||||
mediaServerService.seekRecordStamp(mediaServer, app, stream, seek);
|
||||
mediaServerService.seekRecordStamp(mediaServer, app, stream, seek, schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordSpeed(String mediaServerId, String app, String stream, Integer speed) {
|
||||
public void setRecordSpeed(String mediaServerId, String app, String stream, Integer speed, String schema) {
|
||||
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体节点不存在: " + mediaServerId);
|
||||
}
|
||||
mediaServerService.setRecordSpeed(mediaServer, app, stream, speed);
|
||||
mediaServerService.setRecordSpeed(mediaServer, app, stream, speed, schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -352,7 +353,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
boolean deleteResult = mediaServerService.deleteRecordDirectory(mediaServer, cloudRecordItem.getApp(),
|
||||
cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
|
||||
if (deleteResult) {
|
||||
log.warn("[录像文件定时清理] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
|
||||
log.warn("[录像文件] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
|
||||
cloudRecordItemIdListForDelete.add(cloudRecordItem);
|
||||
}
|
||||
}catch (ControllerException e) {
|
||||
|
||||
@@ -318,10 +318,13 @@ public class CloudRecordController {
|
||||
@RequestParam(required = true) String mediaServerId,
|
||||
@RequestParam(required = true) String app,
|
||||
@RequestParam(required = true) String stream,
|
||||
@RequestParam(required = true) Double seek
|
||||
@RequestParam(required = true) Double seek,
|
||||
@RequestParam(required = false) String schema
|
||||
) {
|
||||
|
||||
cloudRecordService.seekRecord(mediaServerId, app, stream, seek);
|
||||
if (schema == null) {
|
||||
schema = "ts";
|
||||
}
|
||||
cloudRecordService.seekRecord(mediaServerId, app, stream, seek, schema);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@@ -334,10 +337,14 @@ public class CloudRecordController {
|
||||
@RequestParam(required = true) String mediaServerId,
|
||||
@RequestParam(required = true) String app,
|
||||
@RequestParam(required = true) String stream,
|
||||
@RequestParam(required = true) Integer speed
|
||||
) {
|
||||
@RequestParam(required = true) Integer speed,
|
||||
@RequestParam(required = false) String schema
|
||||
) {
|
||||
if (schema == null) {
|
||||
schema = "ts";
|
||||
}
|
||||
|
||||
cloudRecordService.setRecordSpeed(mediaServerId, app, stream, speed);
|
||||
cloudRecordService.setRecordSpeed(mediaServerId, app, stream, speed, schema);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@@ -410,7 +417,7 @@ public class CloudRecordController {
|
||||
try {
|
||||
ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
zos.putNextEntry(new ZipEntry(DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss(cloudRecordItem.getStartTime()) + ".mp4"));
|
||||
zos.putNextEntry(new ZipEntry(DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss((long)cloudRecordItem.getStartTime()) + ".mp4"));
|
||||
File file = new File(cloudRecordItem.getFilePath());
|
||||
if (!file.exists() || file.isDirectory()) {
|
||||
continue;
|
||||
@@ -519,7 +526,7 @@ public class CloudRecordController {
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
CloudRecordUrl cloudRecordUrl = new CloudRecordUrl();
|
||||
cloudRecordUrl.setId(cloudRecordItem.getId());
|
||||
cloudRecordUrl.setDownloadUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath() + "&save_name=" + cloudRecordItem.getStream() + "_" + cloudRecordItem.getCallId() + "_" + DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss(cloudRecordItem.getStartTime()));
|
||||
cloudRecordUrl.setDownloadUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath() + "&save_name=" + cloudRecordItem.getStream() + "_" + cloudRecordItem.getCallId() + "_" + DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss((long)cloudRecordItem.getStartTime()));
|
||||
cloudRecordUrl.setPlayUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath());
|
||||
cloudRecordUrlList.add(cloudRecordUrl);
|
||||
}
|
||||
|
||||
@@ -63,9 +63,9 @@ public class UserApiKeyController {
|
||||
|
||||
Long expirationTime = null;
|
||||
if (expiresAt != null) {
|
||||
long timestamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(expiresAt);
|
||||
expirationTime = (timestamp - System.currentTimeMillis()) / (60 * 1000);
|
||||
if (expirationTime < 0) {
|
||||
expirationTime = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(expiresAt);
|
||||
long difference = (expirationTime - System.currentTimeMillis()) / (60 * 1000);
|
||||
if (difference < 0) {
|
||||
throw new ControllerException(ErrorCode.ERROR400.getCode(), "过期时间不能早于当前时间");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
|
||||
spring:
|
||||
cache:
|
||||
type: redis
|
||||
thymeleaf:
|
||||
cache: false
|
||||
# 设置接口超时时间
|
||||
mvc:
|
||||
async:
|
||||
|
||||
16
zlm.md
Normal file
16
zlm.md
Normal file
@@ -0,0 +1,16 @@
|
||||
1. 增加接口:用来返回hook调用耗时。
|
||||
参数: sn,
|
||||
返回值: hook耗时
|
||||
功能: zlm收到调用后立即调用心跳hook,hook中携带sn信息, zlm收到hook返回后计算此次hook的耗时,然后返回耗时
|
||||
用途: 1. 可以用来检测hook配置是否正常
|
||||
2. 检测wvp与zlm连接的健康程度
|
||||
2. 增加接口:查询loadMP4File的录像播放位置
|
||||
参数: app, steam,
|
||||
返回: 当前播放的hook位置
|
||||
功能: zlm收到请求后查询流的seek位置并返回
|
||||
用途: 1. 获取当前播放位置,可以在页面同步显示时间
|
||||
2. 用来时快进五秒 快退五秒类似的操作
|
||||
3. loadMP4File扩展, 增加默认seek值和默认倍速,开始时直接从这个位置开始,并使用指定的倍速
|
||||
4. 支持下载指定取件的mp4文件,这个区间可能包括多个文件和文件的一部分。
|
||||
5. 希望seek接口和倍速接口可以去除schema的必选,作为可选项,不传则默认全部
|
||||
6. 可选: seek值如果可以换算为日期,会有更好的体验, 比如2025-4-15 12:32:11
|
||||
Reference in New Issue
Block a user