Merge branch 'master' into dev/1078-newUI
# Conflicts: # src/main/resources/application.yml
This commit is contained in:
69
src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java
Executable file
69
src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java
Executable 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);
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user