Merge branch 'master' into dev/1078-newUI

# Conflicts:
#	src/main/resources/application.yml
This commit is contained in:
648540858
2025-05-10 08:59:45 +08:00
378 changed files with 48292 additions and 475 deletions

View File

@@ -0,0 +1,69 @@
package com.genersoft.iot.vmp.service;
import com.alibaba.fastjson2.JSONArray;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.github.pagehelper.PageInfo;
import java.util.List;
import java.util.Set;
/**
* 云端录像管理
* @author lin
*/
public interface ICloudRecordService {
/**
* 分页回去云端录像列表
*/
PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems, String callId, Boolean ascOrder);
/**
* 获取所有的日期
*/
List<String> getDateList(String app, String stream, int year, int month, List<MediaServer> mediaServerItems);
/**
* 添加合并任务
*/
String addTask(String app, String stream, MediaServer mediaServerItem, String startTime,
String endTime, String callId, String remoteHost, boolean filterMediaServer);
/**
* 查询合并任务列表
*/
JSONArray queryTask(String app, String stream, String callId, String taskId, String mediaServerId, Boolean isEnd, String scheme);
/**
* 收藏视频,收藏的视频过期不会删除
*/
int changeCollect(boolean result, String app, String stream, String mediaServerId, String startTime, String endTime, String callId);
/**
* 添加指定录像收藏
*/
int changeCollectById(Integer recordId, boolean result);
/**
* 获取播放地址
*/
DownloadFileInfo getPlayUrlPath(Integer recordId);
List<CloudRecordItem> getAllList(String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems, String callId, List<Integer> ids);
/**
* 加载录像文件,形成录像流
*/
void loadRecord(String app, String stream, String date, ErrorCallback<StreamInfo> callback);
void seekRecord(String mediaServerId,String app, String stream, Double seek, String schema);
void setRecordSpeed(String mediaServerId, String app, String stream, Integer speed, String schema);
void deleteFileByIds(Set<Integer> ids);
}

View File

@@ -15,52 +15,52 @@ public class CloudRecordItem {
* 主键
*/
private int id;
/**
* 应用名
*/
private String app;
/**
* 流
*/
private String stream;
/**
* 健全ID
*/
private String callId;
/**
* 开始时间
*/
private long startTime;
/**
* 结束时间
*/
private long endTime;
/**
* ZLM Id
*/
private String mediaServerId;
/**
* 文件名称
*/
private String fileName;
/**
* 文件路径
*/
private String filePath;
/**
* 文件夹
*/
private String folder;
/**
* 收藏,收藏的文件不移除
*/
@@ -70,16 +70,16 @@ public class CloudRecordItem {
* 保留,收藏的文件不移除
*/
private Boolean reserve;
/**
* 文件大小
*/
private long fileSize;
/**
* 文件时长
*/
private long timeLen;
private double timeLen;
/**
* 所属服务ID
@@ -96,7 +96,7 @@ public class CloudRecordItem {
cloudRecordItem.setFileSize(param.getRecordInfo().getFileSize());
cloudRecordItem.setFilePath(param.getRecordInfo().getFilePath());
cloudRecordItem.setMediaServerId(param.getMediaServer().getId());
cloudRecordItem.setTimeLen((long) param.getRecordInfo().getTimeLen() * 1000);
cloudRecordItem.setTimeLen(param.getRecordInfo().getTimeLen() * 1000);
cloudRecordItem.setEndTime((param.getRecordInfo().getStartTime() + (long)param.getRecordInfo().getTimeLen()) * 1000);
Map<String, String> paramsMap = MediaServerUtils.urlParamToMap(param.getRecordInfo().getParams());
if (paramsMap.get("callId") != null) {

View File

@@ -2,16 +2,22 @@ package com.genersoft.iot.vmp.service.impl;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.event.hook.Hook;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import com.genersoft.iot.vmp.media.event.hook.HookType;
import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
import com.genersoft.iot.vmp.service.ICloudRecordService;
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcPlayService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
@@ -28,12 +34,10 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.io.File;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
@Slf4j
@Service
@@ -57,9 +61,12 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
@Autowired
private IRedisRpcPlayService redisRpcPlayService;
@Autowired
private HookSubscribe subscribe;
@Override
public PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime,
String endTime, List<MediaServer> mediaServerItems, String callId) {
String endTime, List<MediaServer> mediaServerItems, String callId, Boolean ascOrder) {
// 开始时间和结束时间在数据库中都是以秒为单位的
Long startTimeStamp = null;
Long endTimeStamp = null;
@@ -84,7 +91,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
.replaceAll("_", "/_");
}
List<CloudRecordItem> all = cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
callId, mediaServerItems, null);
callId, mediaServerItems, null, ascOrder);
return new PageInfo<>(all);
}
@@ -100,7 +107,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
long startTimeStamp = startDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
long endTimeStamp = endDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp,
endTimeStamp, null, mediaServerItems, null);
endTimeStamp, null, mediaServerItems, null, null);
if (cloudRecordItemList.isEmpty()) {
return new ArrayList<>();
}
@@ -213,7 +220,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
}
List<CloudRecordItem> all = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp, endTimeStamp,
callId, mediaServerItems, null);
callId, mediaServerItems, null, null);
if (all.isEmpty()) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到待收藏的视频");
}
@@ -273,6 +280,96 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
}
return cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
callId, mediaServerItems, ids);
callId, mediaServerItems, ids, null);
}
@Override
public void loadRecord(String app, String stream, String date, ErrorCallback<StreamInfo> callback) {
long startTimestamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(date + " 00:00:00");
long endTimestamp = startTimestamp + 24 * 60 * 60 * 1000;
List<CloudRecordItem> recordItemList = cloudRecordServiceMapper.getList(null, app, stream, startTimestamp, endTimestamp, null, null, null, false);
if (recordItemList.isEmpty()) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "此时间无录像");
}
String mediaServerId = recordItemList.get(0).getMediaServerId();
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
if (mediaServer == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体节点不存在: " + mediaServerId);
}
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, buildApp, buildStream, mediaInfo, null);
callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
}
return;
}
Hook hook = Hook.getInstance(HookType.on_media_arrival, buildApp, buildStream, mediaServerId);
subscribe.addSubscribe(hook, (hookData) -> {
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, buildApp, buildStream, dateDir);
}
@Override
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, schema);
}
@Override
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, schema);
}
@Override
public void deleteFileByIds(Set<Integer> ids) {
log.info("[删除录像文件] ids: {}", ids.toArray());
List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.queryRecordByIds(ids);
if (cloudRecordItemList.isEmpty()) {
return;
}
List<CloudRecordItem> cloudRecordItemIdListForDelete = new ArrayList<>();
StringBuilder stringBuilder = new StringBuilder();
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
String date = new File(cloudRecordItem.getFilePath()).getParentFile().getName();
MediaServer mediaServer = mediaServerService.getOne(cloudRecordItem.getMediaServerId());
try {
boolean deleteResult = mediaServerService.deleteRecordDirectory(mediaServer, cloudRecordItem.getApp(),
cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
if (deleteResult) {
log.warn("[录像文件] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
cloudRecordItemIdListForDelete.add(cloudRecordItem);
}
}catch (ControllerException e) {
if (stringBuilder.length() > 0) {
stringBuilder.append(", ");
}
stringBuilder.append(cloudRecordItem.getFileName());
}
}
if (!cloudRecordItemIdListForDelete.isEmpty()) {
cloudRecordServiceMapper.deleteList(cloudRecordItemIdListForDelete);
}
if (stringBuilder.length() > 0) {
stringBuilder.append(" 删除失败");
throw new ControllerException(ErrorCode.ERROR100.getCode(), stringBuilder.toString());
}
}
}

View File

@@ -79,9 +79,8 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
Map<Integer, StreamInfo> recordStreamMap = new HashMap<>();
@Scheduled(fixedRate = 10, timeUnit = TimeUnit.MINUTES)
@Scheduled(fixedRate = 1, timeUnit = TimeUnit.MINUTES)
public void execution() {
log.info("[录制计划] 执行");
// 查询现在需要录像的通道Id
List<Integer> startChannelIdList = queryCurrentChannelRecord();
@@ -133,7 +132,7 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
// 获取当前时间在一周内的序号, 数据库存储的从第几个30分钟开始, 0-47, 包括首尾
LocalDateTime now = LocalDateTime.now();
int week = now.getDayOfWeek().getValue();
int index = now.getHour() * 2 + (now.getMinute() > 30?1:0);
int index = now.getHour() * 60 + now.getMinute();
// 查询现在需要录像的通道Id
return recordPlanMapper.queryRecordIng(week, index);
@@ -221,7 +220,7 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
}
}
// TODO 更新录像队列
}
@Override

View File

@@ -6,7 +6,7 @@ import com.genersoft.iot.vmp.conf.redis.RedisRpcConfig;
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcMessage;
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest;
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse;
import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService;
import com.genersoft.iot.vmp.service.ICloudRecordService;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import com.genersoft.iot.vmp.service.redisMsg.dto.RedisRpcController;
import com.genersoft.iot.vmp.service.redisMsg.dto.RedisRpcMapping;