云端录像回放使用单文件播放
This commit is contained in:
@@ -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 = "文件下载地址(录像下载使用)")
|
||||
|
||||
@@ -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<StreamInfo> callback) {
|
||||
public void loadMP4File(MediaServer mediaServer, String app, String stream, String filePath, String fileName, ErrorCallback<StreamInfo> 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<StreamInfo> 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());
|
||||
}
|
||||
|
||||
@@ -526,7 +526,7 @@ public class ABLRESTfulUtils {
|
||||
Map<String, Object> 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) {
|
||||
|
||||
@@ -20,6 +20,7 @@ public class ABLResult {
|
||||
private String stream;
|
||||
private String starttime;
|
||||
private String endtime;
|
||||
private Long duration;
|
||||
private ABLUrls url;
|
||||
private List<ABLRecordFile> recordFileList;
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ public interface IMediaNodeServerService {
|
||||
|
||||
List<String> listRtpServer(MediaServer mediaServer);
|
||||
|
||||
void loadMP4File(MediaServer mediaServer, String app, String stream, String datePath, String dateDir, ErrorCallback<StreamInfo> callback);
|
||||
void loadMP4FileForDate(MediaServer mediaServer, String app, String stream, String datePath, String dateDir, ErrorCallback<StreamInfo> 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<StreamInfo> callback);
|
||||
}
|
||||
|
||||
@@ -164,11 +164,13 @@ public interface IMediaServerService {
|
||||
|
||||
List<String> listRtpServer(MediaServer mediaServer);
|
||||
|
||||
void loadMP4File(MediaServer mediaServer, String app, String stream, String datePath, String dateDir, ErrorCallback<StreamInfo> callback);
|
||||
void loadMP4FileForDate(MediaServer mediaServer, String app, String stream, String datePath, String dateDir, ErrorCallback<StreamInfo> 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<StreamInfo> callback);
|
||||
}
|
||||
|
||||
@@ -952,14 +952,24 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMP4File(MediaServer mediaServer, String app, String stream, String date, String dateDir, ErrorCallback<StreamInfo> callback) {
|
||||
public void loadMP4FileForDate(MediaServer mediaServer, String app, String stream, String date, String dateDir, ErrorCallback<StreamInfo> 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<StreamInfo> 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
|
||||
|
||||
@@ -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<StreamInfo> callback) {
|
||||
public void loadMP4File(MediaServer mediaServer, String app, String stream, String filePath, String fileName, ErrorCallback<StreamInfo> 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<StreamInfo> callback) {
|
||||
String buildApp = "mp4_record";
|
||||
String buildStream = app + "_" + stream + "_" + date;
|
||||
MediaInfo mediaInfo = getMediaInfo(mediaServer, buildApp, buildStream);
|
||||
|
||||
@@ -59,11 +59,13 @@ public interface ICloudRecordService {
|
||||
/**
|
||||
* 加载录像文件,形成录像流
|
||||
*/
|
||||
void loadRecord(String app, String stream, String date, ErrorCallback<StreamInfo> callback);
|
||||
void loadMP4FileForDate(String app, String stream, String date, ErrorCallback<StreamInfo> 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<Integer> ids);
|
||||
|
||||
void loadMP4File(String app, String stream, int cloudRecordId, ErrorCallback<StreamInfo> callback);
|
||||
}
|
||||
|
||||
@@ -281,7 +281,36 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadRecord(String app, String stream, String date, ErrorCallback<StreamInfo> callback) {
|
||||
public void loadMP4File(String app, String stream, int cloudRecordId, ErrorCallback<StreamInfo> 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<StreamInfo> 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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<WVPResult<StreamContent>> loadRecord(
|
||||
HttpServletRequest request,
|
||||
@RequestParam(required = true) String app,
|
||||
@RequestParam(required = true) String stream,
|
||||
@RequestParam(required = true) String date
|
||||
@RequestParam(required = true) int cloudRecordId
|
||||
) {
|
||||
DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>();
|
||||
|
||||
result.onTimeout(()->{
|
||||
log.info("[加载录像文件超时] app={}, stream={}, date={}", app, stream, date);
|
||||
log.info("[加载录像文件超时] app={}, stream={}, cloudRecordId={}", app, stream, cloudRecordId);
|
||||
WVPResult<StreamContent> 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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user