Commit c014a90cc6a294dfc2aac740be87e75f44193a29

Authored by 648540858
2 parents 9bd58895 e8b76617

Merge branch 'wvp-28181-2.0' into main-dev

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.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/media/zlm/ZLMHttpHookListener.java
Showing 23 changed files with 497 additions and 237 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
@@ -101,6 +101,21 @@ public class VideoManagerConstants { @@ -101,6 +101,21 @@ public class VideoManagerConstants {
101 */ 101 */
102 public static final String VM_MSG_STREAM_PUSH_REQUESTED = "VM_MSG_STREAM_PUSH_REQUESTED"; 102 public static final String VM_MSG_STREAM_PUSH_REQUESTED = "VM_MSG_STREAM_PUSH_REQUESTED";
103 103
  104 + /**
  105 + * redis 消息通知上级平台开始观看流
  106 + */
  107 + public static final String VM_MSG_STREAM_START_PLAY_NOTIFY = "VM_MSG_STREAM_START_PLAY_NOTIFY";
  108 +
  109 + /**
  110 + * redis 消息通知上级平台停止观看流
  111 + */
  112 + public static final String VM_MSG_STREAM_STOP_PLAY_NOTIFY = "VM_MSG_STREAM_STOP_PLAY_NOTIFY";
  113 +
  114 + /**
  115 + * redis 消息接收关闭一个推流
  116 + */
  117 + public static final String VM_MSG_STREAM_PUSH_CLOSE_REQUESTED = "VM_MSG_STREAM_PUSH_CLOSE_REQUESTED";
  118 +
104 119
105 /** 120 /**
106 * redis 消息通知平台通知设备推流结果 121 * 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
@@ -284,7 +284,7 @@ public class SIPRequestHeaderPlarformProvider { @@ -284,7 +284,7 @@ public class SIPRequestHeaderPlarformProvider {
284 viaHeader.setRPort(); 284 viaHeader.setRPort();
285 viaHeaders.add(viaHeader); 285 viaHeaders.add(viaHeader);
286 // from 286 // from
287 - SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getDeviceGBId(), 287 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sendRtpItem.getChannelId(),
288 platform.getDeviceIp() + ":" + platform.getDevicePort()); 288 platform.getDeviceIp() + ":" + platform.getDevicePort());
289 Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); 289 Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
290 FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, sendRtpItem.getToTag()); 290 FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, sendRtpItem.getToTag());
@@ -297,13 +297,10 @@ public class SIPRequestHeaderPlarformProvider { @@ -297,13 +297,10 @@ public class SIPRequestHeaderPlarformProvider {
297 MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); 297 MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
298 // ceq 298 // ceq
299 CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE); 299 CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
300 - MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory();  
301 - // 设置编码, 防止中文乱码  
302 - messageFactory.setDefaultContentEncodingCharset("gb2312");  
303 300
304 CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId()); 301 CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId());
305 302
306 - request = (SIPRequest) messageFactory.createRequest(requestURI, Request.BYE, callIdHeader, cSeqHeader, fromHeader, 303 + request = (SIPRequest) SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.BYE, callIdHeader, cSeqHeader, fromHeader,
307 toHeader, viaHeaders, maxForwards); 304 toHeader, viaHeaders, maxForwards);
308 305
309 request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); 306 request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
@@ -311,6 +308,7 @@ public class SIPRequestHeaderPlarformProvider { @@ -311,6 +308,7 @@ public class SIPRequestHeaderPlarformProvider {
311 String sipAddress = platform.getDeviceIp() + ":" + platform.getDevicePort(); 308 String sipAddress = platform.getDeviceIp() + ":" + platform.getDevicePort();
312 Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory() 309 Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory()
313 .createSipURI(platform.getDeviceGBId(), sipAddress)); 310 .createSipURI(platform.getDeviceGBId(), sipAddress));
  311 +
314 request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); 312 request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
315 313
316 return request; 314 return request;
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -378,7 +378,6 @@ public class SIPCommander implements ISIPCommander { @@ -378,7 +378,6 @@ public class SIPCommander implements ISIPCommander {
378 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); 378 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
379 errorEvent.response(e); 379 errorEvent.response(e);
380 }), e -> { 380 }), e -> {
381 - // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值  
382 ResponseEvent responseEvent = (ResponseEvent) e.event; 381 ResponseEvent responseEvent = (ResponseEvent) e.event;
383 SIPResponse response = (SIPResponse) responseEvent.getResponse(); 382 SIPResponse response = (SIPResponse) responseEvent.getResponse();
384 streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, 383 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
@@ -237,6 +237,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -237,6 +237,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
237 }else { 237 }else {
238 if (channel.getChannelId().length() != 20) { 238 if (channel.getChannelId().length() != 20) {
239 catalogXml.append("</Item>\r\n"); 239 catalogXml.append("</Item>\r\n");
  240 + logger.warn("[编号长度异常] {} 长度错误,请使用20位长度的国标编号,当前长度:{}", channel.getChannelId(), channel.getChannelId().length());
  241 + catalogXml.append("</Item>\r\n");
240 continue; 242 continue;
241 } 243 }
242 switch (Integer.parseInt(channel.getChannelId().substring(10, 13))){ 244 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.JSONObject; 3 import com.alibaba.fastjson2.JSONObject;
4 import com.genersoft.iot.vmp.conf.DynamicTask; 4 import com.genersoft.iot.vmp.conf.DynamicTask;
5 import com.genersoft.iot.vmp.conf.UserSetting; 5 import com.genersoft.iot.vmp.conf.UserSetting;
  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.dto.HookSubscribeFactory; @@ -14,6 +16,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
14 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRtpServerTimeout; 16 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRtpServerTimeout;
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.IPlayService; 20 import com.genersoft.iot.vmp.service.IPlayService;
18 import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; 21 import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
19 import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; 22 import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
@@ -57,6 +60,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In @@ -57,6 +60,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
57 private IRedisCatchStorage redisCatchStorage; 60 private IRedisCatchStorage redisCatchStorage;
58 61
59 @Autowired 62 @Autowired
  63 + private UserSetting userSetting;
  64 +
  65 + @Autowired
60 private IVideoManagerStorage storager; 66 private IVideoManagerStorage storager;
61 67
62 @Autowired 68 @Autowired
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
@@ -2,9 +2,11 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; @@ -2,9 +2,11 @@ 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.conf.UserSetting;
5 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; 6 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
6 import com.genersoft.iot.vmp.gb28181.bean.*; 7 import com.genersoft.iot.vmp.gb28181.bean.*;
7 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; 8 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
  9 +import com.genersoft.iot.vmp.gb28181.bean.*;
8 import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; 10 import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
9 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 11 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
10 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; 12 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
@@ -18,6 +20,7 @@ import com.genersoft.iot.vmp.service.IDeviceService; @@ -18,6 +20,7 @@ import com.genersoft.iot.vmp.service.IDeviceService;
18 import com.genersoft.iot.vmp.service.IInviteStreamService; 20 import com.genersoft.iot.vmp.service.IInviteStreamService;
19 import com.genersoft.iot.vmp.service.IMediaServerService; 21 import com.genersoft.iot.vmp.service.IMediaServerService;
20 import com.genersoft.iot.vmp.service.IPlayService; 22 import com.genersoft.iot.vmp.service.IPlayService;
  23 +import com.genersoft.iot.vmp.service.*;
21 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; 24 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
22 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 25 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
23 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 26 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
@@ -31,11 +34,7 @@ import org.springframework.stereotype.Component; @@ -31,11 +34,7 @@ import org.springframework.stereotype.Component;
31 import javax.sip.InvalidArgumentException; 34 import javax.sip.InvalidArgumentException;
32 import javax.sip.RequestEvent; 35 import javax.sip.RequestEvent;
33 import javax.sip.SipException; 36 import javax.sip.SipException;
34 -import javax.sip.address.SipURI;  
35 import javax.sip.header.CallIdHeader; 37 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; 38 import javax.sip.message.Response;
40 import java.text.ParseException; 39 import java.text.ParseException;
41 import java.util.HashMap; 40 import java.util.HashMap;
@@ -64,12 +63,18 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @@ -64,12 +63,18 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
64 private IInviteStreamService inviteStreamService; 63 private IInviteStreamService inviteStreamService;
65 64
66 @Autowired 65 @Autowired
  66 + private IPlatformService platformService;
  67 +
  68 + @Autowired
67 private IDeviceService deviceService; 69 private IDeviceService deviceService;
68 70
69 @Autowired 71 @Autowired
70 private AudioBroadcastManager audioBroadcastManager; 72 private AudioBroadcastManager audioBroadcastManager;
71 73
72 @Autowired 74 @Autowired
  75 + private IDeviceChannelService channelService;
  76 +
  77 + @Autowired
73 private IVideoManagerStorage storager; 78 private IVideoManagerStorage storager;
74 79
75 @Autowired 80 @Autowired
@@ -90,6 +95,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @@ -90,6 +95,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
90 @Autowired 95 @Autowired
91 private IPlayService playService; 96 private IPlayService playService;
92 97
  98 + @Autowired
  99 + private UserSetting userSetting;
  100 +
93 @Override 101 @Override
94 public void afterPropertiesSet() throws Exception { 102 public void afterPropertiesSet() throws Exception {
95 // 添加消息处理的订阅 103 // 添加消息处理的订阅
@@ -102,201 +110,107 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @@ -102,201 +110,107 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
102 */ 110 */
103 @Override 111 @Override
104 public void process(RequestEvent evt) { 112 public void process(RequestEvent evt) {
105 -  
106 - // TODO 此处需要重构  
107 - SIPRequest request =(SIPRequest) evt.getRequest(); 113 + SIPRequest request = (SIPRequest) evt.getRequest();
108 try { 114 try {
109 responseAck(request, Response.OK); 115 responseAck(request, Response.OK);
110 } catch (SipException | InvalidArgumentException | ParseException e) { 116 } catch (SipException | InvalidArgumentException | ParseException e) {
111 logger.error("[回复BYE信息失败],{}", e.getMessage()); 117 logger.error("[回复BYE信息失败],{}", e.getMessage());
112 } 118 }
113 CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); 119 CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
114 - String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();  
115 - String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();  
116 - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId, null, callIdHeader.getCallId());  
117 - logger.info("[收到bye] {}/{}", platformGbId, channelId);  
118 - if (sendRtpItem != null){  
119 - String streamId = sendRtpItem.getStream();  
120 - Map<String, Object> param = new HashMap<>();  
121 - param.put("vhost","__defaultVhost__");  
122 - param.put("app",sendRtpItem.getApp());  
123 - param.put("stream",streamId);  
124 - param.put("ssrc",sendRtpItem.getSsrc());  
125 - logger.info("[收到bye] 停止向上级推流:{}", streamId);  
126 - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());  
127 - redisCatchStorage.deleteSendRTPServer(platformGbId, channelId, callIdHeader.getCallId(), null);  
128 - ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());  
129 - zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);  
130 - int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId);  
131 - if (totalReaderCount <= 0) {  
132 - logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId);  
133 - if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) {  
134 - Device device = deviceService.getDevice(sendRtpItem.getDeviceId());  
135 - if (device == null) {  
136 - logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId);  
137 - }  
138 - try {  
139 - logger.warn("[停止点播] {}/{}", sendRtpItem.getDeviceId(), channelId);  
140 - cmder.streamByeCmd(device, channelId, streamId, null);  
141 - } catch (InvalidArgumentException | ParseException | SipException |  
142 - SsrcTransactionNotFoundException e) {  
143 - logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage());  
144 - }  
145 - }  
146 - if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {  
147 - MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,  
148 - sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),  
149 - sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId());  
150 - redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);  
151 - } 120 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId());
  121 +
  122 + if (sendRtpItem != null){
  123 + logger.info("[收到bye] 来自平台{}, 停止通道:{}", sendRtpItem.getPlatformId(), sendRtpItem.getChannelId());
  124 + String streamId = sendRtpItem.getStream();
  125 + Map<String, Object> param = new HashMap<>();
  126 + param.put("vhost","__defaultVhost__");
  127 + param.put("app",sendRtpItem.getApp());
  128 + param.put("stream",streamId);
  129 + param.put("ssrc",sendRtpItem.getSsrc());
  130 + logger.info("[收到bye] 停止向上级推流:{}", streamId);
  131 + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
  132 + redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(),
  133 + callIdHeader.getCallId(), null);
  134 + zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
  135 + if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
  136 + ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId());
  137 + if (platform != null) {
  138 + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
  139 + sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
  140 + sendRtpItem.getPlatformId(), platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
  141 + messageForPushChannel.setPlatFormIndex(platform.getId());
  142 + redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
  143 + }else {
  144 + logger.info("[上级平台停止观看] 未找到平台{}的信息,发送redis消息失败", sendRtpItem.getPlatformId());
152 } 145 }
153 } 146 }
154 - // 可能是设备主动停止  
155 - Device device = storager.queryVideoDeviceByChannelId(platformGbId);  
156 - if (device != null) {  
157 - storager.stopPlay(device.getDeviceId(), channelId);  
158 - SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);  
159 - if (ssrcTransactionForPlay != null){  
160 - if (ssrcTransactionForPlay.getCallId().equals(callIdHeader.getCallId())){  
161 - // 释放ssrc  
162 - MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlay.getMediaServerId());  
163 - if (mediaServerItem != null) {  
164 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlay.getSsrc());  
165 - }  
166 - streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlay.getStream()); 147 +
  148 + int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId);
  149 + if (totalReaderCount <= 0) {
  150 + logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId);
  151 + if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) {
  152 + Device device = deviceService.getDevice(sendRtpItem.getDeviceId());
  153 + if (device == null) {
  154 + logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId);
167 } 155 }
168 - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);  
169 - inviteStreamService.removeInviteInfo(inviteInfo);  
170 - if (inviteInfo != null) {  
171 - if (inviteInfo.getStreamInfo() != null) {  
172 - mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStream());  
173 - } 156 + try {
  157 + logger.warn("[停止点播] {}/{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
  158 + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), streamId, null);
  159 + } catch (InvalidArgumentException | ParseException | SipException |
  160 + SsrcTransactionNotFoundException e) {
  161 + logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage());
174 } 162 }
175 } 163 }
176 - SsrcTransaction ssrcTransactionForPlayBack = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, callIdHeader.getCallId(), null);  
177 - if (ssrcTransactionForPlayBack != null) {  
178 - // 释放ssrc  
179 - MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlayBack.getMediaServerId());  
180 - if (mediaServerItem != null) {  
181 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlayBack.getSsrc());  
182 - }  
183 - streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlayBack.getStream());  
184 - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAYBACK, device.getDeviceId(), channelId); 164 + }
  165 + }
185 166
186 - if (inviteInfo != null) {  
187 - inviteStreamService.removeInviteInfo(inviteInfo);  
188 - if (inviteInfo.getStreamInfo() != null) {  
189 - mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStream());  
190 - }  
191 - }  
192 - } 167 +
  168 +
  169 + // 可能是设备发送的停止
  170 + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
  171 + if (ssrcTransaction == null) {
  172 + logger.info("[收到bye] 但是无法获取推流信息和发流信息,忽略此请求");
  173 + logger.info(request.toString());
  174 + return;
193 } 175 }
  176 + logger.info("[收到bye] 来自设备:{}, 通道已停止推流: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
194 177
195 - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, request.getCallIdHeader().getCallId(), null);  
196 - if (ssrcTransaction != null) { 178 + Device device = deviceService.getDevice(ssrcTransaction.getDeviceId());
  179 + if (device == null) {
  180 + logger.info("[收到bye] 未找到设备:{} ", ssrcTransaction.getDeviceId());
  181 + return;
  182 + }
  183 + DeviceChannel channel = channelService.getOne(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
  184 + if (channel == null) {
  185 + logger.info("[收到bye] 未找到通道,设备:{}, 通道:{}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
  186 + return;
  187 + }
  188 + storager.stopPlay(device.getDeviceId(), channel.getChannelId());
  189 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId());
  190 + if (inviteInfo != null) {
  191 + inviteStreamService.removeInviteInfo(inviteInfo);
  192 + if (inviteInfo.getStreamInfo() != null) {
  193 + mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStreamInfo().getStream());
  194 + }
  195 + }
197 // 释放ssrc 196 // 释放ssrc
198 MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId()); 197 MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
199 if (mediaServerItem != null) { 198 if (mediaServerItem != null) {
200 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc()); 199 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc());
201 } 200 }
202 -  
203 - switch (ssrcTransaction.getType()) {  
204 -// case play:  
205 -// break;  
206 -// case talk:  
207 -// break;  
208 -// case playback:  
209 -// break;  
210 -// case download:  
211 -// break;  
212 - case BROADCAST:  
213 - String channelId1 = ssrcTransaction.getChannelId();  
214 -  
215 - Device deviceFromTransaction = storager.queryVideoDevice(ssrcTransaction.getDeviceId());  
216 - if (deviceFromTransaction == null) {  
217 - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(ssrcTransaction.getDeviceId());  
218 - if (parentPlatform != null) {  
219 - // 来自上级平台的停止对讲  
220 - logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", ssrcTransaction.getDeviceId(), channelId1);  
221 - // 释放ssrc  
222 - streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());  
223 - if (mediaServerItem != null) {  
224 - zlmrtpServerFactory.closeRtpServer(mediaServerItem, ssrcTransaction.getStream());  
225 - }  
226 - // 查找来源的对讲设备,发送停止  
227 - Device sourceDevice = storager.queryVideoDeviceByPlatformIdAndChannelId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());  
228 - if (sourceDevice != null) {  
229 - playService.stopAudioBroadcast(sourceDevice.getDeviceId(), channelId);  
230 - }  
231 - }  
232 - }else {  
233 - // 来自设备的停止对讲  
234 -  
235 - // 如果是来自设备,则听停止推流。 来自上级则停止收流  
236 - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(ssrcTransaction.getDeviceId(), channelId1);  
237 - if (audioBroadcastCatch != null) {  
238 - //  
239 - SendRtpItem sendRtpItemForBroadcast = redisCatchStorage.querySendRTPServer(ssrcTransaction.getDeviceId(), channelId1,  
240 - audioBroadcastCatch.getStream(), audioBroadcastCatch.getSipTransactionInfo().getCallId());  
241 - if (sendRtpItemForBroadcast != null) {  
242 - MediaServerItem mediaServerItemForBroadcast = mediaServerService.getOne(sendRtpItem.getMediaServerId());  
243 - if (mediaServerItemForBroadcast == null) {  
244 - return;  
245 - }  
246 -  
247 - Boolean ready = zlmrtpServerFactory.isStreamReady(mediaServerItem, sendRtpItem.getApp(), audioBroadcastCatch.getStream());  
248 - if (ready) {  
249 - Map<String, Object> param = new HashMap<>();  
250 - param.put("vhost","__defaultVhost__");  
251 - param.put("app",sendRtpItem.getApp());  
252 - param.put("stream",audioBroadcastCatch.getStream());  
253 - param.put("ssrc",sendRtpItem.getSsrc());  
254 - logger.info("[收到bye] 停止推流:{}", audioBroadcastCatch.getStream());  
255 - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());  
256 - redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), request.getCallIdHeader().getCallId(), null);  
257 - zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);  
258 - }  
259 - if (audioBroadcastCatch.isFromPlatform()) {  
260 - // 上级也正在点播。 向上级回复bye  
261 - List<SsrcTransaction> ssrcTransactions = streamSession.getSsrcTransactionForAll(null, channelId1, null, null);  
262 - if (ssrcTransactions.size() > 0) {  
263 - for (SsrcTransaction transaction : ssrcTransactions) {  
264 - if (transaction.getType().equals(InviteSessionType.BROADCAST)) {  
265 - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(transaction.getDeviceId());  
266 - if (parentPlatform != null) {  
267 - try {  
268 - commanderForPlatform.streamByeCmd(parentPlatform, channelId1, transaction.getStream(), transaction.getCallId(), eventResult -> {  
269 - streamSession.remove(transaction.getDeviceId(), transaction.getChannelId(), transaction.getStream());  
270 - });  
271 - audioBroadcastManager.del(transaction.getDeviceId(), channelId1);  
272 - } catch (InvalidArgumentException | SipException | ParseException |  
273 - SsrcTransactionNotFoundException e) {  
274 - logger.error("[命令发送失败] 向{}发送bye失败", transaction.getDeviceId());  
275 - }  
276 - // 释放ssrc  
277 - MediaServerItem mediaServerItemFromTransaction = mediaServerService.getOne(transaction.getMediaServerId());  
278 - if (mediaServerItemFromTransaction != null) {  
279 - mediaServerService.releaseSsrc(mediaServerItemFromTransaction.getId(), transaction.getSsrc());  
280 - }  
281 - streamSession.remove(transaction.getDeviceId(), transaction.getChannelId(), transaction.getStream());  
282 - }  
283 - }  
284 - }  
285 - }  
286 -  
287 - }  
288 - redisCatchStorage.deleteSendRTPServer(ssrcTransaction.getDeviceId(), channelId1,  
289 - audioBroadcastCatch.getStream(), audioBroadcastCatch.getSipTransactionInfo().getCallId());  
290 -  
291 - }  
292 - }  
293 - }  
294 - audioBroadcastManager.del(ssrcTransaction.getDeviceId(), channelId1);  
295 - break;  
296 - default:  
297 - break; 201 + streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.getStream());
  202 + if (ssrcTransaction.getType() == InviteSessionType.BROADCAST) {
  203 + // 查找来源的对讲设备,发送停止
  204 + Device sourceDevice = storager.queryVideoDeviceByPlatformIdAndChannelId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
  205 + if (sourceDevice != null) {
  206 + playService.stopAudioBroadcast(sourceDevice.getDeviceId(), channel.getChannelId());
  207 + }
  208 + }
  209 + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(ssrcTransaction.getDeviceId(), channel.getChannelId());
  210 + if (audioBroadcastCatch != null) {
  211 + // 来自上级平台的停止对讲
  212 + logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", ssrcTransaction.getDeviceId(), channel.getChannelId());
  213 + audioBroadcastManager.del(ssrcTransaction.getDeviceId(), channel.getChannelId());
298 } 214 }
299 -  
300 - }  
301 } 215 }
302 } 216 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -210,16 +210,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -210,16 +210,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
210 return; 210 return;
211 } else { 211 } else {
212 streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream()); 212 streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream());
213 - if (streamPushItem == null || streamPushItem.getServerId().equals(userSetting.getServerId())) {  
214 - logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);  
215 - try {  
216 - responseAck(request, Response.GONE);  
217 - } catch (SipException | InvalidArgumentException | ParseException e) {  
218 - logger.error("[命令发送失败] invite GONE: {}", e.getMessage());  
219 - }  
220 - return;  
221 - }else {  
222 - // TODO 可能漏回复消息 213 + if (streamPushItem != null) {
  214 + mediaServerItem = mediaServerService.getOne(streamPushItem.getMediaServerId());
  215 + }
  216 + if (mediaServerItem == null) {
  217 + mediaServerItem = mediaServerService.getDefaultMediaServer();
223 } 218 }
224 } 219 }
225 } else { 220 } else {
@@ -380,7 +375,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -380,7 +375,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
380 } 375 }
381 logger.info("[上级Invite] {}, 平台:{}, 通道:{}, 收流地址:{}:{},收流方式:{}, ssrc:{}", sessionName, username, channelId, addressStr, port, streamTypeStr, ssrc); 376 logger.info("[上级Invite] {}, 平台:{}, 通道:{}, 收流地址:{}:{},收流方式:{}, ssrc:{}", sessionName, username, channelId, addressStr, port, streamTypeStr, ssrc);
382 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, 377 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
383 - device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp()); 378 + device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback -> {
  379 + return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null;
  380 + });
384 381
385 if (tcpActive != null) { 382 if (tcpActive != null) {
386 sendRtpItem.setTcpActive(tcpActive); 383 sendRtpItem.setTcpActive(tcpActive);
@@ -584,14 +581,16 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -584,14 +581,16 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
584 * 安排推流 581 * 安排推流
585 */ 582 */
586 private void pushProxyStream(RequestEvent evt, SIPRequest request, GbStream gbStream, ParentPlatform platform, 583 private void pushProxyStream(RequestEvent evt, SIPRequest request, GbStream gbStream, ParentPlatform platform,
587 - CallIdHeader callIdHeader, MediaServerItem mediaServerItem,  
588 - int port, Boolean tcpActive, boolean mediaTransmissionTCP,  
589 - String channelId, String addressStr, String ssrc, String requesterId) {  
590 - Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());  
591 - if (streamReady != null && streamReady) {  
592 - // 自平台内容  
593 - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,  
594 - gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); 584 + CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
  585 + int port, Boolean tcpActive, boolean mediaTransmissionTCP,
  586 + String channelId, String addressStr, String ssrc, String requesterId) {
  587 + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
  588 + if (streamReady != null && streamReady) {
  589 + // 自平台内容
  590 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
  591 + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback ->{
  592 + return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null;
  593 + });
595 594
596 if (sendRtpItem == null) { 595 if (sendRtpItem == null) {
597 logger.warn("服务器端口资源不足"); 596 logger.warn("服务器端口资源不足");
@@ -631,7 +630,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -631,7 +630,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
631 if (streamReady != null && streamReady) { 630 if (streamReady != null && streamReady) {
632 // 自平台内容 631 // 自平台内容
633 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, 632 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
634 - gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); 633 + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback ->{
  634 + return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null;
  635 + });
635 636
636 if (sendRtpItem == null) { 637 if (sendRtpItem == null) {
637 logger.warn("服务器端口资源不足"); 638 logger.warn("服务器端口资源不足");
@@ -747,7 +748,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -747,7 +748,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
747 dynamicTask.stop(callIdHeader.getCallId()); 748 dynamicTask.stop(callIdHeader.getCallId());
748 if (serverId.equals(userSetting.getServerId())) { 749 if (serverId.equals(userSetting.getServerId())) {
749 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId, 750 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
750 - app, stream, channelId, mediaTransmissionTCP, platform.isRtcp()); 751 + app, stream, channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback -> {
  752 + return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null;
  753 + });
751 754
752 if (sendRtpItem == null) { 755 if (sendRtpItem == null) {
753 logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足"); 756 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 {
@@ -283,4 +283,4 @@ public class SipUtils { @@ -283,4 +283,4 @@ public class SipUtils {
283 } 283 }
284 return localDateTime.format(DateUtil.formatterISO8601); 284 return localDateTime.format(DateUtil.formatterISO8601);
285 } 285 }
286 -} 286 -}
  287 +}
287 \ No newline at end of file 288 \ No newline at end of file
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -23,6 +23,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; @@ -23,6 +23,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
23 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; 23 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
24 import com.genersoft.iot.vmp.media.zlm.dto.hook.*; 24 import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
25 import com.genersoft.iot.vmp.service.*; 25 import com.genersoft.iot.vmp.service.*;
  26 +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
26 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 27 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
27 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 28 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
28 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; 29 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
@@ -543,6 +544,13 @@ public class ZLMHttpHookListener { @@ -543,6 +544,13 @@ public class ZLMHttpHookListener {
543 } 544 }
544 redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(), 545 redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
545 sendRtpItem.getCallId(), sendRtpItem.getStream()); 546 sendRtpItem.getCallId(), sendRtpItem.getStream());
  547 + if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) {
  548 + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
  549 + sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
  550 + sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
  551 + messageForPushChannel.setPlatFormIndex(parentPlatform.getId());
  552 + redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
  553 + }
546 } 554 }
547 } 555 }
548 } 556 }
@@ -595,7 +603,7 @@ public class ZLMHttpHookListener { @@ -595,7 +603,7 @@ public class ZLMHttpHookListener {
595 } 603 }
596 return ret; 604 return ret;
597 } 605 }
598 - // 推流具有主动性,暂时不做处理 606 + // TODO 推流具有主动性,暂时不做处理
599 // StreamPushItem streamPushItem = streamPushService.getPush(app, streamId); 607 // StreamPushItem streamPushItem = streamPushService.getPush(app, streamId);
600 // if (streamPushItem != null) { 608 // if (streamPushItem != null) {
601 // // TODO 发送停止 609 // // TODO 发送停止
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
@@ -227,13 +227,14 @@ public class ZLMRTPServerFactory { @@ -227,13 +227,14 @@ public class ZLMRTPServerFactory {
227 * @param tcp 是否为tcp 227 * @param tcp 是否为tcp
228 * @return SendRtpItem 228 * @return SendRtpItem
229 */ 229 */
230 - public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp, boolean rtcp){ 230 + public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId,
  231 + String deviceId, String channelId, boolean tcp, boolean rtcp, KeepPortCallback callback){
231 232
232 // 默认为随机端口 233 // 默认为随机端口
233 int localPort = 0; 234 int localPort = 0;
234 if (userSetting.getGbSendStreamStrict()) { 235 if (userSetting.getGbSendStreamStrict()) {
235 if (userSetting.getGbSendStreamStrict()) { 236 if (userSetting.getGbSendStreamStrict()) {
236 - localPort = keepPort(serverItem, ssrc, localPort); 237 + localPort = keepPort(serverItem, ssrc, localPort, callback);
237 if (localPort == 0) { 238 if (localPort == 0) {
238 return null; 239 return null;
239 } 240 }
@@ -265,11 +266,12 @@ public class ZLMRTPServerFactory { @@ -265,11 +266,12 @@ public class ZLMRTPServerFactory {
265 * @param tcp 是否为tcp 266 * @param tcp 是否为tcp
266 * @return SendRtpItem 267 * @return SendRtpItem
267 */ 268 */
268 - public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp, boolean rtcp){ 269 + public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId,
  270 + String app, String stream, String channelId, boolean tcp, boolean rtcp, KeepPortCallback callback){
269 // 默认为随机端口 271 // 默认为随机端口
270 int localPort = 0; 272 int localPort = 0;
271 if (userSetting.getGbSendStreamStrict()) { 273 if (userSetting.getGbSendStreamStrict()) {
272 - localPort = keepPort(serverItem, ssrc, localPort); 274 + localPort = keepPort(serverItem, ssrc, localPort, callback);
273 if (localPort == 0) { 275 if (localPort == 0) {
274 return null; 276 return null;
275 } 277 }
@@ -290,10 +292,14 @@ public class ZLMRTPServerFactory { @@ -290,10 +292,14 @@ public class ZLMRTPServerFactory {
290 return sendRtpItem; 292 return sendRtpItem;
291 } 293 }
292 294
  295 + public interface KeepPortCallback{
  296 + Boolean keep(String ssrc);
  297 + }
  298 +
293 /** 299 /**
294 * 保持端口,直到需要需要发流时再释放 300 * 保持端口,直到需要需要发流时再释放
295 */ 301 */
296 - public int keepPort(MediaServerItem serverItem, String ssrc, Integer localPort) { 302 + public int keepPort(MediaServerItem serverItem, String ssrc, int localPort, KeepPortCallback keepPortCallback) {
297 Map<String, Object> param = new HashMap<>(3); 303 Map<String, Object> param = new HashMap<>(3);
298 param.put("port", localPort); 304 param.put("port", localPort);
299 param.put("enable_tcp", 1); 305 param.put("enable_tcp", 1);
@@ -302,18 +308,20 @@ public class ZLMRTPServerFactory { @@ -302,18 +308,20 @@ public class ZLMRTPServerFactory {
302 if (jsonObject.getInteger("code") == 0) { 308 if (jsonObject.getInteger("code") == 0) {
303 localPort = jsonObject.getInteger("port"); 309 localPort = jsonObject.getInteger("port");
304 HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId()); 310 HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId());
  311 + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
305 Integer finalLocalPort = localPort; 312 Integer finalLocalPort = localPort;
306 hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout, 313 hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout,
307 (MediaServerItem mediaServerItem, HookParam hookParam)->{ 314 (MediaServerItem mediaServerItem, HookParam hookParam)->{
308 logger.info("[上级点播] {}->监听端口到期继续保持监听: {}", ssrc, finalLocalPort); 315 logger.info("[上级点播] {}->监听端口到期继续保持监听: {}", ssrc, finalLocalPort);
309 OnRtpServerTimeoutHookParam rtpServerTimeoutHookParam = (OnRtpServerTimeoutHookParam) hookParam; 316 OnRtpServerTimeoutHookParam rtpServerTimeoutHookParam = (OnRtpServerTimeoutHookParam) hookParam;
310 - if (!ssrc.equals(rtpServerTimeoutHookParam.getSsrc())) {  
311 - return;  
312 - }  
313 - int port = keepPort(serverItem, ssrc, finalLocalPort);  
314 - if (port == 0) {  
315 - logger.info("[上级点播] {}->监听端口失败,移除监听", ssrc);  
316 - hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout); 317 + if (ssrc.equals(rtpServerTimeoutHookParam.getSsrc())) {
  318 + if (keepPortCallback.keep(ssrc)) {
  319 + logger.info("[上级点播] {}->监听端口到期继续保持监听", ssrc);
  320 + keepPort(serverItem, ssrc, finalLocalPort, keepPortCallback);
  321 + }else {
  322 + logger.info("[上级点播] {}->发送取消,无需继续监听", ssrc);
  323 + releasePort(serverItem, ssrc);
  324 + }
317 } 325 }
318 }); 326 });
319 logger.info("[上级点播] {}->监听端口: {}", ssrc, localPort); 327 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/DeviceServiceImpl.java
1 package com.genersoft.iot.vmp.service.impl; 1 package com.genersoft.iot.vmp.service.impl;
2 2
3 -import com.genersoft.iot.vmp.common.InviteSessionType;  
4 import com.genersoft.iot.vmp.common.VideoManagerConstants; 3 import com.genersoft.iot.vmp.common.VideoManagerConstants;
5 import com.genersoft.iot.vmp.conf.DynamicTask; 4 import com.genersoft.iot.vmp.conf.DynamicTask;
6 import com.genersoft.iot.vmp.conf.UserSetting; 5 import com.genersoft.iot.vmp.conf.UserSetting;
@@ -415,8 +414,8 @@ public class DeviceServiceImpl implements IDeviceService { @@ -415,8 +414,8 @@ public class DeviceServiceImpl implements IDeviceService {
415 if (device == null) { 414 if (device == null) {
416 return null; 415 return null;
417 } 416 }
418 - if (ObjectUtils.isEmpty(parentId) || parentId.equals(deviceId)) {  
419 - parentId = null; 417 + if (ObjectUtils.isEmpty(parentId) ) {
  418 + parentId = deviceId;
420 } 419 }
421 List<DeviceChannel> rootNodes = deviceChannelMapper.getSubChannelsByDeviceId(deviceId, parentId, onlyCatalog); 420 List<DeviceChannel> rootNodes = deviceChannelMapper.getSubChannelsByDeviceId(deviceId, parentId, onlyCatalog);
422 return transportChannelsToTree(rootNodes, ""); 421 return transportChannelsToTree(rootNodes, "");
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -362,7 +362,7 @@ public class PlayServiceImpl implements IPlayService { @@ -362,7 +362,7 @@ public class PlayServiceImpl implements IPlayService {
362 null); 362 null);
363 return; 363 return;
364 } 364 }
365 - logger.info("[点播开始] deviceId: {}, channelId: {},码流类型:{},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", 365 + logger.info("[点播开始] deviceId: {}, channelId: {},码流类型:{}, 收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}",
366 device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流", ssrcInfo.getPort(), 366 device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流", ssrcInfo.getPort(),
367 device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); 367 device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
368 //端口获取失败的ssrcInfo 没有必要发送点播指令 368 //端口获取失败的ssrcInfo 没有必要发送点播指令
@@ -445,7 +445,7 @@ public class PlayServiceImpl implements IPlayService { @@ -445,7 +445,7 @@ public class PlayServiceImpl implements IPlayService {
445 InviteErrorCode.SUCCESS.getCode(), 445 InviteErrorCode.SUCCESS.getCode(),
446 InviteErrorCode.SUCCESS.getMsg(), 446 InviteErrorCode.SUCCESS.getMsg(),
447 streamInfo); 447 streamInfo);
448 - logger.info("[点播成功] deviceId: {}, channelId: {},码流类型:{}", device.getDeviceId(), 448 + logger.info("[点播成功] deviceId: {}, channelId:{}, 码流类型:{}", device.getDeviceId(),
449 device.isSwitchPrimarySubStream() ? "辅码流" : "主码流"); 449 device.isSwitchPrimarySubStream() ? "辅码流" : "主码流");
450 String streamUrl; 450 String streamUrl;
451 if (mediaServerItemInuse.getRtspPort() != 0) { 451 if (mediaServerItemInuse.getRtspPort() != 0) {
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/dao/DeviceChannelMapper.java
@@ -459,8 +459,8 @@ public interface DeviceChannelMapper { @@ -459,8 +459,8 @@ public interface DeviceChannelMapper {
459 "select * " + 459 "select * " +
460 "from wvp_device_channel " + 460 "from wvp_device_channel " +
461 "where device_id=#{deviceId}" + 461 "where device_id=#{deviceId}" +
462 - " <if test='parentId != null '> and parent_id = #{parentId} </if>" +  
463 - " <if test='parentId == null '> and parent_id is null </if>" + 462 + " <if test='parentId != null and parentId != deviceId'> and parent_id = #{parentId} </if>" +
  463 + " <if test='parentId == null or parentId == deviceId'> and parent_id is null or parent_id = #{deviceId}</if>" +
464 " <if test='onlyCatalog == true '> and parental = 1 </if>" + 464 " <if test='onlyCatalog == true '> and parental = 1 </if>" +
465 " </script>"}) 465 " </script>"})
466 List<DeviceChannel> getSubChannelsByDeviceId(String deviceId, String parentId, boolean onlyCatalog); 466 List<DeviceChannel> getSubChannelsByDeviceId(String deviceId, String parentId, boolean onlyCatalog);
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 +}