Commit f62bf7b2c6239f2c67f5d9019f8302c8d441f870
Merge branch '2.6.8' into wvp-28181-2.0
# Conflicts: # src/main/java/com/genersoft/iot/vmp/conf/redis/RedisMsgListenConfig.java # src/main/java/com/genersoft/iot/vmp/gb28181/bean/Gb28181Sdp.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java # src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java # src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java # src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java # src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
Showing
21 changed files
with
489 additions
and
122 deletions
doc/_sidebar.md
| 1 | <!-- 侧边栏 --> | 1 | <!-- 侧边栏 --> |
| 2 | 2 | ||
| 3 | * **编译与部署** | 3 | * **编译与部署** |
| 4 | + * [测试](_content/introduction/test.md) | ||
| 4 | * [编译](_content/introduction/compile.md) | 5 | * [编译](_content/introduction/compile.md) |
| 5 | * [配置](_content/introduction/config.md) | 6 | * [配置](_content/introduction/config.md) |
| 6 | * [部署](_content/introduction/deployment.md) | 7 | * [部署](_content/introduction/deployment.md) |
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
| @@ -100,6 +100,21 @@ public class VideoManagerConstants { | @@ -100,6 +100,21 @@ public class VideoManagerConstants { | ||
| 100 | */ | 100 | */ |
| 101 | public static final String VM_MSG_STREAM_PUSH_REQUESTED = "VM_MSG_STREAM_PUSH_REQUESTED"; | 101 | public static final String VM_MSG_STREAM_PUSH_REQUESTED = "VM_MSG_STREAM_PUSH_REQUESTED"; |
| 102 | 102 | ||
| 103 | + /** | ||
| 104 | + * redis 消息通知上级平台开始观看流 | ||
| 105 | + */ | ||
| 106 | + public static final String VM_MSG_STREAM_START_PLAY_NOTIFY = "VM_MSG_STREAM_START_PLAY_NOTIFY"; | ||
| 107 | + | ||
| 108 | + /** | ||
| 109 | + * redis 消息通知上级平台停止观看流 | ||
| 110 | + */ | ||
| 111 | + public static final String VM_MSG_STREAM_STOP_PLAY_NOTIFY = "VM_MSG_STREAM_STOP_PLAY_NOTIFY"; | ||
| 112 | + | ||
| 113 | + /** | ||
| 114 | + * redis 消息接收关闭一个推流 | ||
| 115 | + */ | ||
| 116 | + public static final String VM_MSG_STREAM_PUSH_CLOSE_REQUESTED = "VM_MSG_STREAM_PUSH_CLOSE_REQUESTED"; | ||
| 117 | + | ||
| 103 | 118 | ||
| 104 | /** | 119 | /** |
| 105 | * redis 消息通知平台通知设备推流结果 | 120 | * redis 消息通知平台通知设备推流结果 |
src/main/java/com/genersoft/iot/vmp/conf/redis/RedisMsgListenConfig.java
| @@ -46,6 +46,9 @@ public class RedisMsgListenConfig { | @@ -46,6 +46,9 @@ public class RedisMsgListenConfig { | ||
| 46 | @Autowired | 46 | @Autowired |
| 47 | private RedisCloseStreamMsgListener redisCloseStreamMsgListener; | 47 | private RedisCloseStreamMsgListener redisCloseStreamMsgListener; |
| 48 | 48 | ||
| 49 | + @Autowired | ||
| 50 | + private RedisPushStreamCloseResponseListener redisPushStreamCloseResponseListener; | ||
| 51 | + | ||
| 49 | 52 | ||
| 50 | /** | 53 | /** |
| 51 | * redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 | 54 | * redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 |
| @@ -67,6 +70,7 @@ public class RedisMsgListenConfig { | @@ -67,6 +70,7 @@ public class RedisMsgListenConfig { | ||
| 67 | container.addMessageListener(redisPushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE)); | 70 | container.addMessageListener(redisPushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE)); |
| 68 | container.addMessageListener(redisPushStreamResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE)); | 71 | container.addMessageListener(redisPushStreamResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE)); |
| 69 | container.addMessageListener(redisCloseStreamMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE)); | 72 | container.addMessageListener(redisCloseStreamMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE)); |
| 73 | + container.addMessageListener(redisPushStreamCloseResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE_REQUESTED)); | ||
| 70 | return container; | 74 | return container; |
| 71 | } | 75 | } |
| 72 | } | 76 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
| @@ -64,7 +64,7 @@ public class SipLayer implements CommandLineRunner { | @@ -64,7 +64,7 @@ public class SipLayer implements CommandLineRunner { | ||
| 64 | try { | 64 | try { |
| 65 | sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties(monitorIp, userSetting.getSipLog())); | 65 | sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties(monitorIp, userSetting.getSipLog())); |
| 66 | } catch (PeerUnavailableException e) { | 66 | } catch (PeerUnavailableException e) { |
| 67 | - logger.error("[Sip Server] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp); | 67 | + logger.error("[SIP SERVER] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp); |
| 68 | return; | 68 | return; |
| 69 | } | 69 | } |
| 70 | 70 | ||
| @@ -76,12 +76,12 @@ public class SipLayer implements CommandLineRunner { | @@ -76,12 +76,12 @@ public class SipLayer implements CommandLineRunner { | ||
| 76 | tcpSipProvider.addSipListener(sipProcessorObserver); | 76 | tcpSipProvider.addSipListener(sipProcessorObserver); |
| 77 | tcpSipProviderMap.put(monitorIp, tcpSipProvider); | 77 | tcpSipProviderMap.put(monitorIp, tcpSipProvider); |
| 78 | 78 | ||
| 79 | - logger.info("[Sip Server] tcp://{}:{} 启动成功", monitorIp, port); | 79 | + logger.info("[SIP SERVER] tcp://{}:{} 启动成功", monitorIp, port); |
| 80 | } catch (TransportNotSupportedException | 80 | } catch (TransportNotSupportedException |
| 81 | | TooManyListenersException | 81 | | TooManyListenersException |
| 82 | | ObjectInUseException | 82 | | ObjectInUseException |
| 83 | | InvalidArgumentException e) { | 83 | | InvalidArgumentException e) { |
| 84 | - logger.error("[Sip Server] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" | 84 | + logger.error("[SIP SERVER] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" |
| 85 | , monitorIp, port); | 85 | , monitorIp, port); |
| 86 | } | 86 | } |
| 87 | 87 | ||
| @@ -93,12 +93,12 @@ public class SipLayer implements CommandLineRunner { | @@ -93,12 +93,12 @@ public class SipLayer implements CommandLineRunner { | ||
| 93 | 93 | ||
| 94 | udpSipProviderMap.put(monitorIp, udpSipProvider); | 94 | udpSipProviderMap.put(monitorIp, udpSipProvider); |
| 95 | 95 | ||
| 96 | - logger.info("[Sip Server] udp://{}:{} 启动成功", monitorIp, port); | 96 | + logger.info("[SIP SERVER] udp://{}:{} 启动成功", monitorIp, port); |
| 97 | } catch (TransportNotSupportedException | 97 | } catch (TransportNotSupportedException |
| 98 | | TooManyListenersException | 98 | | TooManyListenersException |
| 99 | | ObjectInUseException | 99 | | ObjectInUseException |
| 100 | | InvalidArgumentException e) { | 100 | | InvalidArgumentException e) { |
| 101 | - logger.error("[Sip Server] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" | 101 | + logger.error("[SIP SERVER] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" |
| 102 | , monitorIp, port); | 102 | , monitorIp, port); |
| 103 | } | 103 | } |
| 104 | } | 104 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
| @@ -283,7 +283,7 @@ public class SIPRequestHeaderPlarformProvider { | @@ -283,7 +283,7 @@ public class SIPRequestHeaderPlarformProvider { | ||
| 283 | viaHeader.setRPort(); | 283 | viaHeader.setRPort(); |
| 284 | viaHeaders.add(viaHeader); | 284 | viaHeaders.add(viaHeader); |
| 285 | // from | 285 | // from |
| 286 | - SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getDeviceGBId(), | 286 | + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sendRtpItem.getChannelId(), |
| 287 | platform.getDeviceIp() + ":" + platform.getDevicePort()); | 287 | platform.getDeviceIp() + ":" + platform.getDevicePort()); |
| 288 | Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); | 288 | Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); |
| 289 | FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, sendRtpItem.getToTag()); | 289 | FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, sendRtpItem.getToTag()); |
| @@ -296,13 +296,10 @@ public class SIPRequestHeaderPlarformProvider { | @@ -296,13 +296,10 @@ public class SIPRequestHeaderPlarformProvider { | ||
| 296 | MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); | 296 | MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); |
| 297 | // ceq | 297 | // ceq |
| 298 | CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE); | 298 | CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE); |
| 299 | - MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory(); | ||
| 300 | - // 设置编码, 防止中文乱码 | ||
| 301 | - messageFactory.setDefaultContentEncodingCharset("gb2312"); | ||
| 302 | 299 | ||
| 303 | CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId()); | 300 | CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId()); |
| 304 | 301 | ||
| 305 | - request = (SIPRequest) messageFactory.createRequest(requestURI, Request.BYE, callIdHeader, cSeqHeader, fromHeader, | 302 | + request = (SIPRequest) SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.BYE, callIdHeader, cSeqHeader, fromHeader, |
| 306 | toHeader, viaHeaders, maxForwards); | 303 | toHeader, viaHeaders, maxForwards); |
| 307 | 304 | ||
| 308 | request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); | 305 | request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); |
| @@ -310,6 +307,7 @@ public class SIPRequestHeaderPlarformProvider { | @@ -310,6 +307,7 @@ public class SIPRequestHeaderPlarformProvider { | ||
| 310 | String sipAddress = platform.getDeviceIp() + ":" + platform.getDevicePort(); | 307 | String sipAddress = platform.getDeviceIp() + ":" + platform.getDevicePort(); |
| 311 | Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory() | 308 | Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory() |
| 312 | .createSipURI(platform.getDeviceGBId(), sipAddress)); | 309 | .createSipURI(platform.getDeviceGBId(), sipAddress)); |
| 310 | + | ||
| 313 | request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); | 311 | request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); |
| 314 | 312 | ||
| 315 | return request; | 313 | return request; |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| @@ -371,7 +371,6 @@ public class SIPCommander implements ISIPCommander { | @@ -371,7 +371,6 @@ public class SIPCommander implements ISIPCommander { | ||
| 371 | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); | 371 | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); |
| 372 | errorEvent.response(e); | 372 | errorEvent.response(e); |
| 373 | }), e -> { | 373 | }), e -> { |
| 374 | - // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 | ||
| 375 | ResponseEvent responseEvent = (ResponseEvent) e.event; | 374 | ResponseEvent responseEvent = (ResponseEvent) e.event; |
| 376 | SIPResponse response = (SIPResponse) responseEvent.getResponse(); | 375 | SIPResponse response = (SIPResponse) responseEvent.getResponse(); |
| 377 | streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, | 376 | streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
| @@ -215,6 +215,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { | @@ -215,6 +215,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { | ||
| 215 | }else { | 215 | }else { |
| 216 | if (channel.getChannelId().length() != 20) { | 216 | if (channel.getChannelId().length() != 20) { |
| 217 | catalogXml.append("</Item>\r\n"); | 217 | catalogXml.append("</Item>\r\n"); |
| 218 | + logger.warn("[编号长度异常] {} 长度错误,请使用20位长度的国标编号,当前长度:{}", channel.getChannelId(), channel.getChannelId().length()); | ||
| 219 | + catalogXml.append("</Item>\r\n"); | ||
| 218 | continue; | 220 | continue; |
| 219 | } | 221 | } |
| 220 | switch (Integer.parseInt(channel.getChannelId().substring(10, 13))){ | 222 | switch (Integer.parseInt(channel.getChannelId().substring(10, 13))){ |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
| @@ -3,6 +3,8 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; | @@ -3,6 +3,8 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; | ||
| 3 | import com.alibaba.fastjson2.JSON; | 3 | import com.alibaba.fastjson2.JSON; |
| 4 | import com.alibaba.fastjson2.JSONObject; | 4 | import com.alibaba.fastjson2.JSONObject; |
| 5 | import com.genersoft.iot.vmp.conf.DynamicTask; | 5 | import com.genersoft.iot.vmp.conf.DynamicTask; |
| 6 | +import com.genersoft.iot.vmp.conf.UserSetting; | ||
| 7 | +import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType; | ||
| 6 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | 8 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| 7 | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; | 9 | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; |
| 8 | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; | 10 | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; |
| @@ -14,6 +16,7 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; | @@ -14,6 +16,7 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; | ||
| 14 | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; | 16 | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; |
| 15 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | 17 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 16 | import com.genersoft.iot.vmp.service.IMediaServerService; | 18 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 19 | +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; | ||
| 17 | import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; | 20 | import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; |
| 18 | import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; | 21 | import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; |
| 19 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 22 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| @@ -58,6 +61,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In | @@ -58,6 +61,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In | ||
| 58 | private IRedisCatchStorage redisCatchStorage; | 61 | private IRedisCatchStorage redisCatchStorage; |
| 59 | 62 | ||
| 60 | @Autowired | 63 | @Autowired |
| 64 | + private UserSetting userSetting; | ||
| 65 | + | ||
| 66 | + @Autowired | ||
| 61 | private IVideoManagerStorage storager; | 67 | private IVideoManagerStorage storager; |
| 62 | 68 | ||
| 63 | @Autowired | 69 | @Autowired |
| @@ -155,6 +161,13 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In | @@ -155,6 +161,13 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In | ||
| 155 | } else if (jsonObject.getInteger("code") == 0) { | 161 | } else if (jsonObject.getInteger("code") == 0) { |
| 156 | logger.info("调用ZLM推流接口, 结果: {}", jsonObject); | 162 | logger.info("调用ZLM推流接口, 结果: {}", jsonObject); |
| 157 | logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); | 163 | logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); |
| 164 | + if (sendRtpItem.getPlayType() == InviteStreamType.PUSH) { | ||
| 165 | + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStreamId(), | ||
| 166 | + sendRtpItem.getChannelId(), parentPlatform.getServerGBId(), parentPlatform.getName(), userSetting.getServerId(), | ||
| 167 | + sendRtpItem.getMediaServerId()); | ||
| 168 | + messageForPushChannel.setPlatFormIndex(parentPlatform.getId()); | ||
| 169 | + redisCatchStorage.sendPlatformStartPlayMsg(messageForPushChannel); | ||
| 170 | + } | ||
| 158 | } else { | 171 | } else { |
| 159 | logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param)); | 172 | logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param)); |
| 160 | if (sendRtpItem.isOnlyAudio()) { | 173 | if (sendRtpItem.isOnlyAudio()) { |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
| @@ -2,12 +2,15 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; | @@ -2,12 +2,15 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; | ||
| 2 | 2 | ||
| 3 | import com.genersoft.iot.vmp.common.InviteInfo; | 3 | import com.genersoft.iot.vmp.common.InviteInfo; |
| 4 | import com.genersoft.iot.vmp.common.InviteSessionType; | 4 | import com.genersoft.iot.vmp.common.InviteSessionType; |
| 5 | +import com.genersoft.iot.vmp.common.StreamInfo; | ||
| 6 | +import com.genersoft.iot.vmp.conf.UserSetting; | ||
| 5 | import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; | 7 | import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; |
| 6 | import com.genersoft.iot.vmp.gb28181.bean.Device; | 8 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 7 | import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType; | 9 | import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType; |
| 8 | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; | 10 | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; |
| 9 | import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; | 11 | import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; |
| 10 | import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; | 12 | import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; |
| 13 | +import com.genersoft.iot.vmp.gb28181.bean.*; | ||
| 11 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; | 14 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 12 | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; | 15 | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; |
| 13 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; | 16 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| @@ -15,9 +18,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor | @@ -15,9 +18,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor | ||
| 15 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; | 18 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| 16 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; | 19 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| 17 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | 20 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 21 | +import com.genersoft.iot.vmp.service.IDeviceChannelService; | ||
| 18 | import com.genersoft.iot.vmp.service.IDeviceService; | 22 | import com.genersoft.iot.vmp.service.IDeviceService; |
| 19 | import com.genersoft.iot.vmp.service.IInviteStreamService; | 23 | import com.genersoft.iot.vmp.service.IInviteStreamService; |
| 20 | import com.genersoft.iot.vmp.service.IMediaServerService; | 24 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 25 | +import com.genersoft.iot.vmp.service.IPlatformService; | ||
| 21 | import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; | 26 | import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; |
| 22 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 27 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 23 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; | 28 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| @@ -32,10 +37,10 @@ import javax.sip.InvalidArgumentException; | @@ -32,10 +37,10 @@ import javax.sip.InvalidArgumentException; | ||
| 32 | import javax.sip.RequestEvent; | 37 | import javax.sip.RequestEvent; |
| 33 | import javax.sip.SipException; | 38 | import javax.sip.SipException; |
| 34 | import javax.sip.address.SipURI; | 39 | import javax.sip.address.SipURI; |
| 40 | +import javax.sip.InvalidArgumentException; | ||
| 41 | +import javax.sip.RequestEvent; | ||
| 42 | +import javax.sip.SipException; | ||
| 35 | import javax.sip.header.CallIdHeader; | 43 | import javax.sip.header.CallIdHeader; |
| 36 | -import javax.sip.header.FromHeader; | ||
| 37 | -import javax.sip.header.HeaderAddress; | ||
| 38 | -import javax.sip.header.ToHeader; | ||
| 39 | import javax.sip.message.Response; | 44 | import javax.sip.message.Response; |
| 40 | import java.text.ParseException; | 45 | import java.text.ParseException; |
| 41 | import java.util.HashMap; | 46 | import java.util.HashMap; |
| @@ -60,9 +65,15 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In | @@ -60,9 +65,15 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In | ||
| 60 | private IInviteStreamService inviteStreamService; | 65 | private IInviteStreamService inviteStreamService; |
| 61 | 66 | ||
| 62 | @Autowired | 67 | @Autowired |
| 68 | + private IPlatformService platformService; | ||
| 69 | + | ||
| 70 | + @Autowired | ||
| 63 | private IDeviceService deviceService; | 71 | private IDeviceService deviceService; |
| 64 | 72 | ||
| 65 | @Autowired | 73 | @Autowired |
| 74 | + private IDeviceChannelService channelService; | ||
| 75 | + | ||
| 76 | + @Autowired | ||
| 66 | private IVideoManagerStorage storager; | 77 | private IVideoManagerStorage storager; |
| 67 | 78 | ||
| 68 | @Autowired | 79 | @Autowired |
| @@ -80,6 +91,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In | @@ -80,6 +91,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In | ||
| 80 | @Autowired | 91 | @Autowired |
| 81 | private VideoStreamSessionManager streamSession; | 92 | private VideoStreamSessionManager streamSession; |
| 82 | 93 | ||
| 94 | + @Autowired | ||
| 95 | + private UserSetting userSetting; | ||
| 96 | + | ||
| 83 | @Override | 97 | @Override |
| 84 | public void afterPropertiesSet() throws Exception { | 98 | public void afterPropertiesSet() throws Exception { |
| 85 | // 添加消息处理的订阅 | 99 | // 添加消息处理的订阅 |
| @@ -92,93 +106,91 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In | @@ -92,93 +106,91 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In | ||
| 92 | */ | 106 | */ |
| 93 | @Override | 107 | @Override |
| 94 | public void process(RequestEvent evt) { | 108 | public void process(RequestEvent evt) { |
| 95 | - | 109 | + SIPRequest request = (SIPRequest) evt.getRequest(); |
| 96 | try { | 110 | try { |
| 97 | - responseAck((SIPRequest) evt.getRequest(), Response.OK); | 111 | + responseAck(request, Response.OK); |
| 98 | } catch (SipException | InvalidArgumentException | ParseException e) { | 112 | } catch (SipException | InvalidArgumentException | ParseException e) { |
| 99 | logger.error("[回复BYE信息失败],{}", e.getMessage()); | 113 | logger.error("[回复BYE信息失败],{}", e.getMessage()); |
| 100 | } | 114 | } |
| 101 | CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); | 115 | CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); |
| 102 | - String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); | ||
| 103 | - String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); | ||
| 104 | - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId, null, callIdHeader.getCallId()); | ||
| 105 | - logger.info("[收到bye] {}/{}", platformGbId, channelId); | ||
| 106 | - if (sendRtpItem != null){ | ||
| 107 | - String streamId = sendRtpItem.getStreamId(); | ||
| 108 | - Map<String, Object> param = new HashMap<>(); | ||
| 109 | - param.put("vhost","__defaultVhost__"); | ||
| 110 | - param.put("app",sendRtpItem.getApp()); | ||
| 111 | - param.put("stream",streamId); | ||
| 112 | - param.put("ssrc",sendRtpItem.getSsrc()); | ||
| 113 | - logger.info("[收到bye] 停止向上级推流:{}", streamId); | ||
| 114 | - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | ||
| 115 | - redisCatchStorage.deleteSendRTPServer(platformGbId, channelId, callIdHeader.getCallId(), null); | ||
| 116 | - ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc()); | ||
| 117 | - zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param); | ||
| 118 | - int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId); | ||
| 119 | - if (totalReaderCount <= 0) { | ||
| 120 | - logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); | ||
| 121 | - if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) { | ||
| 122 | - Device device = deviceService.getDevice(sendRtpItem.getDeviceId()); | ||
| 123 | - if (device == null) { | ||
| 124 | - logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId); | ||
| 125 | - } | ||
| 126 | - try { | ||
| 127 | - logger.warn("[停止点播] {}/{}", sendRtpItem.getDeviceId(), channelId); | ||
| 128 | - cmder.streamByeCmd(device, channelId, streamId, null); | ||
| 129 | - } catch (InvalidArgumentException | ParseException | SipException | | ||
| 130 | - SsrcTransactionNotFoundException e) { | ||
| 131 | - logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage()); | ||
| 132 | - } | ||
| 133 | - } | ||
| 134 | - if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) { | ||
| 135 | - MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, | ||
| 136 | - sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(), | ||
| 137 | - sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId()); | ||
| 138 | - redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); | ||
| 139 | - } | 116 | + |
| 117 | + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId()); | ||
| 118 | + | ||
| 119 | + if (sendRtpItem != null){ | ||
| 120 | + logger.info("[收到bye] 来自平台{}, 停止通道:{}", sendRtpItem.getPlatformId(), sendRtpItem.getChannelId()); | ||
| 121 | + String streamId = sendRtpItem.getStreamId(); | ||
| 122 | + Map<String, Object> param = new HashMap<>(); | ||
| 123 | + param.put("vhost","__defaultVhost__"); | ||
| 124 | + param.put("app",sendRtpItem.getApp()); | ||
| 125 | + param.put("stream",streamId); | ||
| 126 | + param.put("ssrc",sendRtpItem.getSsrc()); | ||
| 127 | + logger.info("[收到bye] 停止向上级推流:{}", streamId); | ||
| 128 | + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | ||
| 129 | + redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), | ||
| 130 | + callIdHeader.getCallId(), null); | ||
| 131 | + zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param); | ||
| 132 | + if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) { | ||
| 133 | + ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId()); | ||
| 134 | + if (platform != null) { | ||
| 135 | + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, | ||
| 136 | + sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(), | ||
| 137 | + sendRtpItem.getPlatformId(), platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId()); | ||
| 138 | + messageForPushChannel.setPlatFormIndex(platform.getId()); | ||
| 139 | + redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel); | ||
| 140 | + }else { | ||
| 141 | + logger.info("[上级平台停止观看] 未找到平台{}的信息,发送redis消息失败", sendRtpItem.getPlatformId()); | ||
| 140 | } | 142 | } |
| 141 | } | 143 | } |
| 142 | - // 可能是设备主动停止 | ||
| 143 | - Device device = storager.queryVideoDeviceByChannelId(platformGbId); | ||
| 144 | - if (device != null) { | ||
| 145 | - storager.stopPlay(device.getDeviceId(), channelId); | ||
| 146 | - SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); | ||
| 147 | - if (ssrcTransactionForPlay != null){ | ||
| 148 | - if (ssrcTransactionForPlay.getCallId().equals(callIdHeader.getCallId())){ | ||
| 149 | - // 释放ssrc | ||
| 150 | - MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlay.getMediaServerId()); | ||
| 151 | - if (mediaServerItem != null) { | ||
| 152 | - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlay.getSsrc()); | ||
| 153 | - } | ||
| 154 | - streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlay.getStream()); | ||
| 155 | - } | ||
| 156 | - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); | ||
| 157 | - inviteStreamService.removeInviteInfo(inviteInfo); | ||
| 158 | - if (inviteInfo != null) { | ||
| 159 | - if (inviteInfo.getStreamInfo() != null) { | ||
| 160 | - mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStream()); | ||
| 161 | - } | ||
| 162 | - } | ||
| 163 | - } | ||
| 164 | - SsrcTransaction ssrcTransactionForPlayBack = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, callIdHeader.getCallId(), null); | ||
| 165 | - if (ssrcTransactionForPlayBack != null) { | ||
| 166 | - // 释放ssrc | ||
| 167 | - MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlayBack.getMediaServerId()); | ||
| 168 | - if (mediaServerItem != null) { | ||
| 169 | - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlayBack.getSsrc()); | 144 | + |
| 145 | + int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId); | ||
| 146 | + if (totalReaderCount <= 0) { | ||
| 147 | + logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); | ||
| 148 | + if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) { | ||
| 149 | + Device device = deviceService.getDevice(sendRtpItem.getDeviceId()); | ||
| 150 | + if (device == null) { | ||
| 151 | + logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId); | ||
| 170 | } | 152 | } |
| 171 | - streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlayBack.getStream()); | ||
| 172 | - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAYBACK, device.getDeviceId(), channelId); | ||
| 173 | - | ||
| 174 | - if (inviteInfo != null) { | ||
| 175 | - inviteStreamService.removeInviteInfo(inviteInfo); | ||
| 176 | - if (inviteInfo.getStreamInfo() != null) { | ||
| 177 | - mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStream()); | ||
| 178 | - } | 153 | + try { |
| 154 | + logger.warn("[停止点播] {}/{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); | ||
| 155 | + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), streamId, null); | ||
| 156 | + } catch (InvalidArgumentException | ParseException | SipException | | ||
| 157 | + SsrcTransactionNotFoundException e) { | ||
| 158 | + logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage()); | ||
| 179 | } | 159 | } |
| 180 | } | 160 | } |
| 181 | } | 161 | } |
| 162 | + }else { | ||
| 163 | + // 可能是设备发送的停止 | ||
| 164 | + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); | ||
| 165 | + if (ssrcTransaction == null) { | ||
| 166 | + logger.info("[收到bye] 但是无法获取推流信息和发流信息,忽略此请求"); | ||
| 167 | + logger.info(request.toString()); | ||
| 168 | + return; | ||
| 169 | + } | ||
| 170 | + logger.info("[收到bye] 来自设备:{}, 通道已停止推流: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); | ||
| 182 | 171 | ||
| 172 | + Device device = deviceService.getDevice(ssrcTransaction.getDeviceId()); | ||
| 173 | + if (device == null) { | ||
| 174 | + logger.info("[收到bye] 未找到设备:{} ", ssrcTransaction.getDeviceId()); | ||
| 175 | + return; | ||
| 176 | + } | ||
| 177 | + DeviceChannel channel = channelService.getOne(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); | ||
| 178 | + if (channel == null) { | ||
| 179 | + logger.info("[收到bye] 未找到通道,设备:{}, 通道:{}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); | ||
| 180 | + return; | ||
| 181 | + } | ||
| 182 | + storager.stopPlay(device.getDeviceId(), channel.getChannelId()); | ||
| 183 | + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channel.getChannelId()); | ||
| 184 | + if (streamInfo != null) { | ||
| 185 | + redisCatchStorage.stopPlay(streamInfo); | ||
| 186 | + mediaServerService.closeRTPServer(streamInfo.getMediaServerId(), streamInfo.getStream()); | ||
| 187 | + } | ||
| 188 | + // 释放ssrc | ||
| 189 | + MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId()); | ||
| 190 | + if (mediaServerItem != null) { | ||
| 191 | + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc()); | ||
| 192 | + } | ||
| 193 | + streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.getStream()); | ||
| 194 | + } | ||
| 183 | } | 195 | } |
| 184 | } | 196 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
| @@ -181,16 +181,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -181,16 +181,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 181 | return; | 181 | return; |
| 182 | } else { | 182 | } else { |
| 183 | streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream()); | 183 | streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream()); |
| 184 | - if (streamPushItem == null || streamPushItem.getServerId().equals(userSetting.getServerId())) { | ||
| 185 | - logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId); | ||
| 186 | - try { | ||
| 187 | - responseAck(request, Response.GONE); | ||
| 188 | - } catch (SipException | InvalidArgumentException | ParseException e) { | ||
| 189 | - logger.error("[命令发送失败] invite GONE: {}", e.getMessage()); | ||
| 190 | - } | ||
| 191 | - return; | ||
| 192 | - }else { | ||
| 193 | - // TODO 可能漏回复消息 | 184 | + if (streamPushItem != null) { |
| 185 | + mediaServerItem = mediaServerService.getOne(streamPushItem.getMediaServerId()); | ||
| 186 | + } | ||
| 187 | + if (mediaServerItem == null) { | ||
| 188 | + mediaServerItem = mediaServerService.getDefaultMediaServer(); | ||
| 194 | } | 189 | } |
| 195 | } | 190 | } |
| 196 | } else { | 191 | } else { |
| @@ -351,7 +346,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -351,7 +346,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 351 | } | 346 | } |
| 352 | logger.info("[上级Invite] {}, 平台:{}, 通道:{}, 收流地址:{}:{},收流方式:{}, ssrc:{}", sessionName, username, channelId, addressStr, port, streamTypeStr, ssrc); | 347 | logger.info("[上级Invite] {}, 平台:{}, 通道:{}, 收流地址:{}:{},收流方式:{}, ssrc:{}", sessionName, username, channelId, addressStr, port, streamTypeStr, ssrc); |
| 353 | SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, | 348 | SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, |
| 354 | - device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp()); | 349 | + device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback -> { |
| 350 | + return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null; | ||
| 351 | + }); | ||
| 355 | 352 | ||
| 356 | if (tcpActive != null) { | 353 | if (tcpActive != null) { |
| 357 | sendRtpItem.setTcpActive(tcpActive); | 354 | sendRtpItem.setTcpActive(tcpActive); |
| @@ -557,7 +554,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -557,7 +554,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 557 | if (streamReady != null && streamReady) { | 554 | if (streamReady != null && streamReady) { |
| 558 | // 自平台内容 | 555 | // 自平台内容 |
| 559 | SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, | 556 | SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, |
| 560 | - gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); | 557 | + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback ->{ |
| 558 | + return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null; | ||
| 559 | + }); | ||
| 561 | 560 | ||
| 562 | if (sendRtpItem == null) { | 561 | if (sendRtpItem == null) { |
| 563 | logger.warn("服务器端口资源不足"); | 562 | logger.warn("服务器端口资源不足"); |
| @@ -596,7 +595,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -596,7 +595,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 596 | if (streamReady != null && streamReady) { | 595 | if (streamReady != null && streamReady) { |
| 597 | // 自平台内容 | 596 | // 自平台内容 |
| 598 | SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, | 597 | SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, |
| 599 | - gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); | 598 | + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback ->{ |
| 599 | + return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null; | ||
| 600 | + }); | ||
| 600 | 601 | ||
| 601 | if (sendRtpItem == null) { | 602 | if (sendRtpItem == null) { |
| 602 | logger.warn("服务器端口资源不足"); | 603 | logger.warn("服务器端口资源不足"); |
| @@ -712,7 +713,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -712,7 +713,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 712 | dynamicTask.stop(callIdHeader.getCallId()); | 713 | dynamicTask.stop(callIdHeader.getCallId()); |
| 713 | if (serverId.equals(userSetting.getServerId())) { | 714 | if (serverId.equals(userSetting.getServerId())) { |
| 714 | SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId, | 715 | SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId, |
| 715 | - app, stream, channelId, mediaTransmissionTCP, platform.isRtcp()); | 716 | + app, stream, channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback -> { |
| 717 | + return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null; | ||
| 718 | + }); | ||
| 716 | 719 | ||
| 717 | if (sendRtpItem == null) { | 720 | if (sendRtpItem == null) { |
| 718 | logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足"); | 721 | logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足"); |
src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java
| @@ -125,7 +125,7 @@ public class SipUtils { | @@ -125,7 +125,7 @@ public class SipUtils { | ||
| 125 | strTmp = String.format("%02X", moveSpeed); | 125 | strTmp = String.format("%02X", moveSpeed); |
| 126 | builder.append(strTmp, 0, 2); | 126 | builder.append(strTmp, 0, 2); |
| 127 | builder.append(strTmp, 0, 2); | 127 | builder.append(strTmp, 0, 2); |
| 128 | - | 128 | + |
| 129 | //优化zoom低倍速下的变倍速率 | 129 | //优化zoom低倍速下的变倍速率 |
| 130 | if ((zoomSpeed > 0) && (zoomSpeed <16)) | 130 | if ((zoomSpeed > 0) && (zoomSpeed <16)) |
| 131 | { | 131 | { |
| @@ -263,4 +263,4 @@ public class SipUtils { | @@ -263,4 +263,4 @@ public class SipUtils { | ||
| 263 | } | 263 | } |
| 264 | return localDateTime.format(DateUtil.formatterISO8601); | 264 | return localDateTime.format(DateUtil.formatterISO8601); |
| 265 | } | 265 | } |
| 266 | -} | 266 | -} |
| 267 | +} | ||
| 267 | \ No newline at end of file | 268 | \ No newline at end of file |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
| @@ -22,6 +22,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; | @@ -22,6 +22,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; | ||
| 22 | import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; | 22 | import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; |
| 23 | import com.genersoft.iot.vmp.media.zlm.dto.hook.*; | 23 | import com.genersoft.iot.vmp.media.zlm.dto.hook.*; |
| 24 | import com.genersoft.iot.vmp.service.*; | 24 | import com.genersoft.iot.vmp.service.*; |
| 25 | +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; | ||
| 25 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 26 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 26 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; | 27 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| 27 | import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; | 28 | import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; |
| @@ -468,6 +469,13 @@ public class ZLMHttpHookListener { | @@ -468,6 +469,13 @@ public class ZLMHttpHookListener { | ||
| 468 | } | 469 | } |
| 469 | redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(), | 470 | redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(), |
| 470 | sendRtpItem.getCallId(), sendRtpItem.getStreamId()); | 471 | sendRtpItem.getCallId(), sendRtpItem.getStreamId()); |
| 472 | + if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) { | ||
| 473 | + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, | ||
| 474 | + sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(), | ||
| 475 | + sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId()); | ||
| 476 | + messageForPushChannel.setPlatFormIndex(parentPlatform.getId()); | ||
| 477 | + redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel); | ||
| 478 | + } | ||
| 471 | } | 479 | } |
| 472 | } | 480 | } |
| 473 | } | 481 | } |
| @@ -513,7 +521,7 @@ public class ZLMHttpHookListener { | @@ -513,7 +521,7 @@ public class ZLMHttpHookListener { | ||
| 513 | } | 521 | } |
| 514 | return ret; | 522 | return ret; |
| 515 | } | 523 | } |
| 516 | - // 推流具有主动性,暂时不做处理 | 524 | + // TODO 推流具有主动性,暂时不做处理 |
| 517 | // StreamPushItem streamPushItem = streamPushService.getPush(app, streamId); | 525 | // StreamPushItem streamPushItem = streamPushService.getPush(app, streamId); |
| 518 | // if (streamPushItem != null) { | 526 | // if (streamPushItem != null) { |
| 519 | // // TODO 发送停止 | 527 | // // TODO 发送停止 |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
| @@ -221,13 +221,14 @@ public class ZLMRTPServerFactory { | @@ -221,13 +221,14 @@ public class ZLMRTPServerFactory { | ||
| 221 | * @param tcp 是否为tcp | 221 | * @param tcp 是否为tcp |
| 222 | * @return SendRtpItem | 222 | * @return SendRtpItem |
| 223 | */ | 223 | */ |
| 224 | - public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp, boolean rtcp){ | 224 | + public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, |
| 225 | + String deviceId, String channelId, boolean tcp, boolean rtcp, KeepPortCallback callback){ | ||
| 225 | 226 | ||
| 226 | // 默认为随机端口 | 227 | // 默认为随机端口 |
| 227 | int localPort = 0; | 228 | int localPort = 0; |
| 228 | if (userSetting.getGbSendStreamStrict()) { | 229 | if (userSetting.getGbSendStreamStrict()) { |
| 229 | if (userSetting.getGbSendStreamStrict()) { | 230 | if (userSetting.getGbSendStreamStrict()) { |
| 230 | - localPort = keepPort(serverItem, ssrc, localPort); | 231 | + localPort = keepPort(serverItem, ssrc, localPort, callback); |
| 231 | if (localPort == 0) { | 232 | if (localPort == 0) { |
| 232 | return null; | 233 | return null; |
| 233 | } | 234 | } |
| @@ -259,11 +260,12 @@ public class ZLMRTPServerFactory { | @@ -259,11 +260,12 @@ public class ZLMRTPServerFactory { | ||
| 259 | * @param tcp 是否为tcp | 260 | * @param tcp 是否为tcp |
| 260 | * @return SendRtpItem | 261 | * @return SendRtpItem |
| 261 | */ | 262 | */ |
| 262 | - public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp, boolean rtcp){ | 263 | + public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, |
| 264 | + String app, String stream, String channelId, boolean tcp, boolean rtcp, KeepPortCallback callback){ | ||
| 263 | // 默认为随机端口 | 265 | // 默认为随机端口 |
| 264 | int localPort = 0; | 266 | int localPort = 0; |
| 265 | if (userSetting.getGbSendStreamStrict()) { | 267 | if (userSetting.getGbSendStreamStrict()) { |
| 266 | - localPort = keepPort(serverItem, ssrc, localPort); | 268 | + localPort = keepPort(serverItem, ssrc, localPort, callback); |
| 267 | if (localPort == 0) { | 269 | if (localPort == 0) { |
| 268 | return null; | 270 | return null; |
| 269 | } | 271 | } |
| @@ -284,10 +286,14 @@ public class ZLMRTPServerFactory { | @@ -284,10 +286,14 @@ public class ZLMRTPServerFactory { | ||
| 284 | return sendRtpItem; | 286 | return sendRtpItem; |
| 285 | } | 287 | } |
| 286 | 288 | ||
| 289 | + public interface KeepPortCallback{ | ||
| 290 | + Boolean keep(String ssrc); | ||
| 291 | + } | ||
| 292 | + | ||
| 287 | /** | 293 | /** |
| 288 | * 保持端口,直到需要需要发流时再释放 | 294 | * 保持端口,直到需要需要发流时再释放 |
| 289 | */ | 295 | */ |
| 290 | - public int keepPort(MediaServerItem serverItem, String ssrc, Integer localPort) { | 296 | + public int keepPort(MediaServerItem serverItem, String ssrc, int localPort, KeepPortCallback keepPortCallback) { |
| 291 | Map<String, Object> param = new HashMap<>(3); | 297 | Map<String, Object> param = new HashMap<>(3); |
| 292 | param.put("port", localPort); | 298 | param.put("port", localPort); |
| 293 | param.put("enable_tcp", 1); | 299 | param.put("enable_tcp", 1); |
| @@ -296,18 +302,20 @@ public class ZLMRTPServerFactory { | @@ -296,18 +302,20 @@ public class ZLMRTPServerFactory { | ||
| 296 | if (jsonObject.getInteger("code") == 0) { | 302 | if (jsonObject.getInteger("code") == 0) { |
| 297 | localPort = jsonObject.getInteger("port"); | 303 | localPort = jsonObject.getInteger("port"); |
| 298 | HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId()); | 304 | HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId()); |
| 305 | + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 | ||
| 299 | Integer finalLocalPort = localPort; | 306 | Integer finalLocalPort = localPort; |
| 300 | hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout, | 307 | hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout, |
| 301 | (MediaServerItem mediaServerItem, HookParam hookParam)->{ | 308 | (MediaServerItem mediaServerItem, HookParam hookParam)->{ |
| 302 | logger.info("[上级点播] {}->监听端口到期继续保持监听: {}", ssrc, finalLocalPort); | 309 | logger.info("[上级点播] {}->监听端口到期继续保持监听: {}", ssrc, finalLocalPort); |
| 303 | OnRtpServerTimeoutHookParam rtpServerTimeoutHookParam = (OnRtpServerTimeoutHookParam) hookParam; | 310 | OnRtpServerTimeoutHookParam rtpServerTimeoutHookParam = (OnRtpServerTimeoutHookParam) hookParam; |
| 304 | - if (!ssrc.equals(rtpServerTimeoutHookParam.getSsrc())) { | ||
| 305 | - return; | ||
| 306 | - } | ||
| 307 | - int port = keepPort(serverItem, ssrc, finalLocalPort); | ||
| 308 | - if (port == 0) { | ||
| 309 | - logger.info("[上级点播] {}->监听端口失败,移除监听", ssrc); | ||
| 310 | - hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout); | 311 | + if (ssrc.equals(rtpServerTimeoutHookParam.getSsrc())) { |
| 312 | + if (keepPortCallback.keep(ssrc)) { | ||
| 313 | + logger.info("[上级点播] {}->监听端口到期继续保持监听", ssrc); | ||
| 314 | + keepPort(serverItem, ssrc, finalLocalPort, keepPortCallback); | ||
| 315 | + }else { | ||
| 316 | + logger.info("[上级点播] {}->发送取消,无需继续监听", ssrc); | ||
| 317 | + releasePort(serverItem, ssrc); | ||
| 318 | + } | ||
| 311 | } | 319 | } |
| 312 | }); | 320 | }); |
| 313 | logger.info("[上级点播] {}->监听端口: {}", ssrc, localPort); | 321 | logger.info("[上级点播] {}->监听端口: {}", ssrc, localPort); |
src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java
| 1 | package com.genersoft.iot.vmp.service.bean; | 1 | package com.genersoft.iot.vmp.service.bean; |
| 2 | 2 | ||
| 3 | -import java.util.stream.Stream; | ||
| 4 | - | ||
| 5 | /** | 3 | /** |
| 6 | * 当上级平台 | 4 | * 当上级平台 |
| 7 | * @author lin | 5 | * @author lin |
| @@ -29,11 +27,16 @@ public class MessageForPushChannel { | @@ -29,11 +27,16 @@ public class MessageForPushChannel { | ||
| 29 | private String gbId; | 27 | private String gbId; |
| 30 | 28 | ||
| 31 | /** | 29 | /** |
| 32 | - * 请求的平台ID | 30 | + * 请求的平台国标编号 |
| 33 | */ | 31 | */ |
| 34 | private String platFormId; | 32 | private String platFormId; |
| 35 | 33 | ||
| 36 | /** | 34 | /** |
| 35 | + * 请求的平台自增ID | ||
| 36 | + */ | ||
| 37 | + private int platFormIndex; | ||
| 38 | + | ||
| 39 | + /** | ||
| 37 | * 请求平台名称 | 40 | * 请求平台名称 |
| 38 | */ | 41 | */ |
| 39 | private String platFormName; | 42 | private String platFormName; |
| @@ -128,4 +131,12 @@ public class MessageForPushChannel { | @@ -128,4 +131,12 @@ public class MessageForPushChannel { | ||
| 128 | public void setMediaServerId(String mediaServerId) { | 131 | public void setMediaServerId(String mediaServerId) { |
| 129 | this.mediaServerId = mediaServerId; | 132 | this.mediaServerId = mediaServerId; |
| 130 | } | 133 | } |
| 134 | + | ||
| 135 | + public int getPlatFormIndex() { | ||
| 136 | + return platFormIndex; | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + public void setPlatFormIndex(int platFormIndex) { | ||
| 140 | + this.platFormIndex = platFormIndex; | ||
| 141 | + } | ||
| 131 | } | 142 | } |
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
| @@ -105,6 +105,7 @@ public class PlatformServiceImpl implements IPlatformService { | @@ -105,6 +105,7 @@ public class PlatformServiceImpl implements IPlatformService { | ||
| 105 | // 行政区划默认去编号的前6位 | 105 | // 行政区划默认去编号的前6位 |
| 106 | parentPlatform.setAdministrativeDivision(parentPlatform.getServerGBId().substring(0,6)); | 106 | parentPlatform.setAdministrativeDivision(parentPlatform.getServerGBId().substring(0,6)); |
| 107 | } | 107 | } |
| 108 | + parentPlatform.setTreeType("CivilCode"); | ||
| 108 | parentPlatform.setCatalogId(parentPlatform.getDeviceGBId()); | 109 | parentPlatform.setCatalogId(parentPlatform.getDeviceGBId()); |
| 109 | int result = platformMapper.addParentPlatform(parentPlatform); | 110 | int result = platformMapper.addParentPlatform(parentPlatform); |
| 110 | // 添加缓存 | 111 | // 添加缓存 |
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
| @@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; | @@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; | ||
| 13 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | 13 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 14 | import com.genersoft.iot.vmp.service.IMediaServerService; | 14 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 15 | import com.genersoft.iot.vmp.service.bean.*; | 15 | import com.genersoft.iot.vmp.service.bean.*; |
| 16 | +import com.genersoft.iot.vmp.utils.redis.RedisUtil; | ||
| 16 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | 17 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| 17 | import org.slf4j.Logger; | 18 | import org.slf4j.Logger; |
| 18 | import org.slf4j.LoggerFactory; | 19 | import org.slf4j.LoggerFactory; |
| @@ -26,6 +27,7 @@ import org.springframework.stereotype.Component; | @@ -26,6 +27,7 @@ import org.springframework.stereotype.Component; | ||
| 26 | 27 | ||
| 27 | import java.text.ParseException; | 28 | import java.text.ParseException; |
| 28 | import java.util.HashMap; | 29 | import java.util.HashMap; |
| 30 | +import java.util.List; | ||
| 29 | import java.util.Map; | 31 | import java.util.Map; |
| 30 | import java.util.UUID; | 32 | import java.util.UUID; |
| 31 | import java.util.concurrent.ConcurrentHashMap; | 33 | import java.util.concurrent.ConcurrentHashMap; |
| @@ -127,6 +129,7 @@ public class RedisGbPlayMsgListener implements MessageListener { | @@ -127,6 +129,7 @@ public class RedisGbPlayMsgListener implements MessageListener { | ||
| 127 | case WvpRedisMsgCmd.REQUEST_PUSH_STREAM: | 129 | case WvpRedisMsgCmd.REQUEST_PUSH_STREAM: |
| 128 | RequestPushStreamMsg param = JSON.to(RequestPushStreamMsg.class, wvpRedisMsg.getContent()); | 130 | RequestPushStreamMsg param = JSON.to(RequestPushStreamMsg.class, wvpRedisMsg.getContent()); |
| 129 | requestPushStreamMsgHand(param, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial()); | 131 | requestPushStreamMsgHand(param, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial()); |
| 132 | + | ||
| 130 | break; | 133 | break; |
| 131 | default: | 134 | default: |
| 132 | break; | 135 | break; |
| @@ -311,7 +314,9 @@ public class RedisGbPlayMsgListener implements MessageListener { | @@ -311,7 +314,9 @@ public class RedisGbPlayMsgListener implements MessageListener { | ||
| 311 | SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(), | 314 | SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(), |
| 312 | content.getPort(), content.getSsrc(), content.getPlatformId(), | 315 | content.getPort(), content.getSsrc(), content.getPlatformId(), |
| 313 | content.getApp(), content.getStream(), content.getChannelId(), | 316 | content.getApp(), content.getStream(), content.getChannelId(), |
| 314 | - content.getTcp(), content.getRtcp()); | 317 | + content.getTcp(), content.getRtcp(), ssrcFromCallback -> { |
| 318 | + return querySendRTPServer(content.getPlatformId(), content.getChannelId(), content.getStream(), null) != null; | ||
| 319 | + }); | ||
| 315 | 320 | ||
| 316 | WVPResult<ResponseSendItemMsg> result = new WVPResult<>(); | 321 | WVPResult<ResponseSendItemMsg> result = new WVPResult<>(); |
| 317 | result.setCode(0); | 322 | result.setCode(0); |
| @@ -388,4 +393,31 @@ public class RedisGbPlayMsgListener implements MessageListener { | @@ -388,4 +393,31 @@ public class RedisGbPlayMsgListener implements MessageListener { | ||
| 388 | }); | 393 | }); |
| 389 | redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); | 394 | redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); |
| 390 | } | 395 | } |
| 396 | + | ||
| 397 | + private SendRtpItem querySendRTPServer(String platformGbId, String channelId, String streamId, String callId) { | ||
| 398 | + if (platformGbId == null) { | ||
| 399 | + platformGbId = "*"; | ||
| 400 | + } | ||
| 401 | + if (channelId == null) { | ||
| 402 | + channelId = "*"; | ||
| 403 | + } | ||
| 404 | + if (streamId == null) { | ||
| 405 | + streamId = "*"; | ||
| 406 | + } | ||
| 407 | + if (callId == null) { | ||
| 408 | + callId = "*"; | ||
| 409 | + } | ||
| 410 | + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX | ||
| 411 | + + userSetting.getServerId() + "_*_" | ||
| 412 | + + platformGbId + "_" | ||
| 413 | + + channelId + "_" | ||
| 414 | + + streamId + "_" | ||
| 415 | + + callId; | ||
| 416 | + List<Object> scan = RedisUtil.scan(redisTemplate, key); | ||
| 417 | + if (scan.size() > 0) { | ||
| 418 | + return (SendRtpItem)redisTemplate.opsForValue().get(scan.get(0)); | ||
| 419 | + }else { | ||
| 420 | + return null; | ||
| 421 | + } | ||
| 422 | + } | ||
| 391 | } | 423 | } |
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamCloseResponseListener.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.service.redisMsg; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson2.JSON; | ||
| 4 | +import com.genersoft.iot.vmp.conf.UserSetting; | ||
| 5 | +import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType; | ||
| 6 | +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | ||
| 7 | +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; | ||
| 8 | +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; | ||
| 9 | +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; | ||
| 10 | +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | ||
| 11 | +import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; | ||
| 12 | +import com.genersoft.iot.vmp.service.IMediaServerService; | ||
| 13 | +import com.genersoft.iot.vmp.service.IStreamPushService; | ||
| 14 | +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; | ||
| 15 | +import com.genersoft.iot.vmp.service.bean.MessageForPushChannelResponse; | ||
| 16 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | ||
| 17 | +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; | ||
| 18 | +import org.slf4j.Logger; | ||
| 19 | +import org.slf4j.LoggerFactory; | ||
| 20 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 21 | +import org.springframework.data.redis.connection.Message; | ||
| 22 | +import org.springframework.data.redis.connection.MessageListener; | ||
| 23 | +import org.springframework.stereotype.Component; | ||
| 24 | + | ||
| 25 | +import javax.sip.InvalidArgumentException; | ||
| 26 | +import javax.sip.SipException; | ||
| 27 | +import java.text.ParseException; | ||
| 28 | +import java.util.HashMap; | ||
| 29 | +import java.util.List; | ||
| 30 | +import java.util.Map; | ||
| 31 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 32 | + | ||
| 33 | +/** | ||
| 34 | + * 接收redis发送的结束推流请求 | ||
| 35 | + * @author lin | ||
| 36 | + */ | ||
| 37 | +@Component | ||
| 38 | +public class RedisPushStreamCloseResponseListener implements MessageListener { | ||
| 39 | + | ||
| 40 | + private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamCloseResponseListener.class); | ||
| 41 | + | ||
| 42 | + @Autowired | ||
| 43 | + private IStreamPushService streamPushService; | ||
| 44 | + | ||
| 45 | + @Autowired | ||
| 46 | + private IRedisCatchStorage redisCatchStorage; | ||
| 47 | + | ||
| 48 | + @Autowired | ||
| 49 | + private IVideoManagerStorage storager; | ||
| 50 | + | ||
| 51 | + @Autowired | ||
| 52 | + private ISIPCommanderForPlatform commanderFroPlatform; | ||
| 53 | + | ||
| 54 | + @Autowired | ||
| 55 | + private UserSetting userSetting; | ||
| 56 | + | ||
| 57 | + @Autowired | ||
| 58 | + private IMediaServerService mediaServerService; | ||
| 59 | + | ||
| 60 | + @Autowired | ||
| 61 | + private ZLMRTPServerFactory zlmrtpServerFactory; | ||
| 62 | + | ||
| 63 | + | ||
| 64 | + private Map<String, PushStreamResponseEvent> responseEvents = new ConcurrentHashMap<>(); | ||
| 65 | + | ||
| 66 | + public interface PushStreamResponseEvent{ | ||
| 67 | + void run(MessageForPushChannelResponse response); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Override | ||
| 71 | + public void onMessage(Message message, byte[] bytes) { | ||
| 72 | + logger.info("[REDIS消息-推流结束]: {}", new String(message.getBody())); | ||
| 73 | + MessageForPushChannel pushChannel = JSON.parseObject(message.getBody(), MessageForPushChannel.class); | ||
| 74 | + StreamPushItem push = streamPushService.getPush(pushChannel.getApp(), pushChannel.getStream()); | ||
| 75 | + if (push != null) { | ||
| 76 | + if (redisCatchStorage.isChannelSendingRTP(push.getGbId())) { | ||
| 77 | + List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChnnelId( | ||
| 78 | + push.getGbId()); | ||
| 79 | + if (sendRtpItems.size() > 0) { | ||
| 80 | + for (SendRtpItem sendRtpItem : sendRtpItems) { | ||
| 81 | + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); | ||
| 82 | + // 停止向上级推流 | ||
| 83 | + String streamId = sendRtpItem.getStreamId(); | ||
| 84 | + Map<String, Object> param = new HashMap<>(); | ||
| 85 | + param.put("vhost","__defaultVhost__"); | ||
| 86 | + param.put("app",sendRtpItem.getApp()); | ||
| 87 | + param.put("stream",streamId); | ||
| 88 | + param.put("ssrc",sendRtpItem.getSsrc()); | ||
| 89 | + logger.info("[REDIS消息-推流结束] 停止向上级推流:{}", streamId); | ||
| 90 | + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | ||
| 91 | + redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), sendRtpItem.getCallId(), sendRtpItem.getStreamId()); | ||
| 92 | + zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param); | ||
| 93 | + | ||
| 94 | + try { | ||
| 95 | + commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem); | ||
| 96 | + } catch (SipException | InvalidArgumentException | ParseException e) { | ||
| 97 | + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); | ||
| 98 | + } | ||
| 99 | + if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) { | ||
| 100 | + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, | ||
| 101 | + sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(), | ||
| 102 | + sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId()); | ||
| 103 | + messageForPushChannel.setPlatFormIndex(parentPlatform.getId()); | ||
| 104 | + redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel); | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | + } | ||
| 108 | + } | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + public void addEvent(String app, String stream, PushStreamResponseEvent callback) { | ||
| 114 | + responseEvents.put(app + stream, callback); | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + public void removeEvent(String app, String stream) { | ||
| 118 | + responseEvents.remove(app + stream); | ||
| 119 | + } | ||
| 120 | +} |
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
| @@ -202,5 +202,10 @@ public interface IRedisCatchStorage { | @@ -202,5 +202,10 @@ public interface IRedisCatchStorage { | ||
| 202 | void removeAllDevice(); | 202 | void removeAllDevice(); |
| 203 | 203 | ||
| 204 | void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online); | 204 | void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online); |
| 205 | + | ||
| 205 | void sendChannelAddOrDelete(String deviceId, String channelId, boolean add); | 206 | void sendChannelAddOrDelete(String deviceId, String channelId, boolean add); |
| 207 | + | ||
| 208 | + void sendPlatformStartPlayMsg(MessageForPushChannel messageForPushChannel); | ||
| 209 | + | ||
| 210 | + void sendPlatformStopPlayMsg(MessageForPushChannel messageForPushChannel); | ||
| 206 | } | 211 | } |
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
| @@ -622,4 +622,18 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { | @@ -622,4 +622,18 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { | ||
| 622 | // 使用 RedisTemplate<Object, Object> 发送字符串消息会导致发送的消息多带了双引号 | 622 | // 使用 RedisTemplate<Object, Object> 发送字符串消息会导致发送的消息多带了双引号 |
| 623 | stringRedisTemplate.convertAndSend(key, msg.toString()); | 623 | stringRedisTemplate.convertAndSend(key, msg.toString()); |
| 624 | } | 624 | } |
| 625 | + | ||
| 626 | + @Override | ||
| 627 | + public void sendPlatformStartPlayMsg(MessageForPushChannel msg) { | ||
| 628 | + String key = VideoManagerConstants.VM_MSG_STREAM_START_PLAY_NOTIFY; | ||
| 629 | + logger.info("[redis发送通知] 推流被上级平台观看 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId()); | ||
| 630 | + redisTemplate.convertAndSend(key, JSON.toJSON(msg)); | ||
| 631 | + } | ||
| 632 | + | ||
| 633 | + @Override | ||
| 634 | + public void sendPlatformStopPlayMsg(MessageForPushChannel msg) { | ||
| 635 | + String key = VideoManagerConstants.VM_MSG_STREAM_STOP_PLAY_NOTIFY; | ||
| 636 | + logger.info("[redis发送通知] 上级平台停止观看 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId()); | ||
| 637 | + redisTemplate.convertAndSend(key, JSON.toJSON(msg)); | ||
| 638 | + } | ||
| 625 | } | 639 | } |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java
| @@ -97,6 +97,9 @@ public class PtzController { | @@ -97,6 +97,9 @@ public class PtzController { | ||
| 97 | cmdCode = 32; | 97 | cmdCode = 32; |
| 98 | break; | 98 | break; |
| 99 | case "stop": | 99 | case "stop": |
| 100 | + horizonSpeed = 0; | ||
| 101 | + verticalSpeed = 0; | ||
| 102 | + zoomSpeed = 0; | ||
| 100 | break; | 103 | break; |
| 101 | default: | 104 | default: |
| 102 | break; | 105 | break; |
src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.vmanager.rtp; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.conf.SipConfig; | ||
| 4 | +import com.genersoft.iot.vmp.conf.UserSetting; | ||
| 5 | +import com.genersoft.iot.vmp.conf.VersionInfo; | ||
| 6 | +import com.genersoft.iot.vmp.conf.exception.ControllerException; | ||
| 7 | +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; | ||
| 8 | +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; | ||
| 9 | +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | ||
| 10 | +import com.genersoft.iot.vmp.service.*; | ||
| 11 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | ||
| 12 | +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; | ||
| 13 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 14 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 15 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 16 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 17 | +import org.springframework.beans.factory.annotation.Value; | ||
| 18 | +import org.springframework.web.bind.annotation.GetMapping; | ||
| 19 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
| 20 | +import org.springframework.web.bind.annotation.ResponseBody; | ||
| 21 | +import org.springframework.web.bind.annotation.RestController; | ||
| 22 | + | ||
| 23 | +@SuppressWarnings("rawtypes") | ||
| 24 | +@Tag(name = "第三方服务对接") | ||
| 25 | + | ||
| 26 | +@RestController | ||
| 27 | +@RequestMapping("/api/rtp") | ||
| 28 | +public class RtpController { | ||
| 29 | + | ||
| 30 | + @Autowired | ||
| 31 | + private ZlmHttpHookSubscribe zlmHttpHookSubscribe; | ||
| 32 | + | ||
| 33 | + @Autowired | ||
| 34 | + private IMediaServerService mediaServerService; | ||
| 35 | + | ||
| 36 | + @Autowired | ||
| 37 | + private VersionInfo versionInfo; | ||
| 38 | + | ||
| 39 | + @Autowired | ||
| 40 | + private SipConfig sipConfig; | ||
| 41 | + | ||
| 42 | + @Autowired | ||
| 43 | + private UserSetting userSetting; | ||
| 44 | + | ||
| 45 | + @Autowired | ||
| 46 | + private IDeviceService deviceService; | ||
| 47 | + | ||
| 48 | + @Autowired | ||
| 49 | + private IDeviceChannelService channelService; | ||
| 50 | + | ||
| 51 | + @Autowired | ||
| 52 | + private IStreamPushService pushService; | ||
| 53 | + | ||
| 54 | + | ||
| 55 | + @Autowired | ||
| 56 | + private IStreamProxyService proxyService; | ||
| 57 | + | ||
| 58 | + | ||
| 59 | + @Value("${server.port}") | ||
| 60 | + private int serverPort; | ||
| 61 | + | ||
| 62 | + | ||
| 63 | + @Autowired | ||
| 64 | + private IRedisCatchStorage redisCatchStorage; | ||
| 65 | + | ||
| 66 | + | ||
| 67 | + @GetMapping(value = "/receive/open") | ||
| 68 | + @ResponseBody | ||
| 69 | + @Operation(summary = "开启收流和获取发流信息") | ||
| 70 | + @Parameter(name = "isSend", description = "是否发送,false时只开启收流, true同时返回推流信息", required = true) | ||
| 71 | + @Parameter(name = "callId", description = "整个过程的唯一标识,为了与后续接口关联", required = true) | ||
| 72 | + @Parameter(name = "ssrc", description = "来源流的SSRC,不传则不校验来源ssrc", required = false) | ||
| 73 | + @Parameter(name = "stream", description = "形成的流的ID", required = true) | ||
| 74 | + @Parameter(name = "tcpMode", description = "收流模式, 0为UDP, 1为TCP被动", required = true) | ||
| 75 | + @Parameter(name = "callBack", description = "回调地址,如果收流超时会通道回调通知,回调为get请求,参数为callId", required = true) | ||
| 76 | + public SendRtpItem openRtpServer(Boolean isSend, String ssrc, String callId, String stream, Integer tcpMode, String callBack) { | ||
| 77 | + MediaServerItem mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(null); | ||
| 78 | + if (mediaServerItem == null) { | ||
| 79 | + throw new ControllerException(ErrorCode.ERROR100.getCode(),"没有可用的MediaServer"); | ||
| 80 | + } | ||
| 81 | + return null; | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + @GetMapping(value = "/receive/close") | ||
| 85 | + @ResponseBody | ||
| 86 | + @Operation(summary = "关闭收流") | ||
| 87 | + @Parameter(name = "stream", description = "流的ID", required = true) | ||
| 88 | + public void closeRtpServer(String stream) { | ||
| 89 | + | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + @GetMapping(value = "/send/start") | ||
| 93 | + @ResponseBody | ||
| 94 | + @Operation(summary = "发送流") | ||
| 95 | + @Parameter(name = "ssrc", description = "发送流的SSRC", required = true) | ||
| 96 | + @Parameter(name = "ip", description = "目标IP", required = true) | ||
| 97 | + @Parameter(name = "port", description = "目标端口", required = true) | ||
| 98 | + @Parameter(name = "app", description = "待发送应用名", required = true) | ||
| 99 | + @Parameter(name = "stream", description = "待发送流Id", required = true) | ||
| 100 | + @Parameter(name = "callId", description = "整个过程的唯一标识,不传则使用随机端口发流", required = true) | ||
| 101 | + @Parameter(name = "onlyAudio", description = "是否只有音频", required = true) | ||
| 102 | + @Parameter(name = "streamType", description = "流类型,1为es流,2为ps流, 默认es流", required = false) | ||
| 103 | + public void sendRTP(String ssrc, String ip, Integer port, String app, String stream, String callId, Boolean onlyAudio, Integer streamType) { | ||
| 104 | + | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + | ||
| 108 | + | ||
| 109 | + @GetMapping(value = "/send/stop") | ||
| 110 | + @ResponseBody | ||
| 111 | + @Operation(summary = "关闭发送流") | ||
| 112 | + @Parameter(name = "callId", description = "整个过程的唯一标识,不传则使用随机端口发流", required = true) | ||
| 113 | + public void closeSendRTP(String callId) { | ||
| 114 | + | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | +} |