Merge branch 'wvp-28181-2.0' into main-dev

# Conflicts:
#	pom.xml
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java
#	src/main/resources/all-application.yml
#	src/main/resources/application-dev.yml
This commit is contained in:
648540858
2024-02-07 20:30:37 +08:00
144 changed files with 6070 additions and 28330 deletions

View File

@@ -2,40 +2,69 @@ package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import okhttp3.*;
import okhttp3.logging.HttpLoggingInterceptor;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import java.io.IOException;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Component
public class AssistRESTfulUtils {
private final static Logger logger = LoggerFactory.getLogger(AssistRESTfulUtils.class);
private OkHttpClient client;
public interface RequestCallback{
void run(JSONObject response);
}
private OkHttpClient getClient(){
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
if (logger.isDebugEnabled()) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
logger.debug("http请求参数" + message);
});
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
// OkHttp進行添加攔截器loggingInterceptor
httpClientBuilder.addInterceptor(logging);
return getClient(null);
}
private OkHttpClient getClient(Integer readTimeOut){
if (client == null) {
if (readTimeOut == null) {
readTimeOut = 10;
}
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
// 设置连接超时时间
httpClientBuilder.connectTimeout(8, TimeUnit.SECONDS);
// 设置读取超时时间
httpClientBuilder.readTimeout(readTimeOut,TimeUnit.SECONDS);
// 设置连接池
httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES));
if (logger.isDebugEnabled()) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
logger.debug("http请求参数" + message);
});
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
// OkHttp進行添加攔截器loggingInterceptor
httpClientBuilder.addInterceptor(logging);
}
client = httpClientBuilder.build();
}
return httpClientBuilder.build();
return client;
}
@@ -49,11 +78,11 @@ public class AssistRESTfulUtils {
logger.warn("未启用Assist服务");
return null;
}
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(String.format("http://%s:%s/%s", mediaServerItem.getIp(), mediaServerItem.getRecordAssistPort(), api));
StringBuilder stringBuffer = new StringBuilder();
stringBuffer.append(api);
JSONObject responseJSON = null;
if (param != null && param.keySet().size() > 0) {
if (param != null && !param.keySet().isEmpty()) {
stringBuffer.append("?");
int index = 1;
for (String key : param.keySet()){
@@ -68,6 +97,7 @@ public class AssistRESTfulUtils {
}
String url = stringBuffer.toString();
logger.info("[访问assist] {}", url);
Request request = new Request.Builder()
.get()
.url(url)
@@ -123,13 +153,93 @@ public class AssistRESTfulUtils {
return responseJSON;
}
public JSONObject sendPost(MediaServerItem mediaServerItem, String url,
JSONObject param, ZLMRESTfulUtils.RequestCallback callback,
Integer readTimeOut) {
OkHttpClient client = getClient(readTimeOut);
public JSONObject fileDuration(MediaServerItem mediaServerItem, String app, String stream, RequestCallback callback){
Map<String, Object> param = new HashMap<>();
param.put("app",app);
param.put("stream",stream);
param.put("recordIng",true);
return sendGet(mediaServerItem, "api/record/file/duration",param, callback);
if (mediaServerItem == null) {
return null;
}
logger.info("[访问assist] {}, 参数: {}", url, param);
JSONObject responseJSON = new JSONObject();
//-2自定义流媒体 调用错误码
responseJSON.put("code",-2);
responseJSON.put("msg","ASSIST调用失败");
RequestBody requestBodyJson = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), param.toString());
Request request = new Request.Builder()
.post(requestBodyJson)
.url(url)
.addHeader("Content-Type", "application/json")
.build();
if (callback == null) {
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
ResponseBody responseBody = response.body();
if (responseBody != null) {
String responseStr = responseBody.string();
responseJSON = JSON.parseObject(responseStr);
}
}else {
response.close();
Objects.requireNonNull(response.body()).close();
}
}catch (IOException e) {
logger.error(String.format("[ %s ]ASSIST请求失败: %s", url, e.getMessage()));
if(e instanceof SocketTimeoutException){
//读取超时超时异常
logger.error(String.format("读取ASSIST数据失败: %s, %s", url, e.getMessage()));
}
if(e instanceof ConnectException){
//判断连接异常我这里是报Failed to connect to 10.7.5.144
logger.error(String.format("连接ASSIST失败: %s, %s", url, e.getMessage()));
}
}catch (Exception e){
logger.error(String.format("访问ASSIST失败: %s, %s", url, e.getMessage()));
}
}else {
client.newCall(request).enqueue(new Callback(){
@Override
public void onResponse(@NotNull Call call, @NotNull Response response){
if (response.isSuccessful()) {
try {
String responseStr = Objects.requireNonNull(response.body()).string();
callback.run(JSON.parseObject(responseStr));
} catch (IOException e) {
logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage()));
}
}else {
response.close();
Objects.requireNonNull(response.body()).close();
}
}
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
if(e instanceof SocketTimeoutException){
//读取超时超时异常
logger.error(String.format("读取ZLM数据失败: %s, %s", call.request().toString(), e.getMessage()));
}
if(e instanceof ConnectException){
//判断连接异常我这里是报Failed to connect to 10.7.5.144
logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
}
}
});
}
return responseJSON;
}
public JSONObject getInfo(MediaServerItem mediaServerItem, RequestCallback callback){
@@ -137,33 +247,43 @@ public class AssistRESTfulUtils {
return sendGet(mediaServerItem, "api/record/info",param, callback);
}
public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){
Map<String, Object> param = new HashMap<>();
param.put("app",app);
param.put("stream",stream);
param.put("callId",callId);
return sendGet(mediaServerItem, "api/record/addStreamCallInfo",param, callback);
public JSONObject addTask(MediaServerItem mediaServerItem, String app, String stream, String startTime,
String endTime, String callId, List<String> filePathList, String remoteHost) {
JSONObject videoTaskInfoJSON = new JSONObject();
videoTaskInfoJSON.put("app", app);
videoTaskInfoJSON.put("stream", stream);
videoTaskInfoJSON.put("startTime", startTime);
videoTaskInfoJSON.put("endTime", endTime);
videoTaskInfoJSON.put("callId", callId);
videoTaskInfoJSON.put("filePathList", filePathList);
if (!ObjectUtils.isEmpty(remoteHost)) {
videoTaskInfoJSON.put("remoteHost", remoteHost);
}
String urlStr = String.format("%s/api/record/file/download/task/add", remoteHost);;
return sendPost(mediaServerItem, urlStr, videoTaskInfoJSON, null, 30);
}
public JSONObject getDateList(MediaServerItem mediaServerItem, String app, String stream, int year, int month) {
public JSONObject queryTaskList(MediaServerItem mediaServerItem, String app, String stream, String callId,
String taskId, Boolean isEnd, String scheme) {
Map<String, Object> param = new HashMap<>();
param.put("app", app);
param.put("stream", stream);
param.put("year", year);
param.put("month", month);
return sendGet(mediaServerItem, "api/record/date/list", param, null);
if (!ObjectUtils.isEmpty(app)) {
param.put("app", app);
}
if (!ObjectUtils.isEmpty(stream)) {
param.put("stream", stream);
}
if (!ObjectUtils.isEmpty(callId)) {
param.put("callId", callId);
}
if (!ObjectUtils.isEmpty(taskId)) {
param.put("taskId", taskId);
}
if (!ObjectUtils.isEmpty(isEnd)) {
param.put("isEnd", isEnd);
}
String urlStr = String.format("%s://%s:%s/api/record/file/download/task/list",
scheme, mediaServerItem.getIp(), mediaServerItem.getRecordAssistPort());;
return sendGet(mediaServerItem, urlStr, param, null);
}
public JSONObject getFileList(MediaServerItem mediaServerItem, int page, int count, String app, String stream,
String startTime, String endTime) {
Map<String, Object> param = new HashMap<>();
param.put("app", app);
param.put("stream", stream);
param.put("page", page);
param.put("count", count);
param.put("startTime", startTime);
param.put("endTime", endTime);
return sendGet(mediaServerItem, "api/record/file/listWithDate", param, null);
}
}

View File

@@ -117,6 +117,9 @@ public class ZLMHttpHookListener {
@Autowired
private IUserService userService;
@Autowired
private ICloudRecordService cloudRecordService;
@Autowired
private VideoStreamSessionManager sessionManager;
@@ -238,12 +241,6 @@ public class ZLMHttpHookListener {
streamAuthorityInfo.setSign(sign);
// 鉴权通过
redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
// 通知assist新的callId
if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) {
taskExecutor.execute(() -> {
assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);
});
}
}
} else {
zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
@@ -269,51 +266,57 @@ public class ZLMHttpHookListener {
} else {
result.setEnable_mp4(userSetting.isRecordPushLive());
}
// 替换流地址
if ("rtp".equals(param.getApp()) && !mediaInfo.isRtpEnable()) {
String ssrc = String.format("%010d", Long.parseLong(param.getStream(), 16));;
InviteInfo inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc);
if (inviteInfo != null) {
result.setStream_replace(inviteInfo.getStream());
logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream());
}
}
List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
String channelId = ssrcTransactionForAll.get(0).getChannelId();
DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
if (deviceChannel != null) {
// 国标流
if ("rtp".equals(param.getApp()) ) {
result.setEnable_audio(deviceChannel.isHasAudio());
}
// 如果是录像下载就设置视频间隔十秒
if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
result.setMp4_max_second(10);
result.setEnable_mp4(true);
}
// 如果是talk对讲则默认获取声音
if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) {
result.setEnable_audio(true);
InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
// 单端口模式下修改流 ID
if (!mediaInfo.isRtpEnable() && inviteInfo == null) {
String ssrc = String.format("%010d", Long.parseLong(param.getStream(), 16));
inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc);
if (inviteInfo != null) {
result.setStream_replace(inviteInfo.getStream());
logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream());
}
}
}
if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
logger.info("推流时发现尚未设置录像路径从assist服务中读取");
JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null);
if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0) {
JSONObject dataJson = info.getJSONObject("data");
if (dataJson != null) {
String recordPath = dataJson.getString("record");
userSetting.setRecordPath(recordPath);
result.setMp4_save_path(recordPath);
// 修改zlm中的录像路径
if (mediaInfo.isAutoConfig()) {
taskExecutor.execute(() -> {
mediaServerService.setZLMConfig(mediaInfo, false);
});
// 设置音频信息及录制信息
List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
// 为录制国标模拟一个鉴权信息, 方便后续写入录像文件时使用
StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
streamAuthorityInfo.setApp(param.getApp());
streamAuthorityInfo.setStream(ssrcTransactionForAll.get(0).getStream());
streamAuthorityInfo.setCallId(ssrcTransactionForAll.get(0).getSipTransactionInfo().getCallId());
redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), ssrcTransactionForAll.get(0).getStream(), streamAuthorityInfo);
String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
String channelId = ssrcTransactionForAll.get(0).getChannelId();
DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
if (deviceChannel != null) {
result.setEnable_audio(deviceChannel.isHasAudio());
}
// 如果是录像下载就设置视频间隔十秒
if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
// 获取录像的总时长,然后设置为这个视频的时长
InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, param.getStream());
if (inviteInfoForDownload != null && inviteInfoForDownload.getStreamInfo() != null) {
String startTime = inviteInfoForDownload.getStreamInfo().getStartTime();
String endTime = inviteInfoForDownload.getStreamInfo().getEndTime();
long difference = DateUtil.getDifference(startTime, endTime) / 1000;
result.setMp4_max_second((int) difference);
result.setEnable_mp4(true);
// 设置为2保证得到的mp4的时长是正常的
result.setModify_stamp(2);
}
}
// 如果是talk对讲则默认获取声音
if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) {
result.setEnable_audio(true);
}
}
}
if (param.getApp().equalsIgnoreCase("rtp")) {
@@ -361,13 +364,11 @@ public class ZLMHttpHookListener {
List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
// TODO 重构此处逻辑
boolean isPush = false;
if (param.isRegist()) {
// 处理流注册的鉴权信息
// 处理流注册的鉴权信息 流注销这里不再删除鉴权信息,下次来了新的鉴权信息会对就的进行覆盖
if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
isPush = true;
StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
if (streamAuthorityInfo == null) {
streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
@@ -377,8 +378,6 @@ public class ZLMHttpHookListener {
}
redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
}
} else {
redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
}
if ("rtsp".equals(param.getSchema())) {
@@ -460,35 +459,40 @@ public class ZLMHttpHookListener {
} else {
if (!"rtp".equals(param.getApp())) {
String type = OriginType.values()[param.getOriginType()].getType();
MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
if (param.isRegist()) {
StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(
param.getApp(), param.getStream());
String callId = null;
if (streamAuthorityInfo != null) {
callId = streamAuthorityInfo.getCallId();
}
StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaInfo,
param.getApp(), param.getStream(), tracks, callId);
param.setStreamInfo(new StreamContent(streamInfoByAppAndStream));
redisCatchStorage.addStream(mediaInfo, type, param.getApp(), param.getStream(), param);
if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
param.setSeverId(userSetting.getServerId());
zlmMediaListManager.addPush(param);
if (mediaServerItem != null) {
if (param.isRegist()) {
StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
String callId = null;
if (streamAuthorityInfo != null) {
callId = streamAuthorityInfo.getCallId();
// 冗余数据,自己系统中自用
redisCatchStorage.addPushListItem(param.getApp(), param.getStream(), param);
}
} else {
// 兼容流注销时类型从redis记录获取
OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
param.getApp(), param.getStream(), param.getMediaServerId());
if (onStreamChangedHookParam != null) {
type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
redisCatchStorage.removeStream(mediaInfo.getId(), type, param.getApp(), param.getStream());
if ("PUSH".equalsIgnoreCase(type)) {
// 冗余数据,自己系统中自用
redisCatchStorage.removePushListItem(param.getApp(), param.getStream(), param.getMediaServerId());
}
StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem,
param.getApp(), param.getStream(), param.getTracks(), callId);
param.setStreamInfo(new StreamContent(streamInfoByAppAndStream));
redisCatchStorage.addStream(mediaServerItem, type, param.getApp(), param.getStream(), param);
if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
param.setSeverId(userSetting.getServerId());
zlmMediaListManager.addPush(param);
}
} else {
// 兼容流注销时类型从redis记录获取
OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
param.getApp(), param.getStream(), param.getMediaServerId());
if (onStreamChangedHookParam != null) {
type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
redisCatchStorage.removeStream(mediaServerItem.getId(), type, param.getApp(), param.getStream());
}
GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
if (gbStream != null) {
}
GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
if (gbStream != null) {
// eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
}
zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
@@ -513,7 +517,7 @@ public class ZLMHttpHookListener {
}
if (!param.isRegist()) {
List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
if (sendRtpItems.size() > 0) {
if (!sendRtpItems.isEmpty()) {
for (SendRtpItem sendRtpItem : sendRtpItems) {
if (sendRtpItem != null && sendRtpItem.getApp().equals(param.getApp())) {
String platformId = sendRtpItem.getPlatformId();
@@ -608,11 +612,15 @@ public class ZLMHttpHookListener {
if (info != null) {
cmder.streamByeCmd(device, inviteInfo.getChannelId(),
inviteInfo.getStream(), null);
}else {
logger.info("[无人观看] 未找到设备的点播信息: {} 流:{}", inviteInfo.getDeviceId(), param.getStream());
}
} catch (InvalidArgumentException | ParseException | SipException |
SsrcTransactionNotFoundException e) {
logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage());
}
}else {
logger.info("[无人观看] 未找到设备: {},流:{}", inviteInfo.getDeviceId(), param.getStream());
}
inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
@@ -684,7 +692,7 @@ public class ZLMHttpHookListener {
String deviceId = s[0];
String channelId = s[1];
Device device = redisCatchStorage.getDevice(deviceId);
if (device == null) {
if (device == null || !device.isOnLine()) {
defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
return defaultResult;
}
@@ -848,7 +856,7 @@ public class ZLMHttpHookListener {
taskExecutor.execute(() -> {
JSONObject json = (JSONObject) JSON.toJSON(param);
List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
if (subscribes != null && subscribes.size() > 0) {
if (subscribes != null && !subscribes.isEmpty()) {
for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
subscribe.response(null, param);
}
@@ -858,6 +866,28 @@ public class ZLMHttpHookListener {
return HookResult.SUCCESS();
}
/**
* 录像完成事件
*/
@ResponseBody
@PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
public HookResult onRecordMp4(HttpServletRequest request, @RequestBody OnRecordMp4HookParam param) {
logger.info("[ZLM HOOK] 录像完成事件:{}->{}", param.getMediaServerId(), param.getFile_path());
taskExecutor.execute(() -> {
List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_record_mp4);
if (subscribes != null && !subscribes.isEmpty()) {
for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
subscribe.response(null, param);
}
}
cloudRecordService.addRecord(param);
});
return HookResult.SUCCESS();
}
private Map<String, String> urlParamToMap(String params) {
HashMap<String, String> map = new HashMap<>();
if (ObjectUtils.isEmpty(params)) {

View File

@@ -25,8 +25,6 @@ public class ZLMRESTfulUtils {
private OkHttpClient client;
public interface RequestCallback{
void run(JSONObject response);
}
@@ -405,4 +403,14 @@ public class ZLMRESTfulUtils {
param.put("stream_id", streamId);
return sendPost(mediaServerItem, "updateRtpServerSSRC",param, null);
}
public JSONObject deleteRecordDirectory(MediaServerItem mediaServerItem, String app, String stream, String date, String fileName) {
Map<String, Object> param = new HashMap<>(1);
param.put("vhost", "__defaultVhost__");
param.put("app", app);
param.put("stream", stream);
param.put("period", date);
param.put("name", fileName);
return sendPost(mediaServerItem, "deleteRecordDirectory",param, null);
}
}

View File

@@ -57,4 +57,15 @@ public class HookSubscribeFactory {
return hookSubscribe;
}
public static HookSubscribeForRecordMp4 on_record_mp4(String mediaServerId, String app, String stream) {
HookSubscribeForRecordMp4 hookSubscribe = new HookSubscribeForRecordMp4();
JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
subscribeKey.put("app", app);
subscribeKey.put("stream", stream);
subscribeKey.put("mediaServerId", mediaServerId);
hookSubscribe.setContent(subscribeKey);
return hookSubscribe;
}
}

View File

@@ -0,0 +1,44 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import java.time.Instant;
/**
* hook订阅-录像完成
* @author lin
*/
public class HookSubscribeForRecordMp4 implements IHookSubscribe{
private HookType hookType = HookType.on_record_mp4;
private JSONObject content;
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Instant expires;
@Override
public HookType getHookType() {
return hookType;
}
@Override
public JSONObject getContent() {
return content;
}
public void setContent(JSONObject content) {
this.content = content;
}
@Override
public Instant getExpires() {
return expires;
}
@Override
public void setExpires(Instant expires) {
this.expires = expires;
}
}

View File

@@ -80,9 +80,11 @@ public class MediaServerItem{
@Schema(description = "是否是默认ZLM")
private boolean defaultServer;
@Schema(description = "当前使用到的端口")
private int currentPort;
@Schema(description = "录像存储时长")
private int recordDay;
@Schema(description = "录像存储路径")
private String recordPath;
public MediaServerItem() {
}
@@ -269,14 +271,6 @@ public class MediaServerItem{
this.updateTime = updateTime;
}
public int getCurrentPort() {
return currentPort;
}
public void setCurrentPort(int currentPort) {
this.currentPort = currentPort;
}
public boolean isStatus() {
return status;
}
@@ -308,4 +302,20 @@ public class MediaServerItem{
public void setSendRtpPortRange(String sendRtpPortRange) {
this.sendRtpPortRange = sendRtpPortRange;
}
public int getRecordDay() {
return recordDay;
}
public void setRecordDay(int recordDay) {
this.recordDay = recordDay;
}
public String getRecordPath() {
return recordPath;
}
public void setRecordPath(String recordPath) {
this.recordPath = recordPath;
}
}

View File

@@ -7,6 +7,7 @@ public class HookResultForOnPublish extends HookResult{
private int mp4_max_second;
private String mp4_save_path;
private String stream_replace;
private Integer modify_stamp;
public HookResultForOnPublish() {
}
@@ -60,14 +61,23 @@ public class HookResultForOnPublish extends HookResult{
this.stream_replace = stream_replace;
}
public Integer getModify_stamp() {
return modify_stamp;
}
public void setModify_stamp(Integer modify_stamp) {
this.modify_stamp = modify_stamp;
}
@Override
public String toString() {
return "HookResultForOnPublish{" +
"enable_audio=" + enable_audio +
", enable_mp4=" + enable_mp4 +
", mp4_max_second=" + mp4_max_second +
", stream_replace=" + stream_replace +
", mp4_save_path='" + mp4_save_path + '\'' +
", stream_replace='" + stream_replace + '\'' +
", modify_stamp='" + modify_stamp + '\'' +
'}';
}
}

View File

@@ -0,0 +1,114 @@
package com.genersoft.iot.vmp.media.zlm.dto.hook;
/**
* zlm hook事件中的on_rtp_server_timeout事件的参数
* @author lin
*/
public class OnRecordMp4HookParam extends HookParam{
private String app;
private String stream;
private String file_name;
private String file_path;
private long file_size;
private String folder;
private String url;
private String vhost;
private long start_time;
private double time_len;
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getStream() {
return stream;
}
public void setStream(String stream) {
this.stream = stream;
}
public String getFile_name() {
return file_name;
}
public void setFile_name(String file_name) {
this.file_name = file_name;
}
public String getFile_path() {
return file_path;
}
public void setFile_path(String file_path) {
this.file_path = file_path;
}
public long getFile_size() {
return file_size;
}
public void setFile_size(long file_size) {
this.file_size = file_size;
}
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 String getVhost() {
return vhost;
}
public void setVhost(String vhost) {
this.vhost = vhost;
}
public long getStart_time() {
return start_time;
}
public void setStart_time(long start_time) {
this.start_time = start_time;
}
public double getTime_len() {
return time_len;
}
public void setTime_len(double time_len) {
this.time_len = time_len;
}
@Override
public String toString() {
return "OnRecordMp4HookParam{" +
"app='" + app + '\'' +
", stream='" + stream + '\'' +
", file_name='" + file_name + '\'' +
", file_path='" + file_path + '\'' +
", file_size='" + file_size + '\'' +
", folder='" + folder + '\'' +
", url='" + url + '\'' +
", vhost='" + vhost + '\'' +
", start_time=" + start_time +
", time_len=" + time_len +
'}';
}
}

View File

@@ -120,17 +120,17 @@ public class OnStreamChangedHookParam extends HookParam{
/**
* H264 = 0, H265 = 1, AAC = 2, G711A = 3, G711U = 4
*/
private int codecId;
private int codec_id;
/**
* 编码类型名称 CodecAAC CodecH264
*/
private String codecIdName;
private String codec_id_name;
/**
* Video = 0, Audio = 1
*/
private int codecType;
private int codec_type;
/**
* 轨道是否准备就绪
@@ -140,17 +140,17 @@ public class OnStreamChangedHookParam extends HookParam{
/**
* 音频采样位数
*/
private int sampleBit;
private int sample_bit;
/**
* 音频采样率
*/
private int sampleRate;
private int sample_rate;
/**
* 视频fps
*/
private int fps;
private float fps;
/**
* 视频高
@@ -162,6 +162,31 @@ public class OnStreamChangedHookParam extends HookParam{
*/
private int width;
/**
* 帧数
*/
private int frames;
/**
* 关键帧数
*/
private int key_frames;
/**
* GOP大小
*/
private int gop_size;
/**
* GOP间隔时长(ms)
*/
private int gop_interval_ms;
/**
* 丢帧率
*/
private float loss;
public int getChannels() {
return channels;
}
@@ -170,28 +195,28 @@ public class OnStreamChangedHookParam extends HookParam{
this.channels = channels;
}
public int getCodecId() {
return codecId;
public int getCodec_id() {
return codec_id;
}
public void setCodecId(int codecId) {
this.codecId = codecId;
public void setCodec_id(int codec_id) {
this.codec_id = codec_id;
}
public String getCodecIdName() {
return codecIdName;
public String getCodec_id_name() {
return codec_id_name;
}
public void setCodecIdName(String codecIdName) {
this.codecIdName = codecIdName;
public void setCodec_id_name(String codec_id_name) {
this.codec_id_name = codec_id_name;
}
public int getCodecType() {
return codecType;
public int getCodec_type() {
return codec_type;
}
public void setCodecType(int codecType) {
this.codecType = codecType;
public void setCodec_type(int codec_type) {
this.codec_type = codec_type;
}
public boolean isReady() {
@@ -202,27 +227,27 @@ public class OnStreamChangedHookParam extends HookParam{
this.ready = ready;
}
public int getSampleBit() {
return sampleBit;
public int getSample_bit() {
return sample_bit;
}
public void setSampleBit(int sampleBit) {
this.sampleBit = sampleBit;
public void setSample_bit(int sample_bit) {
this.sample_bit = sample_bit;
}
public int getSampleRate() {
return sampleRate;
public int getSample_rate() {
return sample_rate;
}
public void setSampleRate(int sampleRate) {
this.sampleRate = sampleRate;
public void setSample_rate(int sample_rate) {
this.sample_rate = sample_rate;
}
public int getFps() {
public float getFps() {
return fps;
}
public void setFps(int fps) {
public void setFps(float fps) {
this.fps = fps;
}
@@ -241,6 +266,46 @@ public class OnStreamChangedHookParam extends HookParam{
public void setWidth(int width) {
this.width = width;
}
public int getFrames() {
return frames;
}
public void setFrames(int frames) {
this.frames = frames;
}
public int getKey_frames() {
return key_frames;
}
public void setKey_frames(int key_frames) {
this.key_frames = key_frames;
}
public int getGop_size() {
return gop_size;
}
public void setGop_size(int gop_size) {
this.gop_size = gop_size;
}
public int getGop_interval_ms() {
return gop_interval_ms;
}
public void setGop_interval_ms(int gop_interval_ms) {
this.gop_interval_ms = gop_interval_ms;
}
public float getLoss() {
return loss;
}
public void setLoss(float loss) {
this.loss = loss;
}
}
public static class OriginSock{