Commit f62bf7b2c6239f2c67f5d9019f8302c8d441f870

Authored by 648540858
2 parents 1294081a bfae9780

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 +}