diff --git a/README.md b/README.md index bbe44d2d..445dfda5 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git - [X] 支持电子地图,支持接入WGS84和GCJ02两种坐标系,并且自动转化为合适的坐标系进行展示和分发 - [X] 接入设备 - [X] 视频预览 + - [X] 支持主码流子码流切换 - [X] 无限制接入路数,能接入多少设备只取决于你的服务器性能 - [X] 云台控制,控制设备转向,拉近,拉远 - [X] 预置位查询,使用与设置 diff --git a/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java b/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java index dabdb4f0..dbe9e090 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java @@ -1,7 +1,6 @@ package com.genersoft.iot.vmp.common; import com.genersoft.iot.vmp.service.bean.SSRCInfo; -import io.swagger.v3.oas.annotations.media.Schema; /** * 记录每次发送invite消息的状态 @@ -125,20 +124,4 @@ public class InviteInfo { this.streamMode = streamMode; } - - /*=========================设备主子码流逻辑START====================*/ - @Schema(description = "是否为子码流(true-是,false-主码流)") - private boolean subStream; - - public boolean isSubStream() { - return subStream; - } - - public void setSubStream(boolean subStream) { - this.subStream = subStream; - } - - - - } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java index 34d624f4..c0e49131 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java @@ -453,19 +453,4 @@ public class Device { this.sipTransactionInfo = sipTransactionInfo; } - /*======================设备主子码流逻辑START=========================*/ - @Schema(description = "开启主子码流切换的开关(false-不开启,true-开启)") - private boolean switchPrimarySubStream; - - public boolean isSwitchPrimarySubStream() { - return switchPrimarySubStream; - } - - public void setSwitchPrimarySubStream(boolean switchPrimarySubStream) { - this.switchPrimarySubStream = switchPrimarySubStream; - } - - /*======================设备主子码流逻辑END=========================*/ - - } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java index f7ef12d3..d58abcb3 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java @@ -246,6 +246,10 @@ public class DeviceChannel { @Schema(description = "GPS的更新时间") private String gpsTime; + @Schema(description = "码流标识,优先级高于设备中码流标识," + + "用于选择码流时组成码流标识。默认为null,不设置。可选值: stream/streamnumber/streamprofile/streamMode") + private String streamIdentification; + public int getId() { return id; } @@ -574,4 +578,12 @@ public class DeviceChannel { public void setGpsTime(String gpsTime) { this.gpsTime = gpsTime; } + + public String getStreamIdentification() { + return streamIdentification; + } + + public void setStreamIdentification(String streamIdentification) { + this.streamIdentification = streamIdentification; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbSteamIdentification.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbSteamIdentification.java new file mode 100644 index 00000000..63c17a80 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbSteamIdentification.java @@ -0,0 +1,44 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +/** + * 码流索引标识 + */ +public enum GbSteamIdentification { + /** + * 主码流 stream:0 + * 子码流 stream:1s + */ + streamMain("stream", new String[]{"0","1"}), + /** + * 国标28181-2022定义的方式 + * 主码流 streamnumber:0 + * 子码流 streamnumber:1 + */ + streamnumber("streamnumber", new String[]{"0","1"}), + /** + * 主码流 streamprofile:0 + * 子码流 streamprofile:1 + */ + streamprofile("streamprofile", new String[]{"0","1"}), + /** + * 适用的品牌: TP-LINK + */ + streamMode("streamMode", new String[]{"main","sub"}), + ; + + GbSteamIdentification(String value, String[] indexArray) { + this.value = value; + this.indexArray = indexArray; + } + + private String value; + private String[] indexArray; + + public String getValue() { + return value; + } + + public String[] getIndexArray() { + return indexArray; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java index a169a317..13c4256b 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java @@ -4,6 +4,7 @@ import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; @@ -96,9 +97,9 @@ public interface ISIPCommander { /** * 请求预览视频流 * @param device 视频设备 - * @param channelId 预览通道 + * @param channel 预览通道 */ - void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 请求回放视频流 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index ea108bbb..8e938d82 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.SipLayer; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; @@ -264,12 +265,12 @@ public class SIPCommander implements ISIPCommander { * 请求预览视频流 * * @param device 视频设备 - * @param channelId 预览通道 + * @param channel 预览通道 * @param event hook订阅 * @param errorEvent sip错误订阅 */ @Override - public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { String stream = ssrcInfo.getStream(); @@ -293,7 +294,7 @@ public class SIPCommander implements ISIPCommander { } StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); - content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n"); + content.append("o=" + channel.getChannelId() + " 0 0 IN IP4 " + sdpIp + "\r\n"); content.append("s=Play\r\n"); content.append("c=IN IP4 " + sdpIp + "\r\n"); content.append("t=0 0\r\n"); @@ -344,20 +345,8 @@ public class SIPCommander implements ISIPCommander { } } - if( device.isSwitchPrimarySubStream() ){ - if("TP-LINK".equals(device.getManufacturer())){ - if (device.isSwitchPrimarySubStream()){ - content.append("a=streamMode:sub\r\n"); - }else { - content.append("a=streamMode:main\r\n"); - } - }else { - if (device.isSwitchPrimarySubStream()){ - content.append("a=streamprofile:1\r\n"); - }else { - content.append("a=streamprofile:0\r\n"); - } - } + if (!ObjectUtils.isEmpty(channel.getStreamIdentification())) { + content.append("a=" + channel.getStreamIdentification() + "\r\n"); } content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc @@ -366,16 +355,16 @@ public class SIPCommander implements ISIPCommander { - Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + Request request = headerProvider.createInviteRequest(device, channel.getChannelId(), content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, (e -> { - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); errorEvent.response(e); }), e -> { ResponseEvent responseEvent = (ResponseEvent) e.event; SIPResponse response = (SIPResponse) responseEvent.getResponse(); String callId = response.getCallIdHeader().getCallId(); - streamSession.put(device.getDeviceId(), channelId, callId, stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, + streamSession.put(device.getDeviceId(), channel.getChannelId(), callId, stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY); okEvent.response(e); }); diff --git a/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java b/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java index cd402a10..5a208415 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java @@ -87,4 +87,9 @@ public interface IDeviceChannelService { * 直接批量添加 */ void batchAddChannel(List deviceChannels); + + /** + * 修改通道的码流类型 + */ + void updateChannelStreamIdentification(DeviceChannel channel); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java index ada75fb5..4eb1138b 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.service; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.exception.ServiceException; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.service.bean.ErrorCallback; import com.genersoft.iot.vmp.service.bean.SSRCInfo; @@ -16,7 +17,7 @@ import java.text.ParseException; */ public interface IPlayService { - void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channelId, ErrorCallback callback); SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback callback); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java index 43cdf427..55fa5e9a 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java @@ -18,6 +18,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; @@ -267,5 +268,16 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService { } } - + @Override + public void updateChannelStreamIdentification(DeviceChannel channel) { + assert !ObjectUtils.isEmpty(channel.getDeviceId()); + assert !ObjectUtils.isEmpty(channel.getStreamIdentification()); + if (ObjectUtils.isEmpty(channel.getStreamIdentification())) { + logger.info("[重置通道码流类型] 设备: {}, 码流: {}", channel.getDeviceId(), channel.getStreamIdentification()); + }else { + logger.info("[更新通道码流类型] 设备: {}, 通道:{}, 码流: {}", channel.getDeviceId(), channel.getChannelId(), + channel.getStreamIdentification()); + } + channelMapper.updateChannelStreamIdentification(channel); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java index 8665e058..ee2eeff8 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java @@ -5,7 +5,6 @@ import com.genersoft.iot.vmp.common.CommonCallback; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; @@ -139,10 +138,6 @@ public class DeviceServiceImpl implements IDeviceService { } sync(device); }else { - - if (deviceInDb != null) { - device.setSwitchPrimarySubStream(deviceInDb.isSwitchPrimarySubStream()); - } if(!device.isOnLine()){ device.setOnLine(true); device.setCreateTime(now); @@ -486,21 +481,6 @@ public class DeviceServiceImpl implements IDeviceService { logger.warn("更新设备时未找到设备信息"); return; } - if(deviceInStore.isSwitchPrimarySubStream() != device.isSwitchPrimarySubStream()){ - //当修改设备的主子码流开关时,需要校验是否存在流,如果存在流则直接关闭 - List ssrcTransactionForAll = streamSession.getSsrcTransactionForAll(device.getDeviceId(), null, null, null); - if(ssrcTransactionForAll != null){ - for (SsrcTransaction ssrcTransaction: ssrcTransactionForAll) { - try { - cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), ssrcTransaction.getStream(), null, null); - } catch (InvalidArgumentException | SsrcTransactionNotFoundException | ParseException | SipException e) { - throw new RuntimeException(e); - } - } - } - deviceChannelMapper.clearPlay(device.getDeviceId()); - inviteStreamService.clearInviteInfo(device.getDeviceId()); - } if (!ObjectUtils.isEmpty(device.getName())) { deviceInStore.setName(device.getName()); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java index 6d405ec2..24379249 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java @@ -15,21 +15,26 @@ import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory; import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; -import com.genersoft.iot.vmp.media.zlm.dto.*; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRecordMp4; +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.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.*; -import com.genersoft.iot.vmp.service.bean.*; +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.service.bean.SSRCInfo; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper; import com.genersoft.iot.vmp.utils.CloudRecordUtils; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; @@ -63,7 +68,7 @@ public class PlayServiceImpl implements IPlayService { private IVideoManagerStorage storager; @Autowired - private SIPCommander cmder; + private ISIPCommander cmder; @Autowired private SIPCommanderFroPlatform sipCommanderFroPlatform; @@ -95,15 +100,15 @@ public class PlayServiceImpl implements IPlayService { @Autowired private IDeviceService deviceService; + @Autowired + private IDeviceChannelService channelService; + @Autowired private UserSetting userSetting; @Autowired private DynamicTask dynamicTask; - @Autowired - private CloudRecordServiceMapper cloudRecordServiceMapper; - @Override public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback callback) { @@ -117,6 +122,11 @@ public class PlayServiceImpl implements IPlayService { logger.warn("[点播] 单端口收流时不支持TCP主动方式收流 deviceId: {},channelId:{}", deviceId, channelId); throw new ControllerException(ErrorCode.ERROR100.getCode(), "单端口收流时不支持TCP主动方式收流"); } + DeviceChannel channel = channelService.getOne(deviceId, channelId); + if (channel == null) { + logger.warn("[点播] 未找到通道 deviceId: {},channelId:{}", deviceId, channelId); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到通道"); + } InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); if (inviteInfo != null ) { if (inviteInfo.getStreamInfo() == null) { @@ -165,13 +175,13 @@ public class PlayServiceImpl implements IPlayService { null); return null; } - play(mediaServerItem, ssrcInfo, device, channelId, callback); + play(mediaServerItem, ssrcInfo, device, channel, callback); return ssrcInfo; } @Override - public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, ErrorCallback callback) { if (mediaServerItem == null || ssrcInfo == null) { @@ -180,110 +190,109 @@ public class PlayServiceImpl implements IPlayService { null); return; } - logger.info("[点播开始] deviceId: {}, channelId: {},码流类型:{}, 收流端口: {}, STREAM:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", - device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流", ssrcInfo.getPort(), ssrcInfo.getStream(), + logger.info("[点播开始] deviceId: {}, channelId: {},码流类型:{}, 收流端口: {}, 码流:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", + device.getDeviceId(), channel.getChannelId(), channel.getStreamIdentification(), ssrcInfo.getPort(), ssrcInfo.getStream(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); //端口获取失败的ssrcInfo 没有必要发送点播指令 if (ssrcInfo.getPort() <= 0) { - logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo); + logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channel.getChannelId(), ssrcInfo); // 释放ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId(), null, InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); return; } // 初始化redis中的invite消息状态 - InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, + InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream(), ssrcInfo, mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, InviteSessionStatus.ready); - inviteInfo.setSubStream(device.isSwitchPrimarySubStream()); inviteStreamService.updateInviteInfo(inviteInfo); // 超时处理 String timeOutTaskKey = UUID.randomUUID().toString(); dynamicTask.startDelay(timeOutTaskKey, () -> { // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况 - InviteInfo inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + InviteInfo inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId()); if (inviteInfoForTimeOut == null || inviteInfoForTimeOut.getStreamInfo() == null) { - logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流类型:{},端口:{}, SSRC: {}", - device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流", + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流:{},端口:{}, SSRC: {}", + device.getDeviceId(), channel.getChannelId(), channel.getStreamIdentification(), ssrcInfo.getPort(), ssrcInfo.getSsrc()); callback.run(InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId(), null, InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId()); try { - cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); + cmder.streamByeCmd(device, channel.getChannelId(), ssrcInfo.getStream(), null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { logger.error("[点播超时], 发送BYE失败 {}", e.getMessage()); } finally { mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); // 取消订阅消息监听 HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); subscribe.removeSubscribe(hookSubscribe); } }else { - logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流类型:{},端口:{}, SSRC: {}", - device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流", + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流:{},端口:{}, SSRC: {}", + device.getDeviceId(), channel.getChannelId(), channel.getStreamIdentification(), ssrcInfo.getPort(), ssrcInfo.getSsrc()); mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); mediaServerService.closeRTPServer(mediaServerItem.getId(), ssrcInfo.getStream()); - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); } }, userSetting.getPlayTimeout()); try { - cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInuse, hookParam ) -> { + cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channel, (mediaServerItemInuse, hookParam ) -> { logger.info("收到订阅消息: " + hookParam); dynamicTask.stop(timeOutTaskKey); // hook响应 - StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInuse, hookParam, device.getDeviceId(), channelId); + StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInuse, hookParam, device.getDeviceId(), channel.getChannelId()); if (streamInfo == null){ callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId(), null, InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); return; } callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId(), null, InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); - logger.info("[点播成功] deviceId: {}, channelId:{}, 码流类型:{}", device.getDeviceId(), channelId, - device.isSwitchPrimarySubStream() ? "辅码流" : "主码流"); - snapOnPlay(mediaServerItemInuse, device.getDeviceId(), channelId, ssrcInfo.getStream()); + logger.info("[点播成功] deviceId: {}, channelId:{}, 码流类型:{}", device.getDeviceId(), channel.getChannelId(), + channel.getStreamIdentification()); + snapOnPlay(mediaServerItemInuse, device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); }, (eventResult) -> { // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题 - InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId, + InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channel.getChannelId(), timeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAY); }, (event) -> { - logger.info("[点播失败] deviceId: {}, channelId:{}, {}: {}", device.getDeviceId(), channelId, event.statusCode, event.msg); + logger.info("[点播失败] deviceId: {}, channelId:{}, {}: {}", device.getDeviceId(), channel.getChannelId(), event.statusCode, event.msg); dynamicTask.stop(timeOutTaskKey); mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); // 释放ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_ERROR.getCode(), String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId(), null, InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId()); }); } catch (InvalidArgumentException | SipException | ParseException e) { @@ -293,15 +302,15 @@ public class PlayServiceImpl implements IPlayService { // 释放ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); callback.run(InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId(), null, InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId()); } } @@ -438,7 +447,7 @@ public class PlayServiceImpl implements IPlayService { @Override public void playBack(String deviceId, String channelId, String startTime, - String endTime, ErrorCallback callback) { + String endTime, ErrorCallback callback) { Device device = storager.queryVideoDevice(deviceId); if (device == null) { logger.warn("[录像回放] 未找到设备 deviceId: {},channelId:{}", deviceId, channelId); @@ -463,8 +472,8 @@ public class PlayServiceImpl implements IPlayService { @Override public void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, - String deviceId, String channelId, String startTime, - String endTime, ErrorCallback callback) { + String deviceId, String channelId, String startTime, + String endTime, ErrorCallback callback) { if (mediaServerItem == null || ssrcInfo == null) { callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), @@ -881,7 +890,7 @@ public class PlayServiceImpl implements IPlayService { cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), ssrcTransaction.getStream(), null); } catch (InvalidArgumentException | ParseException | SipException | - SsrcTransactionNotFoundException e) { + SsrcTransactionNotFoundException e) { logger.error("[zlm离线]为正在使用此zlm的设备, 发送BYE失败 {}", e.getMessage()); } } @@ -1031,16 +1040,16 @@ public class PlayServiceImpl implements IPlayService { MediaServerItem newMediaServerItem = getNewMediaServerItem(device); play(newMediaServerItem, deviceId, channelId, null, (code, msg, data)->{ - if (code == InviteErrorCode.SUCCESS.getCode()) { - InviteInfo inviteInfoForPlay = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); - if (inviteInfoForPlay != null && inviteInfoForPlay.getStreamInfo() != null) { - getSnap(deviceId, channelId, fileName, errorCallback); - }else { - errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); - } - }else { - errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); - } + if (code == InviteErrorCode.SUCCESS.getCode()) { + InviteInfo inviteInfoForPlay = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + if (inviteInfoForPlay != null && inviteInfoForPlay.getStreamInfo() != null) { + getSnap(deviceId, channelId, fileName, errorCallback); + }else { + errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); + } + }else { + errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); + } }); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java index 07597086..e823c6e0 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java @@ -20,11 +20,11 @@ public interface DeviceChannelMapper { @Insert("INSERT INTO wvp_device_channel (channel_id, device_id, name, manufacture, model, owner, civil_code, block, " + "address, parental, parent_id, safety_way, register_way, cert_num, certifiable, err_code, secrecy, " + "ip_address, port, password, ptz_type, status, stream_id, longitude, latitude, longitude_gcj02, latitude_gcj02, " + - "longitude_wgs84, latitude_wgs84, has_audio, create_time, update_time, business_group_id, gps_time) " + + "longitude_wgs84, latitude_wgs84, has_audio, create_time, update_time, business_group_id, gps_time, stream_identification) " + "VALUES (#{channelId}, #{deviceId}, #{name}, #{manufacture}, #{model}, #{owner}, #{civilCode}, #{block}," + "#{address}, #{parental}, #{parentId}, #{safetyWay}, #{registerWay}, #{certNum}, #{certifiable}, #{errCode}, #{secrecy}, " + "#{ipAddress}, #{port}, #{password}, #{PTZType}, #{status}, #{streamId}, #{longitude}, #{latitude}, #{longitudeGcj02}, " + - "#{latitudeGcj02}, #{longitudeWgs84}, #{latitudeWgs84}, #{hasAudio}, #{createTime}, #{updateTime}, #{businessGroupId}, #{gpsTime})") + "#{latitudeGcj02}, #{longitudeWgs84}, #{latitudeWgs84}, #{hasAudio}, #{createTime}, #{updateTime}, #{businessGroupId}, #{gpsTime}, #{streamIdentification})") int add(DeviceChannel channel); @Update(value = {" "}) int update(DeviceChannel channel); @@ -102,6 +103,7 @@ public interface DeviceChannelMapper { "dc.longitude_wgs84, " + "dc.latitude_wgs84, " + "dc.business_group_id, " + + "dc.stream_identification, " + "dc.gps_time " + "from " + "wvp_device_channel dc " + @@ -241,7 +243,7 @@ public interface DeviceChannelMapper { "(channel_id, device_id, name, manufacture, model, owner, civil_code, block, sub_count, " + " address, parental, parent_id, safety_way, register_way, cert_num, certifiable, err_code, secrecy, " + " ip_address,port,password,ptz_type,status,stream_id,longitude,latitude,longitude_gcj02,latitude_gcj02,"+ - " longitude_wgs84,latitude_wgs84,has_audio,create_time,update_time,business_group_id,gps_time)"+ + " longitude_wgs84,latitude_wgs84,has_audio,create_time,update_time,business_group_id,gps_time,stream_identification)"+ "values " + " " + "(#{item.channelId}, #{item.deviceId}, #{item.name}, #{item.manufacture}, #{item.model}, " + @@ -251,7 +253,7 @@ public interface DeviceChannelMapper { "#{item.ipAddress}, #{item.port}, #{item.password}, #{item.PTZType}, #{item.status}, " + "#{item.streamId}, #{item.longitude}, #{item.latitude},#{item.longitudeGcj02}, " + "#{item.latitudeGcj02},#{item.longitudeWgs84}, #{item.latitudeWgs84}, #{item.hasAudio}, now(), now(), " + - "#{item.businessGroupId}, #{item.gpsTime}) " + + "#{item.businessGroupId}, #{item.gpsTime}, #{item.streamIdentification}) " + " " + "") int batchAdd(@Param("addChannels") List addChannels); @@ -349,6 +351,7 @@ public interface DeviceChannelMapper { ", latitude_wgs84=#{item.latitudeWgs84}" + ", business_group_id=#{item.businessGroupId}" + ", gps_time=#{item.gpsTime}" + + ", stream_identification=#{item.streamIdentification}" + "WHERE id=#{item.id}" + "WHERE device_id=#{item.deviceId} AND channel_id=#{item.channelId}" + "" + @@ -542,4 +545,9 @@ public interface DeviceChannelMapper { " "}) List getSubChannelsByDeviceId(@Param("deviceId") String deviceId, @Param("parentId") String parentId, @Param("onlyCatalog") boolean onlyCatalog); + @Update("") + void updateChannelStreamIdentification(DeviceChannel channel); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java index 30ffd2bb..c6b4852a 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java @@ -43,7 +43,6 @@ public interface DeviceMapper { "geo_coord_sys," + "on_line," + "media_server_id," + - "switch_primary_sub_stream," + "(SELECT count(0) FROM wvp_device_channel WHERE device_id=wvp_device.device_id) as channel_count "+ " FROM wvp_device WHERE device_id = #{deviceId}") Device getDeviceByDeviceId(String deviceId); @@ -159,7 +158,6 @@ public interface DeviceMapper { "geo_coord_sys,"+ "on_line,"+ "media_server_id,"+ - "switch_primary_sub_stream switchPrimarySubStream,"+ "(SELECT count(0) FROM wvp_device_channel WHERE device_id=de.device_id) as channel_count " + "FROM wvp_device de" + " where on_line=${onLine}"+ @@ -249,7 +247,6 @@ public interface DeviceMapper { ", ssrc_check=#{ssrcCheck}" + ", as_message_channel=#{asMessageChannel}" + ", geo_coord_sys=#{geoCoordSys}" + - ", switch_primary_sub_stream=#{switchPrimarySubStream}" + ", media_server_id=#{mediaServerId}" + "WHERE device_id=#{deviceId}"+ " "}) @@ -267,8 +264,7 @@ public interface DeviceMapper { "as_message_channel,"+ "geo_coord_sys,"+ "on_line,"+ - "media_server_id,"+ - "switch_primary_sub_stream"+ + "media_server_id"+ ") VALUES (" + "#{deviceId}," + "#{name}," + @@ -281,8 +277,7 @@ public interface DeviceMapper { "#{asMessageChannel}," + "#{geoCoordSys}," + "#{onLine}," + - "#{mediaServerId}," + - "#{switchPrimarySubStream}" + + "#{mediaServerId}" + ")") void addCustomDevice(Device device); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java index 852f1c43..9dd4c3d8 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java @@ -264,6 +264,14 @@ public class DeviceQuery { deviceChannelService.updateChannel(deviceId, channel); } + @Operation(summary = "修改通道的码流类型", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channel", description = "通道信息", required = true) + @PostMapping("/channel/stream/identification/update/") + public void updateChannelStreamIdentification(DeviceChannel channel){ + deviceChannelService.updateChannelStreamIdentification(channel); + } + /** * 修改数据流传输模式 * @param deviceId 设备id diff --git a/web_src/src/components/channelList.vue b/web_src/src/components/channelList.vue index 5547913b..8af42692 100755 --- a/web_src/src/components/channelList.vue +++ b/web_src/src/components/channelList.vue @@ -13,24 +13,30 @@ prefix-icon="el-icon-search" v-model="searchSrt" clearable> 通道类型: - 在线状态: - - 清晰度: - - - + 码流类型重置: + + + + + + + + + @@ -46,11 +52,11 @@ - + - + - + - + - + - + - + - + - + - + + + + - -