diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java index ea0b2241..e8921c67 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java @@ -406,4 +406,24 @@ public class ZLMRESTfulUtils { param.put("name", fileName); return sendPost(mediaServerItem, "deleteRecordDirectory",param, null); } + + public JSONObject loadMP4File(MediaServerItem mediaServerItem, String app, String stream, String file_path) { + Map param = new HashMap<>(1); + param.put("vhost", "__defaultVhost__"); + param.put("app", app); + param.put("stream", stream); + param.put("file_path", file_path); + param.put("file_repeat", '0'); + param.put("enable_hls", '1'); + param.put("enable_hls_fmp4", '1'); + param.put("enable_mp4", '0'); + param.put("enable_rtsp", '1'); + param.put("enable_rtmp", '1'); + param.put("enable_ts", '1'); + param.put("enable_fmp4", '1'); + param.put("enable_audio", '1'); + param.put("add_mute_audio", '1'); + param.put("auto_close", '1'); + return sendPost(mediaServerItem, "loadMP4File",param, null); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java b/src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java index d1f9f535..e0affe14 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java @@ -1,10 +1,12 @@ package com.genersoft.iot.vmp.service; import com.alibaba.fastjson2.JSONArray; +import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam; 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; @@ -56,8 +58,5 @@ public interface ICloudRecordService { */ DownloadFileInfo getPlayUrlPath(Integer recordId); - /** - * 点播视频流 - */ - DownloadFileInfo getLivePath(Integer recordId); + void getLivePath(Integer recordId, ErrorCallback callback); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java index d0c46862..17752b53 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java @@ -2,16 +2,31 @@ package com.genersoft.iot.vmp.service.impl; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionStatus; +import com.genersoft.iot.vmp.common.InviteSessionType; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils; +import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; +import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam; import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam; +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; import com.genersoft.iot.vmp.service.ICloudRecordService; import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.IMediaService; 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.bean.InviteErrorCode; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper; import com.genersoft.iot.vmp.utils.CloudRecordUtils; @@ -25,6 +40,9 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; import java.time.*; import java.util.*; @@ -39,6 +57,9 @@ public class CloudRecordServiceImpl implements ICloudRecordService { @Autowired private IMediaServerService mediaServerService; + @Autowired + private IMediaService mediaService; + @Autowired private IRedisCatchStorage redisCatchStorage; @@ -46,7 +67,13 @@ public class CloudRecordServiceImpl implements ICloudRecordService { private AssistRESTfulUtils assistRESTfulUtils; @Autowired - private VideoStreamSessionManager streamSession; + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + @Autowired + private DynamicTask dynamicTask; @Override public PageInfo getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List mediaServerItems) { @@ -239,4 +266,46 @@ public class CloudRecordServiceImpl implements ICloudRecordService { MediaServerItem mediaServerItem = mediaServerService.getOne(recordItem.getMediaServerId()); return CloudRecordUtils.getDownloadFilePath(mediaServerItem, filePath); } + + @Override + public void getLivePath(Integer recordId, ErrorCallback callback) { + CloudRecordItem recordItem = cloudRecordServiceMapper.queryOne(recordId); + if (recordItem == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "资源不存在"); + } + // 监听流上线 + String app = "record-live"; + String stream = recordItem.getId() + ""; + MediaServerItem mediaServerItem = mediaServerService.getOne(recordItem.getMediaServerId()); + if (mediaServerItem == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "录像记录使用的流媒体节点不在线"); + } + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, recordItem.getMediaServerId(), false); + if (streamInfo != null) { + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); + return; + } + String timeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(timeOutTaskKey, () -> { + // 取消订阅消息监听 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(app, stream, true, "rtsp", mediaServerItem.getId()); + subscribe.removeSubscribe(hookSubscribe); + callback.run(ErrorCode.ERROR100.getCode(), "加载视频文件为视频流超时", null); + }, 10000); + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(app, stream, true, "rtsp", mediaServerItem.getId()); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> { + dynamicTask.stop(timeOutTaskKey); + OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam)hookParam; + StreamInfo streamInfoForHook = mediaService.getStreamInfoByAppAndStream(mediaServerItem, app, stream, streamChangedHookParam.getTracks(), null); + subscribe.removeSubscribe(hookSubscribe); + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfoForHook); + }); + JSONObject jsonObject = zlmresTfulUtils.loadMP4File(mediaServerItem, app, stream, recordItem.getFilePath()); + if (jsonObject == null || jsonObject.getInteger("code") != 0) { + subscribe.removeSubscribe(hookSubscribe); + dynamicTask.stop(timeOutTaskKey); + callback.run(ErrorCode.SUCCESS.getCode(), + jsonObject != null ? jsonObject.getString("msg"): "加载视频文件为视频流失败", null); + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java index 086c268d..225f6ac9 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java @@ -1,6 +1,7 @@ package com.genersoft.iot.vmp.vmanager.cloudRecord; import com.alibaba.fastjson2.JSONArray; +import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.ControllerException; @@ -12,9 +13,7 @@ import com.genersoft.iot.vmp.service.ICloudRecordService; import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.bean.CloudRecordItem; import com.genersoft.iot.vmp.service.bean.DownloadFileInfo; -import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; -import com.genersoft.iot.vmp.vmanager.bean.WVPPageInfo; -import com.genersoft.iot.vmp.vmanager.bean.RecordFile; +import com.genersoft.iot.vmp.vmanager.bean.*; import com.github.pagehelper.PageInfo; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -26,7 +25,11 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; +import javax.servlet.http.HttpServletRequest; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -47,6 +50,9 @@ public class CloudRecordController { @Autowired private IMediaServerService mediaServerService; + @Autowired + private UserSetting userSetting; + @ResponseBody @GetMapping("/date/list") @@ -265,11 +271,32 @@ public class CloudRecordController { @ResponseBody @GetMapping("/play/live") - @Operation(summary = "获取点播i地址") + @Operation(summary = "获取点播地址") @Parameter(name = "recordId", description = "录像记录的ID", required = true) - public DownloadFileInfo getLivePath( - @RequestParam(required = true) Integer recordId + public DeferredResult> getLivePath( + HttpServletRequest request, @RequestParam(required = true) Integer recordId ){ - return cloudRecordService.getLivePath(recordId); + DeferredResult> result = new DeferredResult<>(); + cloudRecordService.getLivePath(recordId, (code, msg, data) -> { + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(code); + wvpResult.setMsg(msg); + if (code == ErrorCode.SUCCESS.getCode()) { + if (userSetting.getUseSourceIpAsStreamIp()) { + data=data.clone();//深拷贝 + String host; + try { + URL url=new URL(request.getRequestURL().toString()); + host=url.getHost(); + } catch (MalformedURLException e) { + host=request.getLocalAddr(); + } + data.channgeStreamIp(host); + } + wvpResult.setData(new StreamContent(data)); + } + result.setResult(wvpResult); + }); + return result; } }