diff --git a/gb28181-wvp-proxy-api/src/main/java/cn/skcks/docking/gb28181/wvp/api/video/VideoController.java b/gb28181-wvp-proxy-api/src/main/java/cn/skcks/docking/gb28181/wvp/api/video/VideoController.java index dfe487f..7277586 100644 --- a/gb28181-wvp-proxy-api/src/main/java/cn/skcks/docking/gb28181/wvp/api/video/VideoController.java +++ b/gb28181-wvp-proxy-api/src/main/java/cn/skcks/docking/gb28181/wvp/api/video/VideoController.java @@ -2,6 +2,7 @@ package cn.skcks.docking.gb28181.wvp.api.video; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; +import cn.skcks.docking.gb28181.common.json.JsonResponse; import cn.skcks.docking.gb28181.media.config.ZlmMediaConfig; import cn.skcks.docking.gb28181.wvp.api.video.dto.VideoMp4Req; import cn.skcks.docking.gb28181.wvp.api.video.dto.VideoReq; @@ -25,6 +26,7 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.request.async.DeferredResult; import java.util.Date; @@ -60,10 +62,10 @@ public class VideoController { } } - @Operation(summary = "获取视频 (返回视频流)") - @GetMapping(value = "/device/video.mp4",produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + @Operation(summary = "获取视频 (返回视频url)") + @GetMapping(value = "/device/video.mp4") @ResponseBody - public void video(HttpServletRequest request, HttpServletResponse response, @ParameterObject VideoMp4Req req) { + public DeferredResult> video(@ParameterObject VideoMp4Req req) { long forward = gb28181DeviceVideoApiConfig.getOffset().getForward().toMillis(); long back = gb28181DeviceVideoApiConfig.getOffset().getBack().toMillis(); DateTime reqStartTime = DateUtil.date(req.getStartTime()); @@ -72,6 +74,6 @@ public class VideoController { Date endTime = DateUtil.offsetMillisecond(reqEndTime, (int) back); log.info("请求的时间范围 {} ~ {}", reqStartTime, reqEndTime); log.info("偏移后的时间范围 {} ~ {}", startTime, endTime); - gb28181DownloadService.videoStream(request,response,req.getDeviceCode(), startTime, endTime); + return gb28181DownloadService.videoUrl(req.getDeviceCode(), startTime, endTime); } } diff --git a/gb28181-wvp-proxy-service/src/main/java/cn/skcks/docking/gb28181/wvp/service/gb28181/Gb28181DownloadService.java b/gb28181-wvp-proxy-service/src/main/java/cn/skcks/docking/gb28181/wvp/service/gb28181/Gb28181DownloadService.java index b496113..3cc0d41 100644 --- a/gb28181-wvp-proxy-service/src/main/java/cn/skcks/docking/gb28181/wvp/service/gb28181/Gb28181DownloadService.java +++ b/gb28181-wvp-proxy-service/src/main/java/cn/skcks/docking/gb28181/wvp/service/gb28181/Gb28181DownloadService.java @@ -49,6 +49,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; +import org.springframework.web.context.request.async.DeferredResult; import javax.sdp.Connection; import javax.sdp.SdpFactory; @@ -84,6 +85,7 @@ public class Gb28181DownloadService { @AllArgsConstructor @Data public static class VideoInfo { + private String streamId; private String url; private String callId; private WvpProxyDevice device; @@ -166,6 +168,42 @@ public class Gb28181DownloadService { }); } + @SneakyThrows + public DeferredResult> videoUrl(String deviceCode, Date startTime, Date endTime) { + long time = DateUtil.between(startTime, endTime, DateUnit.MS); + DeferredResult> result = new DeferredResult<>(time); + result.onTimeout(()->{ + result.setResult(JsonResponse.error("请求超时")); + }); + WvpProxyDevice device = deviceService.getDeviceByDeviceCode(deviceCode).orElse(null); + if(device == null){ + result.setResult(JsonResponse.error("设备不存在")); + return result; + } + WvpProxyDocking docking = dockingService.getDeviceByDeviceCode(device.getGbDeviceId()).orElse(null); + if(docking == null){ + result.setResult(JsonResponse.error("设备(通道)不存在")); + return result; + } + download(deviceCode, startTime, endTime).whenComplete((videoInfo, e)->{ + log.info("到达结束时间 发送 bye 关闭"); + String cacheKey = CacheUtil.getKey(docking.getGbDeviceId(), device.getGbDeviceChannelId()); + String existCallId = RedisUtil.StringOps.get(cacheKey); + scheduledExecutorService.schedule(()->{ + String deviceIp = docking.getIp(); + int devicePort = Integer.parseInt(docking.getPort()); + sender.sendRequest((provider,localIp,localPort)-> + SipRequestBuilder.createByeRequest(deviceIp, devicePort, device.getGbDeviceChannelId(), SipUtil.generateFromTag(), null, existCallId)); + RedisUtil.KeyOps.delete(cacheKey); + zlmMediaService.closeRtpServer(CloseRtpServer.builder() + .streamId(videoInfo.streamId) + .build()); + }, time, TimeUnit.MILLISECONDS); + result.setResult(JsonResponse.success(videoInfo.getUrl())); + }); + return result; + } + @SneakyThrows public void videoStream(HttpServletRequest request, HttpServletResponse response, String deviceCode, Date startTime, Date endTime) { AsyncContext asyncContext = request.startAsync(); @@ -289,7 +327,7 @@ public class Gb28181DownloadService { SipRequestBuilder.createByeRequest(deviceIp, devicePort, device.getGbDeviceChannelId(), SipUtil.generateFromTag(), null, existCallId)); RedisUtil.KeyOps.delete(cacheKey); log.info("关闭已存在的 点播请求 {} {}", cacheKey,existCallId); - Thread.sleep(200); + Thread.sleep(500); } return (provider, ip, port) -> { @@ -334,7 +372,7 @@ public class Gb28181DownloadService { subscribe.getByeSubscribe().addSubscribe(key, byeSubscriber(key,cacheKey, streamId, time, unit)); return SipRequestBuilder.createAckRequest(Response.OK, ip, port, docking, device.getGbDeviceChannelId(), fromTag, toTag, callId); })); - result.complete(new VideoInfo(videoUrl(streamId), callId, device)); + result.complete(new VideoInfo(streamId,videoUrl(streamId), callId, device)); } else { log.info("订阅 {} {} 连接流媒体服务时出现异常, 终止订阅", MessageProcessor.Method.INVITE, subscribeKey); zlmMediaService.closeRtpServer(new CloseRtpServer(streamId));