From 09f1ef20a7af0454887457cd81ad6f39401feed5 Mon Sep 17 00:00:00 2001 From: lin <648540858@qq.com> Date: Fri, 1 Aug 2025 18:59:15 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=9A=E7=94=A8=E9=80=9A=E9=81=93=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=BD=95=E5=83=8F=E5=9B=9E=E6=94=BE=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E5=BD=95=E5=83=8F=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vmp/gb28181/bean/CommonRecordInfo.java | 14 ++ .../iot/vmp/gb28181/bean/RecordItem.java | 90 +---------- .../gb28181/controller/ChannelController.java | 148 +++++++++++++++++- .../controller/PlaybackController.java | 26 +-- .../service/IGbChannelPlayService.java | 9 ++ .../gb28181/service/IGbChannelService.java | 3 - .../iot/vmp/gb28181/service/IPlayService.java | 4 + .../service/ISourcePlaybackService.java | 11 ++ .../impl/GbChannelPlayServiceImpl.java | 47 +++++- .../service/impl/GbChannelServiceImpl.java | 19 --- .../gb28181/service/impl/PlayServiceImpl.java | 26 +++ .../impl/SourcePlaybackServiceForGbImpl.java | 69 +++++++- .../RedisRpcChannelPlayController.java | 2 +- web/src/api/commonChannel.js | 74 +++++++++ web/src/router/index.js | 5 +- web/src/store/modules/commonChanel.js | 97 +++++++++++- web/src/views/channel/index.vue | 2 +- web/src/views/channel/record.vue | 76 +++++---- 18 files changed, 537 insertions(+), 185 deletions(-) create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/bean/CommonRecordInfo.java diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CommonRecordInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CommonRecordInfo.java new file mode 100644 index 000000000..9f2efa913 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CommonRecordInfo.java @@ -0,0 +1,14 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import lombok.Data; + +@Data +public class CommonRecordInfo { + + // 开始时间 + private String startTime; + + // 结束时间 + private String endTime; + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java index 452e13886..c6853330f 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java @@ -3,16 +3,20 @@ package com.genersoft.iot.vmp.gb28181.bean; import com.genersoft.iot.vmp.utils.DateUtil; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; import org.jetbrains.annotations.NotNull; import java.time.Instant; import java.time.temporal.TemporalAccessor; /** - * @description:设备录像bean + * @description:设备录像bean * @author: swwheihei - * @date: 2020年5月8日 下午2:06:54 + * @date: 2020年5月8日 下午2:06:54 */ +@Setter +@Getter @Schema(description = "设备录像详情") public class RecordItem implements Comparable{ @@ -46,87 +50,7 @@ public class RecordItem implements Comparable{ @Schema(description = "录像触发者ID(可选)") private String recorderId; - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getFilePath() { - return filePath; - } - - public void setFilePath(String filePath) { - this.filePath = filePath; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public String getStartTime() { - return startTime; - } - - public void setStartTime(String startTime) { - this.startTime = startTime; - } - - public String getEndTime() { - return endTime; - } - - public void setEndTime(String endTime) { - this.endTime = endTime; - } - - public int getSecrecy() { - return secrecy; - } - - public void setSecrecy(int secrecy) { - this.secrecy = secrecy; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getRecorderId() { - return recorderId; - } - - public void setRecorderId(String recorderId) { - this.recorderId = recorderId; - } - - public String getFileSize() { - return fileSize; - } - - public void setFileSize(String fileSize) { - this.fileSize = fileSize; - } - - @Override + @Override public int compareTo(@NotNull RecordItem recordItem) { TemporalAccessor startTimeNow = DateUtil.formatter.parse(startTime); TemporalAccessor startTimeParam = DateUtil.formatter.parse(recordItem.getStartTime()); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/ChannelController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/ChannelController.java index f72278f5f..f2b5a03c1 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/ChannelController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/ChannelController.java @@ -2,11 +2,9 @@ package com.genersoft.iot.vmp.gb28181.controller; 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.conf.security.JwtUtils; -import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; -import com.genersoft.iot.vmp.gb28181.bean.DeviceType; -import com.genersoft.iot.vmp.gb28181.bean.IndustryCodeType; -import com.genersoft.iot.vmp.gb28181.bean.NetworkIdentificationType; +import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelToGroupByGbDeviceParam; import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelToGroupParam; import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelToRegionByGbDeviceParam; @@ -17,6 +15,8 @@ import com.genersoft.iot.vmp.media.service.IMediaServerService; 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.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.StreamContent; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.github.pagehelper.PageInfo; @@ -35,6 +35,7 @@ import javax.servlet.http.HttpServletRequest; import java.net.MalformedURLException; import java.net.URL; import java.util.List; +import java.util.concurrent.TimeUnit; @Tag(name = "全局通道管理") @@ -325,4 +326,143 @@ public class ChannelController { Assert.notNull(channel, "通道不存在"); channelPlayService.stopPlay(channel, channel.getStreamId()); } + + @Operation(summary = "录像查询", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "channelId", description = "通道ID", required = true) + @Parameter(name = "startTime", description = "开始时间", required = true) + @Parameter(name = "endTime", description = "结束时间", required = true) + @GetMapping("/playback/query") + public DeferredResult>> queryRecord(Integer channelId, String startTime, String endTime){ + + DeferredResult>> result = new DeferredResult<>(Long.valueOf(userSetting.getRecordInfoTimeout()), TimeUnit.MILLISECONDS); + if (!DateUtil.verification(startTime, DateUtil.formatter)){ + throw new ControllerException(ErrorCode.ERROR100.getCode(), "startTime格式为" + DateUtil.PATTERN); + } + if (!DateUtil.verification(endTime, DateUtil.formatter)){ + throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime格式为" + DateUtil.PATTERN); + } + CommonGBChannel channel = channelService.getOne(channelId); + Assert.notNull(channel, "通道不存在"); + + channelPlayService.queryRecord(channel, startTime, endTime, (code, msg, data) -> { + WVPResult> wvpResult = new WVPResult<>(); + wvpResult.setCode(code); + wvpResult.setMsg(msg); + wvpResult.setData(data); + result.setResult(wvpResult); + }); + result.onTimeout(()->{ + WVPResult> wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("timeout"); + result.setResult(wvpResult); + }); + return result; + } + + @Operation(summary = "录像回放", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "channelId", description = "通道ID", required = true) + @Parameter(name = "startTime", description = "开始时间", required = true) + @Parameter(name = "endTime", description = "结束时间", required = true) + @GetMapping("/playback") + public DeferredResult> playback(HttpServletRequest request, Integer channelId, String startTime, String endTime){ + Assert.notNull(channelId,"参数异常"); + CommonGBChannel channel = channelService.getOne(channelId); + Assert.notNull(channel, "通道不存在"); + + DeferredResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + + ErrorCallback callback = (code, msg, streamInfo) -> { + if (code == InviteErrorCode.SUCCESS.getCode()) { + WVPResult wvpResult = WVPResult.success(); + if (streamInfo != null) { + if (userSetting.getUseSourceIpAsStreamIp()) { + streamInfo=streamInfo.clone();//深拷贝 + String host; + try { + URL url=new URL(request.getRequestURL().toString()); + host=url.getHost(); + } catch (MalformedURLException e) { + host=request.getLocalAddr(); + } + streamInfo.changeStreamIp(host); + } + if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix()) + && !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) { + streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix()); + } + wvpResult.setData(new StreamContent(streamInfo)); + }else { + wvpResult.setCode(code); + wvpResult.setMsg(msg); + } + + result.setResult(wvpResult); + }else { + result.setResult(WVPResult.fail(code, msg)); + } + }; + channelPlayService.playback(channel, DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime), + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime), callback); + return result; + } + + @Operation(summary = "停止录像回放", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "channelId", description = "通道ID", required = true) + @Parameter(name = "stream", description = "流ID", required = true) + @GetMapping("/playback/stop") + public void stopPlayback(Integer channelId, String stream){ + Assert.notNull(channelId,"参数异常"); + CommonGBChannel channel = channelService.getOne(channelId); + Assert.notNull(channel, "通道不存在"); + channelPlayService.stopPlayback(channel, stream); + } + + @Operation(summary = "暂停录像回放", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "channelId", description = "通道ID", required = true) + @Parameter(name = "stream", description = "流ID", required = true) + @GetMapping("/playback/pause") + public void pausePlayback(Integer channelId, String stream){ + Assert.notNull(channelId,"参数异常"); + CommonGBChannel channel = channelService.getOne(channelId); + Assert.notNull(channel, "通道不存在"); + channelPlayService.playbackPause(channel, stream); + } + + @Operation(summary = "恢复录像回放", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "channelId", description = "通道ID", required = true) + @Parameter(name = "stream", description = "流ID", required = true) + @GetMapping("/playback/resume") + public void resumePlayback(Integer channelId, String stream){ + Assert.notNull(channelId,"参数异常"); + CommonGBChannel channel = channelService.getOne(channelId); + Assert.notNull(channel, "通道不存在"); + channelPlayService.playbackResume(channel, stream); + } + + @Operation(summary = "拖动录像回放", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "channelId", description = "通道ID", required = true) + @Parameter(name = "stream", description = "流ID", required = true) + @Parameter(name = "seekTime", description = "将要播放的时间", required = true) + @GetMapping("/playback/seek") + public void seekPlayback(Integer channelId, String stream, Long seekTime){ + Assert.notNull(channelId,"参数异常"); + Assert.notNull(seekTime,"参数异常"); + CommonGBChannel channel = channelService.getOne(channelId); + Assert.notNull(channel, "通道不存在"); + channelPlayService.playbackSeek(channel, stream, seekTime); + } + + @Operation(summary = "拖动录像回放", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "channelId", description = "通道ID", required = true) + @Parameter(name = "stream", description = "流ID", required = true) + @Parameter(name = "speed", description = "倍速", required = true) + @GetMapping("/playback/speed") + public void seekPlayback(Integer channelId, String stream, Double speed){ + Assert.notNull(channelId,"参数异常"); + Assert.notNull(speed,"参数异常"); + CommonGBChannel channel = channelService.getOne(channelId); + Assert.notNull(channel, "通道不存在"); + channelPlayService.playbackSpeed(channel, stream, speed); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlaybackController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlaybackController.java index 1dc6e42e5..0ecc8261d 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlaybackController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlaybackController.java @@ -1,6 +1,5 @@ package com.genersoft.iot.vmp.gb28181.controller; -import com.genersoft.iot.vmp.common.InviteInfo; import com.genersoft.iot.vmp.common.InviteSessionType; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.UserSetting; @@ -26,6 +25,7 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -191,23 +191,14 @@ public class PlaybackController { } } - @Operation(summary = "回放拖动播放", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "streamId", description = "回放流ID", required = true) @Parameter(name = "seekTime", description = "拖动偏移量,单位s", required = true) @GetMapping("/seek/{streamId}/{seekTime}") - public void playSeek(@PathVariable String streamId, @PathVariable long seekTime) { + public void playbackSeek(@PathVariable String streamId, @PathVariable long seekTime) { log.info("playSeek: "+streamId+", "+seekTime); - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); - - if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { - log.warn("streamId不存在!"); - throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在"); - } - Device device = deviceService.getDeviceByDeviceId(inviteInfo.getDeviceId()); - DeviceChannel channel = channelService.getOneById(inviteInfo.getChannelId()); try { - cmder.playSeekCmd(device, channel, inviteInfo.getStreamInfo(), seekTime); + playService.playbackSeek(streamId, seekTime); } catch (InvalidArgumentException | ParseException | SipException e) { throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); } @@ -218,17 +209,10 @@ public class PlaybackController { @Parameter(name = "speed", description = "倍速0.25 0.5 1、2、4、8", required = true) @GetMapping("/speed/{streamId}/{speed}") public void playSpeed(@PathVariable String streamId, @PathVariable Double speed) { + Assert.notNull(speed, "倍速不存在"); log.info("playSpeed: "+streamId+", "+speed); - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); - - if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { - log.warn("streamId不存在!"); - throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在"); - } - Device device = deviceService.getDeviceByDeviceId(inviteInfo.getDeviceId()); - DeviceChannel channel = channelService.getOneById(inviteInfo.getChannelId()); try { - cmder.playSpeedCmd(device, channel, inviteInfo.getStreamInfo(), speed); + playService.playbackSpeed(streamId, speed); } catch (InvalidArgumentException | ParseException | SipException e) { throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelPlayService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelPlayService.java index 07214651a..754acd18d 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelPlayService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelPlayService.java @@ -3,10 +3,13 @@ package com.genersoft.iot.vmp.gb28181.service; import com.genersoft.iot.vmp.common.InviteSessionType; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.CommonRecordInfo; import com.genersoft.iot.vmp.gb28181.bean.InviteMessageInfo; import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import java.util.List; + public interface IGbChannelPlayService { void startInvite(CommonGBChannel channel, InviteMessageInfo inviteInfo, Platform platform, ErrorCallback callback); @@ -29,4 +32,10 @@ public interface IGbChannelPlayService { void playbackPause(CommonGBChannel channel, String streamId); void playbackResume(CommonGBChannel channel, String streamId); + + void playbackSeek(CommonGBChannel channel, String stream, long seekTime); + + void playbackSpeed(CommonGBChannel channel, String stream, Double speed); + + void queryRecord(CommonGBChannel channel, String startTime, String endTime, ErrorCallback> callback); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelService.java index 0aa27dd71..4e7497bc3 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelService.java @@ -1,7 +1,6 @@ package com.genersoft.iot.vmp.gb28181.service; import com.genersoft.iot.vmp.gb28181.bean.*; -import com.genersoft.iot.vmp.service.bean.ErrorCallback; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.streamPush.bean.StreamPush; import com.github.pagehelper.PageInfo; @@ -87,8 +86,6 @@ public interface IGbChannelService { PageInfo queryList(int page, int count, String query, Boolean online, Boolean hasRecordPlan, Integer channelType); - void queryRecordInfo(CommonGBChannel channel, String startTime, String endTime, ErrorCallback callback); - PageInfo queryListByCivilCodeForUnusual(int page, int count, String query, Boolean online, Integer channelType); void clearChannelCivilCode(Boolean all, List channelIds); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java index 3c94c63f6..32c3af615 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java @@ -52,6 +52,10 @@ public interface IPlayService { void playbackResume(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; + void playbackSeek(String streamId, long seekTime) throws InvalidArgumentException, ParseException, SipException; + + void playbackSpeed(String streamId, double speed) throws InvalidArgumentException, ParseException, SipException; + void startPushStream(SendRtpInfo sendRtpItem, DeviceChannel channel, SIPResponse sipResponse, Platform platform, CallIdHeader callIdHeader); void startSendRtpStreamFailHand(SendRtpInfo sendRtpItem, Platform platform, CallIdHeader callIdHeader); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/ISourcePlaybackService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/ISourcePlaybackService.java index c478501e1..b27114295 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/ISourcePlaybackService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/ISourcePlaybackService.java @@ -2,8 +2,11 @@ package com.genersoft.iot.vmp.gb28181.service; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.CommonRecordInfo; import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import java.util.List; + /** * 资源能力接入-录像回放 */ @@ -14,4 +17,12 @@ public interface ISourcePlaybackService { void stopPlayback(CommonGBChannel channel, String stream); void playbackPause(CommonGBChannel channel, String stream); + + void playbackResume(CommonGBChannel channel, String stream); + + void playbackSeek(CommonGBChannel channel, String stream, long seekTime); + + void playbackSpeed(CommonGBChannel channel, String stream, Double speed); + + void queryRecord(CommonGBChannel channel, String startTime, String endTime, ErrorCallback> callback); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelPlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelPlayServiceImpl.java index b510ff391..1bd177d44 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelPlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelPlayServiceImpl.java @@ -4,10 +4,7 @@ import com.genersoft.iot.vmp.common.InviteSessionType; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.common.enums.ChannelDataType; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; -import com.genersoft.iot.vmp.gb28181.bean.InviteMessageInfo; -import com.genersoft.iot.vmp.gb28181.bean.Platform; -import com.genersoft.iot.vmp.gb28181.bean.PlayException; +import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.dao.CommonGBChannelMapper; import com.genersoft.iot.vmp.gb28181.service.*; import com.genersoft.iot.vmp.service.bean.ErrorCallback; @@ -17,6 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.sip.message.Response; +import java.util.List; import java.util.Map; @Slf4j @@ -189,6 +187,45 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService { log.error("[点播通用通道] 类型编号: {} 不支持回放暂停恢复", dataType); throw new PlayException(Response.BUSY_HERE, "channel not support"); } - playbackService.playbackPause(channel, stream); + playbackService.playbackResume(channel, stream); + } + + @Override + public void playbackSeek(CommonGBChannel channel, String stream, long seekTime) { + log.info("[通用通道] 回放拖动播放, 类型: {}, 编号:{} stream:{}", channel.getDataType(), channel.getGbDeviceId(), stream); + Integer dataType = channel.getDataType(); + ISourcePlaybackService playbackService = sourcePlaybackServiceMap.get(ChannelDataType.PLAYBACK_SERVICE + dataType); + if (playbackService == null) { + // 通道数据异常 + log.error("[点播通用通道] 类型编号: {} 不支持回放暂停恢复", dataType); + throw new PlayException(Response.BUSY_HERE, "channel not support"); + } + playbackService.playbackSeek(channel, stream, seekTime); + } + + @Override + public void playbackSpeed(CommonGBChannel channel, String stream, Double speed) { + log.info("[通用通道] 回放倍速播放, 类型: {}, 编号:{} stream:{}", channel.getDataType(), channel.getGbDeviceId(), stream); + Integer dataType = channel.getDataType(); + ISourcePlaybackService playbackService = sourcePlaybackServiceMap.get(ChannelDataType.PLAYBACK_SERVICE + dataType); + if (playbackService == null) { + // 通道数据异常 + log.error("[点播通用通道] 类型编号: {} 不支持回放暂停恢复", dataType); + throw new PlayException(Response.BUSY_HERE, "channel not support"); + } + playbackService.playbackSpeed(channel, stream, speed); + } + + @Override + public void queryRecord(CommonGBChannel channel, String startTime, String endTime, ErrorCallback> callback) { + log.info("[通用通道] 录像查询, 类型: {}, 编号:{}", channel.getDataType(), channel.getGbDeviceId()); + Integer dataType = channel.getDataType(); + ISourcePlaybackService playbackService = sourcePlaybackServiceMap.get(ChannelDataType.PLAYBACK_SERVICE + dataType); + if (playbackService == null) { + // 通道数据异常 + log.error("[点播通用通道] 类型编号: {} 不支持回放暂停恢复", dataType); + throw new PlayException(Response.BUSY_HERE, "channel not support"); + } + playbackService.queryRecord(channel, startTime, endTime, callback); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelServiceImpl.java index addb7a49e..ef3fbcf5f 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelServiceImpl.java @@ -717,25 +717,6 @@ public class GbChannelServiceImpl implements IGbChannelService { return new PageInfo<>(all); } - @Override - public void queryRecordInfo(CommonGBChannel channel, String startTime, String endTime, ErrorCallback callback) { - if (channel.getDataType() == ChannelDataType.GB28181) { - deviceChannelService.queryRecordInfo(channel, startTime, endTime, callback); - } else if (channel.getDataType() == ChannelDataType.STREAM_PROXY) { - // 拉流代理 - log.warn("[下载通用通道录像] 不支持下载拉流代理的录像: {}({})", channel.getGbName(), channel.getGbDeviceId()); - throw new PlayException(Response.FORBIDDEN, "forbidden"); - } else if (channel.getDataType() == ChannelDataType.STREAM_PUSH) { - // 推流 - log.warn("[下载通用通道录像] 不支持下载推流的录像: {}({})", channel.getGbName(), channel.getGbDeviceId()); - throw new PlayException(Response.FORBIDDEN, "forbidden"); - } else { - // 通道数据异常 - log.error("[回放通用通道] 通道数据异常,无法识别通道来源: {}({})", channel.getGbName(), channel.getGbDeviceId()); - throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); - } - } - @Override public PageInfo queryListByCivilCodeForUnusual(int page, int count, String query, Boolean online, Integer channelType) { PageHelper.startPage(page, count); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java index bb895e17b..58504e82a 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java @@ -1464,6 +1464,32 @@ public class PlayServiceImpl implements IPlayService { cmder.playResumeCmd(device, channel, inviteInfo.getStreamInfo()); } + @Override + public void playbackSeek(String streamId, long seekTime) throws InvalidArgumentException, ParseException, SipException { + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); + + if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { + log.warn("streamId不存在!"); + throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在"); + } + Device device = deviceService.getDeviceByDeviceId(inviteInfo.getDeviceId()); + DeviceChannel channel = deviceChannelService.getOneById(inviteInfo.getChannelId()); + cmder.playSeekCmd(device, channel, inviteInfo.getStreamInfo(), seekTime); + } + + @Override + public void playbackSpeed(String streamId, double speed) throws InvalidArgumentException, ParseException, SipException { + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); + + if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { + log.warn("streamId不存在!"); + throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在"); + } + Device device = deviceService.getDeviceByDeviceId(inviteInfo.getDeviceId()); + DeviceChannel channel = deviceChannelService.getOneById(inviteInfo.getChannelId()); + cmder.playSpeedCmd(device, channel, inviteInfo.getStreamInfo(), speed); + } + @Override public void startPushStream(SendRtpInfo sendRtpInfo, DeviceChannel channel, SIPResponse sipResponse, Platform platform, CallIdHeader callIdHeader) { // 开始发流 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/SourcePlaybackServiceForGbImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/SourcePlaybackServiceForGbImpl.java index a051df437..f72bcec8d 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/SourcePlaybackServiceForGbImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/SourcePlaybackServiceForGbImpl.java @@ -4,27 +4,36 @@ import com.genersoft.iot.vmp.common.InviteSessionType; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.common.enums.ChannelDataType; import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.CommonRecordInfo; import com.genersoft.iot.vmp.gb28181.bean.PlayException; +import com.genersoft.iot.vmp.gb28181.bean.RecordItem; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.gb28181.service.IPlayService; import com.genersoft.iot.vmp.gb28181.service.ISourcePlaybackService; import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.sip.message.Response; +import java.util.ArrayList; +import java.util.List; @Slf4j @Service(ChannelDataType.PLAYBACK_SERVICE + ChannelDataType.GB28181) public class SourcePlaybackServiceForGbImpl implements ISourcePlaybackService { @Autowired - private IPlayService deviceChannelPlayService; + private IPlayService playService; + + @Autowired + private IDeviceChannelService channelService; @Override public void playback(CommonGBChannel channel, Long startTime, Long stopTime, ErrorCallback callback) { try { - deviceChannelPlayService.playBack(channel, startTime, stopTime, callback); + playService.playBack(channel, startTime, stopTime, callback); } catch (PlayException e) { callback.run(e.getCode(), e.getMsg(), null); } catch (Exception e) { @@ -36,7 +45,7 @@ public class SourcePlaybackServiceForGbImpl implements ISourcePlaybackService { public void stopPlayback(CommonGBChannel channel, String stream) { // 国标通道 try { - deviceChannelPlayService.stop(InviteSessionType.PLAYBACK, channel, stream); + playService.stop(InviteSessionType.PLAYBACK, channel, stream); } catch (Exception e) { log.error("[停止点播失败] {}({})", channel.getGbName(), channel.getGbDeviceId(), e); } @@ -44,6 +53,60 @@ public class SourcePlaybackServiceForGbImpl implements ISourcePlaybackService { @Override public void playbackPause(CommonGBChannel channel, String stream) { + // 国标通道 + try { + playService.playbackPause(stream); + } catch (Exception e) { + log.error("[停止点播失败] {}({})", channel.getGbName(), channel.getGbDeviceId(), e); + } + } + @Override + public void playbackResume(CommonGBChannel channel, String stream) { + // 国标通道 + try { + playService.playbackPause(stream); + } catch (Exception e) { + log.error("[停止点播失败] {}({})", channel.getGbName(), channel.getGbDeviceId(), e); + } + } + + @Override + public void playbackSeek(CommonGBChannel channel, String stream, long seekTime) { + // 国标通道 + try { + playService.playbackPause(stream); + } catch (Exception e) { + log.error("[停止点播失败] {}({})", channel.getGbName(), channel.getGbDeviceId(), e); + } + } + + @Override + public void playbackSpeed(CommonGBChannel channel, String stream, Double speed) { + // 国标通道 + try { + playService.playbackSpeed(stream, speed); + } catch (Exception e) { + log.error("[停止点播失败] {}({})", channel.getGbName(), channel.getGbDeviceId(), e); + } + } + + @Override + public void queryRecord(CommonGBChannel channel, String startTime, String endTime, ErrorCallback> callback) { + channelService.queryRecordInfo(channel, startTime, endTime, (code, msg, data) -> { + if (code == ErrorCode.SUCCESS.getCode()) { + List recordList = data.getRecordList(); + List recordInfoList = new ArrayList<>(); + for (RecordItem recordItem : recordList) { + CommonRecordInfo recordInfo = new CommonRecordInfo(); + recordInfo.setStartTime(recordItem.getStartTime()); + recordInfo.setEndTime(recordItem.getEndTime()); + recordInfoList.add(recordInfo); + } + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), recordInfoList); + }else { + callback.run(code, msg, null); + } + }); } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcChannelPlayController.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcChannelPlayController.java index c51f4fd55..acd8e8136 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcChannelPlayController.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcChannelPlayController.java @@ -117,7 +117,7 @@ public class RedisRpcChannelPlayController extends RpcController { } try { - channelService.queryRecordInfo(channel, startTime, endTime, (code, msg, data) ->{ + channelPlayService.queryRecord(channel, startTime, endTime, (code, msg, data) ->{ if (code == InviteErrorCode.SUCCESS.getCode()) { response.setStatusCode(code); response.setBody(data); diff --git a/web/src/api/commonChannel.js b/web/src/api/commonChannel.js index c76aa22c4..0a9db6178 100644 --- a/web/src/api/commonChannel.js +++ b/web/src/api/commonChannel.js @@ -512,3 +512,77 @@ export function focus({ channelId, command, speed }) { } }) } +export function queryRecord({ channelId, startTime, endTime }) { + return request({ + method: 'get', + url: '/api/common/channel/playback/query', + params: { + channelId: channelId, + startTime: startTime, + endTime: endTime + } + }) +} +export function playback({ channelId, startTime, endTime }) { + return request({ + method: 'get', + url: '/api/common/channel/playback', + params: { + channelId: channelId, + startTime: startTime, + endTime: endTime + } + }) +} +export function stopPlayback({ channelId, stream }) { + return request({ + method: 'get', + url: '/api/common/channel/playback/stop', + params: { + channelId: channelId, + stream: stream + } + }) +} +export function pausePlayback({ channelId, stream}) { + return request({ + method: 'get', + url: '/api/common/channel/playback/pause', + params: { + channelId: channelId, + stream: stream + } + }) +} +export function resumePlayback({ channelId, stream}) { + return request({ + method: 'get', + url: '/api/common/channel/playback/resume', + params: { + channelId: channelId, + stream: stream + } + }) +} +export function seekPlayback({ channelId, stream, seekTime}) { + return request({ + method: 'get', + url: '/api/common/channel/playback/seek', + params: { + channelId: channelId, + stream: stream, + seekTime: seekTime + } + }) +} +export function speedPlayback({ channelId, stream, speed}) { + return request({ + method: 'get', + url: '/api/common/channel/playback/speed', + params: { + channelId: channelId, + stream: stream, + speed: speed + } + }) +} diff --git a/web/src/router/index.js b/web/src/router/index.js index 3a77cea7e..1a70692af 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -75,12 +75,13 @@ export const constantRoutes = [ path: '/channel', name: 'Channel', component: () => import('@/views/channel/index'), - meta: {title: '通道列表', icon: 'channelManger'} + meta: { title: '通道列表', icon: 'channelManger'} }, { path: '/channel/record/:channelId', name: 'CommonRecord', - component: () => import('@/views/channel/record') + component: () => import('@/views/channel/record'), + meta: { title: '设备录像' } } ] }, diff --git a/web/src/store/modules/commonChanel.js b/web/src/store/modules/commonChanel.js index 4d5ea9fca..32ad2357b 100644 --- a/web/src/store/modules/commonChanel.js +++ b/web/src/store/modules/commonChanel.js @@ -15,12 +15,22 @@ import { clearUnusualCivilCodeList, getIndustryList, getTypeList, - getNetworkIdentificationList, playChannel, addToRegion, deleteFromRegion, addToGroup, deleteFromGroup, getList, + getNetworkIdentificationList, + playChannel, + addToRegion, + deleteFromRegion, + addToGroup, + deleteFromGroup, + getList, addPointForCruise, - addPreset, auxiliary, + addPreset, + auxiliary, callPreset, deletePointForCruise, - deletePreset, focus, iris, ptz, + deletePreset, + focus, + iris, + ptz, queryPreset, setCruiseSpeed, setCruiseTime, @@ -30,7 +40,16 @@ import { startCruise, startScan, stopCruise, - stopScan, wiper, getAllForMap, stopPlayChannel + stopScan, + wiper, + getAllForMap, + stopPlayChannel, + queryRecord, + playback, + stopPlayback, + pausePlayback, + resumePlayback, + seekPlayback, speedPlayback } from '@/api/commonChannel' const actions = { @@ -483,6 +502,76 @@ const actions = { reject(error) }) }) + }, + queryRecord({ commit }, params) { + return new Promise((resolve, reject) => { + queryRecord(params).then(response => { + const { data } = response + resolve(data) + }).catch(error => { + reject(error) + }) + }) + }, + playback({ commit }, params) { + return new Promise((resolve, reject) => { + playback(params).then(response => { + const { data } = response + resolve(data) + }).catch(error => { + reject(error) + }) + }) + }, + stopPlayback({ commit }, params) { + return new Promise((resolve, reject) => { + stopPlayback(params).then(response => { + const { data } = response + resolve(data) + }).catch(error => { + reject(error) + }) + }) + }, + pausePlayback({ commit }, params) { + return new Promise((resolve, reject) => { + pausePlayback(params).then(response => { + const { data } = response + resolve(data) + }).catch(error => { + reject(error) + }) + }) + }, + resumePlayback({ commit }, params) { + return new Promise((resolve, reject) => { + resumePlayback(params).then(response => { + const { data } = response + resolve(data) + }).catch(error => { + reject(error) + }) + }) + }, + seekPlayback({ commit }, params) { + return new Promise((resolve, reject) => { + resumePlayback(params).then(response => { + const { data } = response + resolve(data) + }).catch(error => { + reject(error) + }) + }) + }, + speedPlayback({ commit }, params) { + return new Promise((resolve, reject) => { + speedPlayback(params).then(response => { + const { data } = response + resolve(data) + }).catch(error => { + reject(error) + }) + }) } } diff --git a/web/src/views/channel/index.vue b/web/src/views/channel/index.vue index 095df07c4..f7457be88 100755 --- a/web/src/views/channel/index.vue +++ b/web/src/views/channel/index.vue @@ -268,7 +268,7 @@ export default { }) }, queryRecords: function(itemData) { - const channelId = itemData.deviceId + const channelId = itemData.gbId this.$router.push(`/channel/record/${channelId}`) }, queryCloudRecords: function(itemData) { diff --git a/web/src/views/channel/record.vue b/web/src/views/channel/record.vue index f07b99bdd..bd01a3235 100755 --- a/web/src/views/channel/record.vue +++ b/web/src/views/channel/record.vue @@ -37,12 +37,12 @@ {{ getFileShowName(item) }} - + + + + + +
暂无数据
@@ -95,12 +95,12 @@ title="截图" @click="snap()" /> -
+ + + + + + @@ -291,7 +291,6 @@ export default { // 查询当年有视频的日期 this.chooseDate = moment().format('YYYY-MM-DD') this.dateChange() - this.getDownloadSpeedArray() window.addEventListener('beforeunload', this.stopPlayRecord) }, destroyed() { @@ -312,8 +311,6 @@ export default { startTime = this.detailFiles[0].startTime endTime = this.detailFiles[this.detailFiles.length - 1].endTime } - console.log(startTime) - console.log(endTime) this.$refs.chooseTimeRange.openDialog([new Date(startTime), new Date(endTime)], (time) => { console.log(time) const startTime = moment(time[0]).format('YYYY-MM-DD HH:mm:ss') @@ -343,7 +340,12 @@ export default { console.log(speed) // 倍速播放 this.playSpeed = speed - this.$store.dispatch('playback/setSpeed', [this.streamInfo.stream, speed]) + this.$store.dispatch('commonChanel/speedPlayback', + { + channelId: this.channelId, + stream: this.streamInfo.stream, + speed: speed + }) .then(data => { this.$refs.recordVideoPlayer.setPlaybackRate(this.playSpeed) }) @@ -394,15 +396,19 @@ export default { }, dateChange() { this.detailFiles = [] - this.$store.dispatch('gbRecord/query', [this.deviceId, this.channelId, this.startTime, this.endTime]) + this.$store.dispatch('commonChanel/queryRecord', + { + channelId: this.channelId, + startTime: this.startTime, + endTime: this.endTime + }) .then(data => { // 处理时间信息 - if (data.recordList.length === 0) { + if (data.length === 0) { return } - this.detailFiles = data.recordList + this.detailFiles = data this.initTime = new Date(this.detailFiles[0].startTime).getTime() - console.log(this.initTime) for (let i = 0; i < this.detailFiles.length; i++) { this.timeSegments.push({ beginTime: new Date(this.detailFiles[i].startTime).getTime(), @@ -418,29 +424,16 @@ export default { this.recordsLoading = false }) }, - getDownloadSpeedArray() { - this.$store.dispatch('device/queryChannelOne', { - deviceId: this.deviceId, - channelDeviceId: this.channelId - }) - .then(data => { - if (data.downloadSpeed) { - const speedArray = data.downloadSpeed.split('/') - - speedArray.forEach(item => { - if (parseInt(item) > 4) { - this.playSpeedRange.push(parseInt(item)) - } - }) - } - }) - }, stopPlayRecord(callback) { console.log('停止录像回放') if (this.streamInfo !== null) { this.$refs['recordVideoPlayer'].pause() this.videoUrl = '' - this.$store.dispatch('playback/stop', [this.deviceId, this.channelId, this.streamInfo.stream]) + this.$store.dispatch('commonChanel/stopPlayback', + { + channelId: this.channelId, + stream: this.streamInfo.stream + }) .then((data) => { this.streamInfo = null if (callback) callback() @@ -462,7 +455,12 @@ export default { }) } else { this.playerTime = 0 - this.$store.dispatch('playback/play', [this.deviceId, this.channelId, startTime, endTime]) + this.$store.dispatch('commonChanel/playback', + { + channelId: this.channelId, + startTime: startTime, + endTime: endTime + }) .then(data => { this.streamInfo = data this.videoUrl = this.getUrlByStreamInfo()