Commit 6fa5b37b962b05b3aa4b7bf019eb47c4cdbb5738
Merge branch 'main' into main2
# Conflicts: # src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java # 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/message/response/cmd/BroadcastResponseMessageHandler.java # src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java # src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java # src/main/resources/all-application.yml
Showing
15 changed files
with
340 additions
and
143 deletions
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
| @@ -69,6 +69,7 @@ public class VideoManagerConstants { | @@ -69,6 +69,7 @@ public class VideoManagerConstants { | ||
| 69 | public static final String SYSTEM_INFO_NET_PREFIX = "VMP_SYSTEM_INFO_NET_"; | 69 | public static final String SYSTEM_INFO_NET_PREFIX = "VMP_SYSTEM_INFO_NET_"; |
| 70 | 70 | ||
| 71 | public static final String SYSTEM_INFO_DISK_PREFIX = "VMP_SYSTEM_INFO_DISK_"; | 71 | public static final String SYSTEM_INFO_DISK_PREFIX = "VMP_SYSTEM_INFO_DISK_"; |
| 72 | + public static final String BROADCAST_WAITE_INVITE = "task_broadcast_waite_invite_"; | ||
| 72 | 73 | ||
| 73 | 74 | ||
| 74 | 75 |
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
| @@ -43,6 +43,8 @@ public class UserSetting { | @@ -43,6 +43,8 @@ public class UserSetting { | ||
| 43 | 43 | ||
| 44 | private Boolean syncChannelOnDeviceOnline = Boolean.FALSE; | 44 | private Boolean syncChannelOnDeviceOnline = Boolean.FALSE; |
| 45 | 45 | ||
| 46 | + private Boolean pushStreamAfterAck = Boolean.FALSE; | ||
| 47 | + | ||
| 46 | private String serverId = "000000"; | 48 | private String serverId = "000000"; |
| 47 | 49 | ||
| 48 | private String thirdPartyGBIdReg = "[\\s\\S]*"; | 50 | private String thirdPartyGBIdReg = "[\\s\\S]*"; |
| @@ -206,4 +208,12 @@ public class UserSetting { | @@ -206,4 +208,12 @@ public class UserSetting { | ||
| 206 | public void setBroadcastForPlatform(String broadcastForPlatform) { | 208 | public void setBroadcastForPlatform(String broadcastForPlatform) { |
| 207 | this.broadcastForPlatform = broadcastForPlatform; | 209 | this.broadcastForPlatform = broadcastForPlatform; |
| 208 | } | 210 | } |
| 211 | + | ||
| 212 | + public Boolean getPushStreamAfterAck() { | ||
| 213 | + return pushStreamAfterAck; | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + public void setPushStreamAfterAck(Boolean pushStreamAfterAck) { | ||
| 217 | + this.pushStreamAfterAck = pushStreamAfterAck; | ||
| 218 | + } | ||
| 209 | } | 219 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/session/AudioBroadcastManager.java
| @@ -83,4 +83,19 @@ public class AudioBroadcastManager { | @@ -83,4 +83,19 @@ public class AudioBroadcastManager { | ||
| 83 | 83 | ||
| 84 | return audioBroadcastCatch; | 84 | return audioBroadcastCatch; |
| 85 | } | 85 | } |
| 86 | + | ||
| 87 | + public List<AudioBroadcastCatch> get(String deviceId) { | ||
| 88 | + List<AudioBroadcastCatch> audioBroadcastCatchList= new ArrayList<>(); | ||
| 89 | + if (SipUtils.isFrontEnd(deviceId)) { | ||
| 90 | + audioBroadcastCatchList.add(data.get(deviceId)); | ||
| 91 | + }else { | ||
| 92 | + for (String key : data.keySet()) { | ||
| 93 | + if (key.startsWith(deviceId)) { | ||
| 94 | + audioBroadcastCatchList.add(data.get(key)); | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + return audioBroadcastCatchList; | ||
| 100 | + } | ||
| 86 | } | 101 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
| 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request; | 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request; |
| 2 | 2 | ||
| 3 | -import com.genersoft.iot.vmp.conf.SipConfig; | ||
| 4 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | 3 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| 5 | -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; | ||
| 6 | import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; | 4 | import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; |
| 7 | import com.genersoft.iot.vmp.gb28181.utils.SipUtils; | 5 | import com.genersoft.iot.vmp.gb28181.utils.SipUtils; |
| 8 | -import gov.nist.javax.sip.SipProviderImpl; | ||
| 9 | import gov.nist.javax.sip.message.SIPRequest; | 6 | import gov.nist.javax.sip.message.SIPRequest; |
| 10 | import gov.nist.javax.sip.message.SIPResponse; | 7 | import gov.nist.javax.sip.message.SIPResponse; |
| 11 | -import gov.nist.javax.sip.stack.SIPServerTransactionImpl; | ||
| 12 | import org.apache.commons.lang3.ArrayUtils; | 8 | import org.apache.commons.lang3.ArrayUtils; |
| 13 | import org.dom4j.Document; | 9 | import org.dom4j.Document; |
| 14 | import org.dom4j.DocumentException; | 10 | import org.dom4j.DocumentException; |
| @@ -17,14 +13,14 @@ import org.dom4j.io.SAXReader; | @@ -17,14 +13,14 @@ import org.dom4j.io.SAXReader; | ||
| 17 | import org.slf4j.Logger; | 13 | import org.slf4j.Logger; |
| 18 | import org.slf4j.LoggerFactory; | 14 | import org.slf4j.LoggerFactory; |
| 19 | import org.springframework.beans.factory.annotation.Autowired; | 15 | import org.springframework.beans.factory.annotation.Autowired; |
| 20 | -import org.springframework.beans.factory.annotation.Qualifier; | ||
| 21 | -import org.springframework.security.core.parameters.P; | ||
| 22 | 16 | ||
| 23 | import javax.sip.*; | 17 | import javax.sip.*; |
| 24 | import javax.sip.address.Address; | 18 | import javax.sip.address.Address; |
| 25 | import javax.sip.address.AddressFactory; | 19 | import javax.sip.address.AddressFactory; |
| 26 | import javax.sip.address.SipURI; | 20 | import javax.sip.address.SipURI; |
| 27 | -import javax.sip.header.*; | 21 | +import javax.sip.header.ContentTypeHeader; |
| 22 | +import javax.sip.header.ExpiresHeader; | ||
| 23 | +import javax.sip.header.HeaderFactory; | ||
| 28 | import javax.sip.message.MessageFactory; | 24 | import javax.sip.message.MessageFactory; |
| 29 | import javax.sip.message.Request; | 25 | import javax.sip.message.Request; |
| 30 | import javax.sip.message.Response; | 26 | import javax.sip.message.Response; |
| @@ -157,7 +153,10 @@ public abstract class SIPRequestProcessorParent { | @@ -157,7 +153,10 @@ public abstract class SIPRequestProcessorParent { | ||
| 157 | responseAckExtraParam.content = sdp; | 153 | responseAckExtraParam.content = sdp; |
| 158 | responseAckExtraParam.sipURI = sipURI; | 154 | responseAckExtraParam.sipURI = sipURI; |
| 159 | 155 | ||
| 160 | - return responseAck(request, Response.OK, null, responseAckExtraParam); | 156 | + SIPResponse sipResponse = responseAck(request, Response.OK, null, responseAckExtraParam); |
| 157 | + | ||
| 158 | + | ||
| 159 | + return sipResponse; | ||
| 161 | } | 160 | } |
| 162 | 161 | ||
| 163 | /** | 162 | /** |
| @@ -190,7 +189,8 @@ public abstract class SIPRequestProcessorParent { | @@ -190,7 +189,8 @@ public abstract class SIPRequestProcessorParent { | ||
| 190 | reader.setEncoding(charset); | 189 | reader.setEncoding(charset); |
| 191 | // 对海康出现的未转义字符做处理。 | 190 | // 对海康出现的未转义字符做处理。 |
| 192 | String[] destStrArray = new String[]{"<",">","&","'","""}; | 191 | String[] destStrArray = new String[]{"<",">","&","'","""}; |
| 193 | - char despChar = '&'; // 或许可扩展兼容其他字符 | 192 | + // 或许可扩展兼容其他字符 |
| 193 | + char despChar = '&'; | ||
| 194 | byte destBye = (byte) despChar; | 194 | byte destBye = (byte) despChar; |
| 195 | List<Byte> result = new ArrayList<>(); | 195 | List<Byte> result = new ArrayList<>(); |
| 196 | byte[] rawContent = request.getRawContent(); | 196 | byte[] rawContent = request.getRawContent(); |
| @@ -220,4 +220,5 @@ public abstract class SIPRequestProcessorParent { | @@ -220,4 +220,5 @@ public abstract class SIPRequestProcessorParent { | ||
| 220 | return xml.getRootElement(); | 220 | return xml.getRootElement(); |
| 221 | } | 221 | } |
| 222 | 222 | ||
| 223 | + | ||
| 223 | } | 224 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
| 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; | 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; |
| 2 | 2 | ||
| 3 | -import com.alibaba.fastjson2.JSON; | ||
| 4 | import com.alibaba.fastjson2.JSONObject; | 3 | import com.alibaba.fastjson2.JSONObject; |
| 5 | import com.genersoft.iot.vmp.conf.DynamicTask; | 4 | import com.genersoft.iot.vmp.conf.DynamicTask; |
| 6 | -import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; | ||
| 7 | -import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch; | ||
| 8 | -import com.genersoft.iot.vmp.gb28181.bean.Device; | 5 | +import com.genersoft.iot.vmp.conf.UserSetting; |
| 9 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | 6 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| 10 | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; | 7 | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; |
| 11 | -import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; | ||
| 12 | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; | 8 | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; |
| 13 | -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; | ||
| 14 | -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; | ||
| 15 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; | 9 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; |
| 16 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; | 10 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| 17 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; | 11 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| 18 | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; | 12 | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; |
| 19 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | 13 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 20 | -import com.genersoft.iot.vmp.service.IDeviceService; | ||
| 21 | import com.genersoft.iot.vmp.service.IMediaServerService; | 14 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 15 | +import com.genersoft.iot.vmp.service.IPlayService; | ||
| 22 | import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; | 16 | import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; |
| 23 | import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; | 17 | import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; |
| 24 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 18 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| @@ -29,15 +23,15 @@ import org.springframework.beans.factory.InitializingBean; | @@ -29,15 +23,15 @@ import org.springframework.beans.factory.InitializingBean; | ||
| 29 | import org.springframework.beans.factory.annotation.Autowired; | 23 | import org.springframework.beans.factory.annotation.Autowired; |
| 30 | import org.springframework.stereotype.Component; | 24 | import org.springframework.stereotype.Component; |
| 31 | 25 | ||
| 32 | -import javax.sip.InvalidArgumentException; | ||
| 33 | import javax.sip.RequestEvent; | 26 | import javax.sip.RequestEvent; |
| 34 | -import javax.sip.SipException; | ||
| 35 | import javax.sip.address.SipURI; | 27 | import javax.sip.address.SipURI; |
| 36 | import javax.sip.header.CallIdHeader; | 28 | import javax.sip.header.CallIdHeader; |
| 37 | import javax.sip.header.FromHeader; | 29 | import javax.sip.header.FromHeader; |
| 38 | import javax.sip.header.HeaderAddress; | 30 | import javax.sip.header.HeaderAddress; |
| 39 | import javax.sip.header.ToHeader; | 31 | import javax.sip.header.ToHeader; |
| 40 | import java.text.ParseException; | 32 | import java.text.ParseException; |
| 33 | +import java.util.HashMap; | ||
| 34 | +import java.util.Map; | ||
| 41 | 35 | ||
| 42 | /** | 36 | /** |
| 43 | * SIP命令类型: ACK请求 | 37 | * SIP命令类型: ACK请求 |
| @@ -46,7 +40,7 @@ import java.text.ParseException; | @@ -46,7 +40,7 @@ import java.text.ParseException; | ||
| 46 | @Component | 40 | @Component |
| 47 | public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { | 41 | public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { |
| 48 | 42 | ||
| 49 | - private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class); | 43 | + private final Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class); |
| 50 | private final String method = "ACK"; | 44 | private final String method = "ACK"; |
| 51 | 45 | ||
| 52 | @Autowired | 46 | @Autowired |
| @@ -74,31 +68,20 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In | @@ -74,31 +68,20 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In | ||
| 74 | private IMediaServerService mediaServerService; | 68 | private IMediaServerService mediaServerService; |
| 75 | 69 | ||
| 76 | @Autowired | 70 | @Autowired |
| 77 | - private ZlmHttpHookSubscribe subscribe; | ||
| 78 | - | ||
| 79 | - @Autowired | ||
| 80 | private DynamicTask dynamicTask; | 71 | private DynamicTask dynamicTask; |
| 81 | 72 | ||
| 82 | @Autowired | 73 | @Autowired |
| 83 | - private ISIPCommander cmder; | ||
| 84 | - | ||
| 85 | - @Autowired | ||
| 86 | - private IDeviceService deviceService; | ||
| 87 | - | ||
| 88 | - @Autowired | ||
| 89 | - private ISIPCommanderForPlatform commanderForPlatform; | 74 | + private RedisGbPlayMsgListener redisGbPlayMsgListener; |
| 90 | 75 | ||
| 91 | @Autowired | 76 | @Autowired |
| 92 | - private AudioBroadcastManager audioBroadcastManager; | 77 | + private UserSetting userSetting; |
| 93 | 78 | ||
| 94 | @Autowired | 79 | @Autowired |
| 95 | - private RedisGbPlayMsgListener redisGbPlayMsgListener; | 80 | + private IPlayService playService; |
| 96 | 81 | ||
| 97 | 82 | ||
| 98 | /** | 83 | /** |
| 99 | * 处理 ACK请求 | 84 | * 处理 ACK请求 |
| 100 | - * | ||
| 101 | - * @param evt | ||
| 102 | */ | 85 | */ |
| 103 | @Override | 86 | @Override |
| 104 | public void process(RequestEvent evt) { | 87 | public void process(RequestEvent evt) { |
| @@ -106,44 +89,45 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In | @@ -106,44 +89,45 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In | ||
| 106 | 89 | ||
| 107 | String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); | 90 | String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); |
| 108 | logger.info("[收到ACK]: platformGbId->{}", platformGbId); | 91 | logger.info("[收到ACK]: platformGbId->{}", platformGbId); |
| 109 | - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformGbId); | ||
| 110 | - // 取消设置的超时任务 | ||
| 111 | - dynamicTask.stop(callIdHeader.getCallId()); | ||
| 112 | - String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); | ||
| 113 | - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId()); | ||
| 114 | - if (sendRtpItem == null) { | ||
| 115 | - logger.warn("[收到ACK]:未找到通道({})的推流信息", channelId); | ||
| 116 | - return; | ||
| 117 | - } | ||
| 118 | - String is_Udp = sendRtpItem.isTcp() ? "0" : "1"; | ||
| 119 | - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | ||
| 120 | - logger.info("收到ACK,rtp/{}开始向上级推流, 目标={}:{},SSRC={}, RTCP={}", sendRtpItem.getStreamId(), | ||
| 121 | - sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp()); | ||
| 122 | - if (mediaInfo == null) { | ||
| 123 | - RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance( | ||
| 124 | - sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStreamId(), | ||
| 125 | - sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(), | ||
| 126 | - sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio()); | ||
| 127 | - redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, json -> { | ||
| 128 | - startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, json, callIdHeader); | ||
| 129 | - }); | ||
| 130 | - }else { | ||
| 131 | - JSONObject startSendRtpStreamResult = zlmrtpServerFactory.startSendRtp(mediaInfo, sendRtpItem); | ||
| 132 | - if (startSendRtpStreamResult != null) { | ||
| 133 | - startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, callIdHeader); | 92 | + if (userSetting.getPushStreamAfterAck()) { |
| 93 | + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformGbId); | ||
| 94 | + // 取消设置的超时任务 | ||
| 95 | + dynamicTask.stop(callIdHeader.getCallId()); | ||
| 96 | + String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); | ||
| 97 | + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId()); | ||
| 98 | + if (sendRtpItem == null) { | ||
| 99 | + logger.warn("[收到ACK]:未找到通道({})的推流信息", channelId); | ||
| 100 | + return; | ||
| 101 | + } | ||
| 102 | + String is_Udp = sendRtpItem.isTcp() ? "0" : "1"; | ||
| 103 | + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | ||
| 104 | + logger.info("收到ACK,rtp/{}开始向上级推流, 目标={}:{},SSRC={}, RTCP={}", sendRtpItem.getStreamId(), | ||
| 105 | + sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp()); | ||
| 106 | + if (mediaInfo == null) { | ||
| 107 | + RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance( | ||
| 108 | + sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStreamId(), | ||
| 109 | + sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(), | ||
| 110 | + sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio()); | ||
| 111 | + redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, json -> { | ||
| 112 | + startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, json, callIdHeader); | ||
| 113 | + }); | ||
| 114 | + }else { | ||
| 115 | + JSONObject startSendRtpStreamResult = zlmrtpServerFactory.startSendRtp(mediaInfo, sendRtpItem); | ||
| 116 | + if (startSendRtpStreamResult != null) { | ||
| 117 | + startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, callIdHeader); | ||
| 118 | + } | ||
| 134 | } | 119 | } |
| 135 | } | 120 | } |
| 136 | } | 121 | } |
| 137 | - | ||
| 138 | private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform, | 122 | private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform, |
| 139 | - JSONObject jsonObject, CallIdHeader callIdHeader) { | 123 | + JSONObject jsonObject, Map<String, Object> param, CallIdHeader callIdHeader) { |
| 140 | if (jsonObject == null) { | 124 | if (jsonObject == null) { |
| 141 | logger.error("RTP推流失败: 请检查ZLM服务"); | 125 | logger.error("RTP推流失败: 请检查ZLM服务"); |
| 142 | } else if (jsonObject.getInteger("code") == 0) { | 126 | } else if (jsonObject.getInteger("code") == 0) { |
| 143 | logger.info("调用ZLM推流接口, 结果: {}", jsonObject); | 127 | logger.info("调用ZLM推流接口, 结果: {}", jsonObject); |
| 144 | - logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getIp(), sendRtpItem.getPort()); | 128 | + logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); |
| 145 | } else { | 129 | } else { |
| 146 | - logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(sendRtpItem)); | 130 | + logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param)); |
| 147 | if (sendRtpItem.isOnlyAudio()) { | 131 | if (sendRtpItem.isOnlyAudio()) { |
| 148 | Device device = deviceService.getDevice(sendRtpItem.getDeviceId()); | 132 | Device device = deviceService.getDevice(sendRtpItem.getDeviceId()); |
| 149 | AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); | 133 | AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); |
| @@ -152,17 +136,12 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In | @@ -152,17 +136,12 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In | ||
| 152 | cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null); | 136 | cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null); |
| 153 | } catch (SipException | ParseException | InvalidArgumentException | | 137 | } catch (SipException | ParseException | InvalidArgumentException | |
| 154 | SsrcTransactionNotFoundException e) { | 138 | SsrcTransactionNotFoundException e) { |
| 155 | - logger.error("[命令发送失败] 停止语音喊话: {}", e.getMessage()); | 139 | + logger.error("[命令发送失败] 停止语音对讲: {}", e.getMessage()); |
| 156 | } | 140 | } |
| 157 | } | 141 | } |
| 158 | - }else { | ||
| 159 | - // 向上级平台 | ||
| 160 | - try { | ||
| 161 | - commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId()); | ||
| 162 | - } catch (SipException | InvalidArgumentException | ParseException e) { | ||
| 163 | - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); | ||
| 164 | - } | ||
| 165 | } | 142 | } |
| 166 | } | 143 | } |
| 144 | + | ||
| 167 | } | 145 | } |
| 146 | + | ||
| 168 | } | 147 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
| 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; | 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; |
| 2 | 2 | ||
| 3 | import com.alibaba.fastjson2.JSONObject; | 3 | import com.alibaba.fastjson2.JSONObject; |
| 4 | +import com.genersoft.iot.vmp.common.VideoManagerConstants; | ||
| 4 | import com.genersoft.iot.vmp.conf.DynamicTask; | 5 | import com.genersoft.iot.vmp.conf.DynamicTask; |
| 5 | import com.genersoft.iot.vmp.conf.SipConfig; | 6 | import com.genersoft.iot.vmp.conf.SipConfig; |
| 6 | import com.genersoft.iot.vmp.conf.UserSetting; | 7 | import com.genersoft.iot.vmp.conf.UserSetting; |
| @@ -439,18 +440,23 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -439,18 +440,23 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 439 | 440 | ||
| 440 | try { | 441 | try { |
| 441 | // 超时未收到Ack应该回复bye,当前等待时间为10秒 | 442 | // 超时未收到Ack应该回复bye,当前等待时间为10秒 |
| 442 | - dynamicTask.startDelay(callIdHeader.getCallId(), () -> { | ||
| 443 | - logger.info("Ack 等待超时"); | ||
| 444 | - mediaServerService.releaseSsrc(mediaServerItemInUSe.getId(), sendRtpItem.getSsrc()); | ||
| 445 | - // 回复bye | ||
| 446 | - try { | ||
| 447 | - cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId()); | ||
| 448 | - } catch (SipException | InvalidArgumentException | ParseException e) { | ||
| 449 | - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); | ||
| 450 | - } | ||
| 451 | - }, 60 * 1000); | ||
| 452 | - responseSdpAck(request, content.toString(), platform); | 443 | + if (userSetting.getPushStreamAfterAck()) { |
| 444 | + dynamicTask.startDelay(callIdHeader.getCallId(), () -> { | ||
| 445 | + logger.info("Ack 等待超时"); | ||
| 446 | + mediaServerService.releaseSsrc(mediaServerItemInUSe.getId(), sendRtpItem.getSsrc()); | ||
| 447 | + // 回复bye | ||
| 448 | + try { | ||
| 449 | + cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId()); | ||
| 450 | + } catch (SipException | InvalidArgumentException | ParseException e) { | ||
| 451 | + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); | ||
| 452 | + } | ||
| 453 | + }, 60 * 1000); | ||
| 454 | + } | ||
| 453 | 455 | ||
| 456 | + SIPResponse sipResponse = responseSdpAck(request, content.toString(), platform); | ||
| 457 | + if (!userSetting.getPushStreamAfterAck()) { | ||
| 458 | + playService.startPushStream(sendRtpItem, sipResponse, platform, request.getCallIdHeader()); | ||
| 459 | + } | ||
| 454 | } catch (SipException e) { | 460 | } catch (SipException e) { |
| 455 | e.printStackTrace(); | 461 | e.printStackTrace(); |
| 456 | } catch (InvalidArgumentException e) { | 462 | } catch (InvalidArgumentException e) { |
| @@ -878,7 +884,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -878,7 +884,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 878 | content.append("f=\r\n"); | 884 | content.append("f=\r\n"); |
| 879 | 885 | ||
| 880 | try { | 886 | try { |
| 881 | - return responseSdpAck(request, content.toString(), platform); | 887 | + SIPResponse sipResponse = responseSdpAck(request, content.toString(), platform); |
| 888 | + if (!userSetting.getPushStreamAfterAck()) { | ||
| 889 | + playService.startPushStream(sendRtpItem, sipResponse, platform, request.getCallIdHeader()); | ||
| 890 | + } | ||
| 891 | + return sipResponse; | ||
| 882 | } catch (SipException e) { | 892 | } catch (SipException e) { |
| 883 | e.printStackTrace(); | 893 | e.printStackTrace(); |
| 884 | } catch (InvalidArgumentException e) { | 894 | } catch (InvalidArgumentException e) { |
| @@ -905,11 +915,14 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -905,11 +915,14 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 905 | } | 915 | } |
| 906 | if (device != null) { | 916 | if (device != null) { |
| 907 | logger.info("收到设备" + requesterId + "的语音广播Invite请求"); | 917 | logger.info("收到设备" + requesterId + "的语音广播Invite请求"); |
| 908 | - | 918 | + String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId() + audioBroadcastCatch.getChannelId(); |
| 919 | + dynamicTask.stop(key); | ||
| 909 | try { | 920 | try { |
| 910 | responseAck(request, Response.TRYING); | 921 | responseAck(request, Response.TRYING); |
| 911 | } catch (SipException | InvalidArgumentException | ParseException e) { | 922 | } catch (SipException | InvalidArgumentException | ParseException e) { |
| 912 | logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); | 923 | logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); |
| 924 | + playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | ||
| 925 | + return; | ||
| 913 | } | 926 | } |
| 914 | String contentString = new String(request.getRawContent()); | 927 | String contentString = new String(request.getRawContent()); |
| 915 | // jainSip不支持y=字段, 移除移除以解析。 | 928 | // jainSip不支持y=字段, 移除移除以解析。 |
| @@ -964,11 +977,14 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -964,11 +977,14 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 964 | responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 | 977 | responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 |
| 965 | } catch (SipException | InvalidArgumentException | ParseException e) { | 978 | } catch (SipException | InvalidArgumentException | ParseException e) { |
| 966 | logger.error("[命令发送失败] invite 不支持的媒体格式: {}", e.getMessage()); | 979 | logger.error("[命令发送失败] invite 不支持的媒体格式: {}", e.getMessage()); |
| 980 | + playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | ||
| 981 | + return; | ||
| 967 | } | 982 | } |
| 968 | return; | 983 | return; |
| 969 | } | 984 | } |
| 970 | String addressStr = sdp.getOrigin().getAddress(); | 985 | String addressStr = sdp.getOrigin().getAddress(); |
| 971 | - logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", requesterId, addressStr, port, ssrc); | 986 | + logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}, {}", requesterId, addressStr, port, ssrc, |
| 987 | + mediaTransmissionTCP ? (tcpActive? "TCP主动":"TCP被动") : "UDP"); | ||
| 972 | 988 | ||
| 973 | MediaServerItem mediaServerItem = playService.getNewMediaServerItem(device); | 989 | MediaServerItem mediaServerItem = playService.getNewMediaServerItem(device); |
| 974 | if (mediaServerItem == null) { | 990 | if (mediaServerItem == null) { |
| @@ -977,6 +993,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -977,6 +993,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 977 | responseAck(request, Response.BUSY_HERE); | 993 | responseAck(request, Response.BUSY_HERE); |
| 978 | } catch (SipException | InvalidArgumentException | ParseException e) { | 994 | } catch (SipException | InvalidArgumentException | ParseException e) { |
| 979 | logger.error("[命令发送失败] invite 未找到可用的zlm: {}", e.getMessage()); | 995 | logger.error("[命令发送失败] invite 未找到可用的zlm: {}", e.getMessage()); |
| 996 | + playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | ||
| 980 | } | 997 | } |
| 981 | return; | 998 | return; |
| 982 | } | 999 | } |
| @@ -990,13 +1007,12 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -990,13 +1007,12 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 990 | responseAck(request, Response.BUSY_HERE); | 1007 | responseAck(request, Response.BUSY_HERE); |
| 991 | } catch (SipException | InvalidArgumentException | ParseException e) { | 1008 | } catch (SipException | InvalidArgumentException | ParseException e) { |
| 992 | logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); | 1009 | logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); |
| 1010 | + playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | ||
| 1011 | + return; | ||
| 993 | } | 1012 | } |
| 994 | return; | 1013 | return; |
| 995 | } | 1014 | } |
| 996 | - sendRtpItem.setTcp(mediaTransmissionTCP); | ||
| 997 | - if (tcpActive != null) { | ||
| 998 | - sendRtpItem.setTcpActive(tcpActive); | ||
| 999 | - } | 1015 | + |
| 1000 | String app = "broadcast"; | 1016 | String app = "broadcast"; |
| 1001 | String stream = device.getDeviceId() + "_" + audioBroadcastCatch.getChannelId(); | 1017 | String stream = device.getDeviceId() + "_" + audioBroadcastCatch.getChannelId(); |
| 1002 | 1018 | ||
| @@ -1011,6 +1027,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -1011,6 +1027,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 1011 | sendRtpItem.setUsePs(false); | 1027 | sendRtpItem.setUsePs(false); |
| 1012 | sendRtpItem.setRtcp(false); | 1028 | sendRtpItem.setRtcp(false); |
| 1013 | sendRtpItem.setOnlyAudio(true); | 1029 | sendRtpItem.setOnlyAudio(true); |
| 1030 | + sendRtpItem.setTcp(mediaTransmissionTCP); | ||
| 1031 | + if (tcpActive != null) { | ||
| 1032 | + sendRtpItem.setTcpActive(tcpActive); | ||
| 1033 | + } | ||
| 1034 | + | ||
| 1014 | redisCatchStorage.updateSendRTPSever(sendRtpItem); | 1035 | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| 1015 | 1036 | ||
| 1016 | 1037 | ||
| @@ -1023,11 +1044,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -1023,11 +1044,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 1023 | responseAck(request, Response.GONE); | 1044 | responseAck(request, Response.GONE); |
| 1024 | } catch (SipException | InvalidArgumentException | ParseException e) { | 1045 | } catch (SipException | InvalidArgumentException | ParseException e) { |
| 1025 | logger.error("[命令发送失败] 语音通话 回复410失败, {}", e.getMessage()); | 1046 | logger.error("[命令发送失败] 语音通话 回复410失败, {}", e.getMessage()); |
| 1047 | + return; | ||
| 1026 | } | 1048 | } |
| 1027 | playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | 1049 | playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); |
| 1028 | } | 1050 | } |
| 1029 | } catch (SdpException e) { | 1051 | } catch (SdpException e) { |
| 1030 | logger.error("[SDP解析异常]", e); | 1052 | logger.error("[SDP解析异常]", e); |
| 1053 | + playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | ||
| 1031 | } | 1054 | } |
| 1032 | } else { | 1055 | } else { |
| 1033 | logger.warn("来自无效设备/平台的请求"); | 1056 | logger.warn("来自无效设备/平台的请求"); |
| @@ -1084,6 +1107,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -1084,6 +1107,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 1084 | audioBroadcastCatch.setSipTransactionInfoByRequset(sipResponse); | 1107 | audioBroadcastCatch.setSipTransactionInfoByRequset(sipResponse); |
| 1085 | audioBroadcastManager.update(audioBroadcastCatch); | 1108 | audioBroadcastManager.update(audioBroadcastCatch); |
| 1086 | 1109 | ||
| 1110 | + // 开启发流,大华在收到200OK后就会开始建立连接 | ||
| 1111 | + if (!userSetting.getPushStreamAfterAck()) { | ||
| 1112 | + playService.startPushStream(sendRtpItem, sipResponse, parentPlatform, request.getCallIdHeader()); | ||
| 1113 | + } | ||
| 1114 | + | ||
| 1087 | } catch (SipException | InvalidArgumentException | ParseException | SdpParseException e) { | 1115 | } catch (SipException | InvalidArgumentException | ParseException | SdpParseException e) { |
| 1088 | logger.error("[命令发送失败] 语音喊话 回复200OK(SDP): {}", e.getMessage()); | 1116 | logger.error("[命令发送失败] 语音喊话 回复200OK(SDP): {}", e.getMessage()); |
| 1089 | } | 1117 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java
| 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; | 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; |
| 2 | 2 | ||
| 3 | +import com.genersoft.iot.vmp.common.VideoManagerConstants; | ||
| 4 | +import com.genersoft.iot.vmp.conf.DynamicTask; | ||
| 3 | import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch; | 5 | import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch; |
| 4 | import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatchStatus; | 6 | import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatchStatus; |
| 5 | import com.genersoft.iot.vmp.gb28181.bean.Device; | 7 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| @@ -9,6 +11,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; | @@ -9,6 +11,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; | ||
| 9 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; | 11 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| 10 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; | 12 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; |
| 11 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; | 13 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; |
| 14 | +import com.genersoft.iot.vmp.service.IPlayService; | ||
| 12 | import gov.nist.javax.sip.message.SIPRequest; | 15 | import gov.nist.javax.sip.message.SIPRequest; |
| 13 | import org.dom4j.Element; | 16 | import org.dom4j.Element; |
| 14 | import org.slf4j.Logger; | 17 | import org.slf4j.Logger; |
| @@ -35,11 +38,14 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i | @@ -35,11 +38,14 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i | ||
| 35 | private ResponseMessageHandler responseMessageHandler; | 38 | private ResponseMessageHandler responseMessageHandler; |
| 36 | 39 | ||
| 37 | @Autowired | 40 | @Autowired |
| 38 | - private DeferredResultHolder deferredResultHolder; | 41 | + private DynamicTask dynamicTask; |
| 39 | 42 | ||
| 40 | @Autowired | 43 | @Autowired |
| 41 | private AudioBroadcastManager audioBroadcastManager; | 44 | private AudioBroadcastManager audioBroadcastManager; |
| 42 | 45 | ||
| 46 | + @Autowired | ||
| 47 | + private IPlayService playService; | ||
| 48 | + | ||
| 43 | @Override | 49 | @Override |
| 44 | public void afterPropertiesSet() throws Exception { | 50 | public void afterPropertiesSet() throws Exception { |
| 45 | responseMessageHandler.addHandler(cmdType, this); | 51 | responseMessageHandler.addHandler(cmdType, this); |
| @@ -47,6 +53,8 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i | @@ -47,6 +53,8 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i | ||
| 47 | 53 | ||
| 48 | @Override | 54 | @Override |
| 49 | public void handForDevice(RequestEvent evt, Device device, Element rootElement) { | 55 | public void handForDevice(RequestEvent evt, Device device, Element rootElement) { |
| 56 | + | ||
| 57 | + SIPRequest request = (SIPRequest) evt.getRequest(); | ||
| 50 | try { | 58 | try { |
| 51 | String channelId = getText(rootElement, "DeviceID"); | 59 | String channelId = getText(rootElement, "DeviceID"); |
| 52 | if (!audioBroadcastManager.exit(device.getDeviceId(), channelId)) { | 60 | if (!audioBroadcastManager.exit(device.getDeviceId(), channelId)) { |
| @@ -55,12 +63,23 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i | @@ -55,12 +63,23 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i | ||
| 55 | return; | 63 | return; |
| 56 | } | 64 | } |
| 57 | String result = getText(rootElement, "Result"); | 65 | String result = getText(rootElement, "Result"); |
| 58 | - logger.info("收到语音广播的回复 {}:{}/{}", result, device.getDeviceId(), channelId ); | ||
| 59 | - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(device.getDeviceId(), channelId); | ||
| 60 | - audioBroadcastCatch.setStatus(AudioBroadcastCatchStatus.WaiteInvite); | ||
| 61 | - audioBroadcastManager.update(audioBroadcastCatch); | 66 | + logger.info("[语音广播]回复:{}, {}/{}", result, device.getDeviceId(), channelId ); |
| 67 | + | ||
| 62 | // 回复200 OK | 68 | // 回复200 OK |
| 63 | - responseAck((SIPRequest) evt.getRequest(), Response.OK); | 69 | + responseAck(request, Response.OK); |
| 70 | + if (result.equalsIgnoreCase("OK")) { | ||
| 71 | + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(device.getDeviceId(), channelId); | ||
| 72 | + audioBroadcastCatch.setStatus(AudioBroadcastCatchStatus.WaiteInvite); | ||
| 73 | + audioBroadcastManager.update(audioBroadcastCatch); | ||
| 74 | + // 等待invite消息, 超时则结束 | ||
| 75 | + String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId() + channelId; | ||
| 76 | + dynamicTask.startDelay(key, ()->{ | ||
| 77 | + logger.info("[语音广播]等待invite消息超时:{}/{}", device.getDeviceId(), channelId); | ||
| 78 | + playService.stopAudioBroadcast(device.getDeviceId(), channelId); | ||
| 79 | + }, 2000); | ||
| 80 | + }else { | ||
| 81 | + playService.stopAudioBroadcast(device.getDeviceId(), channelId); | ||
| 82 | + } | ||
| 64 | } catch (ParseException | SipException | InvalidArgumentException e) { | 83 | } catch (ParseException | SipException | InvalidArgumentException e) { |
| 65 | logger.error("[命令发送失败] 国标级联 语音喊话: {}", e.getMessage()); | 84 | logger.error("[命令发送失败] 国标级联 语音喊话: {}", e.getMessage()); |
| 66 | } | 85 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
| @@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; | @@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; | ||
| 35 | public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { | 35 | public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { |
| 36 | 36 | ||
| 37 | private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class); | 37 | private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class); |
| 38 | + | ||
| 38 | private final String cmdType = "Catalog"; | 39 | private final String cmdType = "Catalog"; |
| 39 | 40 | ||
| 40 | @Autowired | 41 | @Autowired |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
| @@ -323,7 +323,7 @@ public class ZLMHttpHookListener { | @@ -323,7 +323,7 @@ public class ZLMHttpHookListener { | ||
| 323 | }); | 323 | }); |
| 324 | 324 | ||
| 325 | if ("rtsp".equals(param.getSchema())){ | 325 | if ("rtsp".equals(param.getSchema())){ |
| 326 | - logger.info("on_stream_changed:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream()); | 326 | + logger.info("流变化:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream()); |
| 327 | if (param.isRegist()) { | 327 | if (param.isRegist()) { |
| 328 | mediaServerService.addCount(param.getMediaServerId()); | 328 | mediaServerService.addCount(param.getMediaServerId()); |
| 329 | }else { | 329 | }else { |
| @@ -383,10 +383,10 @@ public class ZLMHttpHookListener { | @@ -383,10 +383,10 @@ public class ZLMHttpHookListener { | ||
| 383 | } | 383 | } |
| 384 | 384 | ||
| 385 | }else { | 385 | }else { |
| 386 | - logger.info("[语音喊话] 推流指向的·通道{}未找到", channelId); | 386 | + logger.info("[语音对讲] 未找到通道:{}", channelId); |
| 387 | } | 387 | } |
| 388 | - }else { | ||
| 389 | - logger.info("[语音喊话] 推流指向的·设备{}未找到", deviceId); | 388 | + }else{ |
| 389 | + logger.info("[语音对讲] 未找到设备:{}", deviceId); | ||
| 390 | } | 390 | } |
| 391 | }else { | 391 | }else { |
| 392 | logger.info("[语音喊话] 推流格式有误, 格式为: broadcast/设备编号_通道编号 "); | 392 | logger.info("[语音喊话] 推流格式有误, 格式为: broadcast/设备编号_通道编号 "); |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
| @@ -36,7 +36,7 @@ public class ZLMRESTfulUtils { | @@ -36,7 +36,7 @@ public class ZLMRESTfulUtils { | ||
| 36 | // 设置连接超时时间 | 36 | // 设置连接超时时间 |
| 37 | httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS); | 37 | httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS); |
| 38 | // 设置读取超时时间 | 38 | // 设置读取超时时间 |
| 39 | - httpClientBuilder.readTimeout(5,TimeUnit.SECONDS); | 39 | + httpClientBuilder.readTimeout(15,TimeUnit.SECONDS); |
| 40 | // 设置连接池 | 40 | // 设置连接池 |
| 41 | httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES)); | 41 | httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES)); |
| 42 | if (logger.isDebugEnabled()) { | 42 | if (logger.isDebugEnabled()) { |
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
| @@ -3,9 +3,7 @@ package com.genersoft.iot.vmp.service; | @@ -3,9 +3,7 @@ package com.genersoft.iot.vmp.service; | ||
| 3 | import com.alibaba.fastjson2.JSONObject; | 3 | import com.alibaba.fastjson2.JSONObject; |
| 4 | import com.genersoft.iot.vmp.common.StreamInfo; | 4 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 5 | import com.genersoft.iot.vmp.conf.exception.ServiceException; | 5 | import com.genersoft.iot.vmp.conf.exception.ServiceException; |
| 6 | -import com.genersoft.iot.vmp.gb28181.bean.Device; | ||
| 7 | -import com.genersoft.iot.vmp.gb28181.bean.InviteStreamCallback; | ||
| 8 | -import com.genersoft.iot.vmp.gb28181.bean.InviteStreamInfo; | 6 | +import com.genersoft.iot.vmp.gb28181.bean.*; |
| 9 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | 7 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| 10 | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; | 8 | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; |
| 11 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | 9 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| @@ -15,11 +13,14 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo; | @@ -15,11 +13,14 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo; | ||
| 15 | import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; | 13 | import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; |
| 16 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; | 14 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; |
| 17 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | 15 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| 16 | +import gov.nist.javax.sip.message.SIPResponse; | ||
| 18 | import org.springframework.web.context.request.async.DeferredResult; | 17 | import org.springframework.web.context.request.async.DeferredResult; |
| 19 | 18 | ||
| 20 | import javax.sip.InvalidArgumentException; | 19 | import javax.sip.InvalidArgumentException; |
| 21 | import javax.sip.SipException; | 20 | import javax.sip.SipException; |
| 21 | +import javax.sip.header.CallIdHeader; | ||
| 22 | import java.text.ParseException; | 22 | import java.text.ParseException; |
| 23 | +import java.util.Map; | ||
| 23 | 24 | ||
| 24 | /** | 25 | /** |
| 25 | * 点播处理 | 26 | * 点播处理 |
| @@ -64,4 +65,9 @@ public interface IPlayService { | @@ -64,4 +65,9 @@ public interface IPlayService { | ||
| 64 | void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; | 65 | void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; |
| 65 | 66 | ||
| 66 | void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; | 67 | void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; |
| 68 | + | ||
| 69 | + void startPushStream(SendRtpItem sendRtpItem, SIPResponse sipResponse, ParentPlatform platform, CallIdHeader callIdHeader); | ||
| 70 | + | ||
| 71 | + void startSendRtpStreamHand(SendRtpItem sendRtpItem, ParentPlatform parentPlatform, | ||
| 72 | + JSONObject jsonObject, Map<String, Object> param, CallIdHeader callIdHeader); | ||
| 67 | } | 73 | } |
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
| @@ -3,12 +3,15 @@ package com.genersoft.iot.vmp.service.impl; | @@ -3,12 +3,15 @@ package com.genersoft.iot.vmp.service.impl; | ||
| 3 | import com.genersoft.iot.vmp.conf.DynamicTask; | 3 | import com.genersoft.iot.vmp.conf.DynamicTask; |
| 4 | import com.genersoft.iot.vmp.conf.UserSetting; | 4 | import com.genersoft.iot.vmp.conf.UserSetting; |
| 5 | import com.genersoft.iot.vmp.gb28181.bean.*; | 5 | import com.genersoft.iot.vmp.gb28181.bean.*; |
| 6 | +import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; | ||
| 6 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; | 7 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 7 | import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; | 8 | import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; |
| 8 | import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; | 9 | import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; |
| 9 | import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; | 10 | import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; |
| 10 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; | 11 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| 11 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; | 12 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; |
| 13 | +import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | ||
| 14 | +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | ||
| 12 | import com.genersoft.iot.vmp.service.IDeviceChannelService; | 15 | import com.genersoft.iot.vmp.service.IDeviceChannelService; |
| 13 | import com.genersoft.iot.vmp.service.IDeviceService; | 16 | import com.genersoft.iot.vmp.service.IDeviceService; |
| 14 | import com.genersoft.iot.vmp.service.IMediaServerService; | 17 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| @@ -32,9 +35,7 @@ import javax.sip.InvalidArgumentException; | @@ -32,9 +35,7 @@ import javax.sip.InvalidArgumentException; | ||
| 32 | import javax.sip.SipException; | 35 | import javax.sip.SipException; |
| 33 | import java.text.ParseException; | 36 | import java.text.ParseException; |
| 34 | import java.time.Instant; | 37 | import java.time.Instant; |
| 35 | -import java.util.ArrayList; | ||
| 36 | -import java.util.Collections; | ||
| 37 | -import java.util.List; | 38 | +import java.util.*; |
| 38 | import java.util.concurrent.TimeUnit; | 39 | import java.util.concurrent.TimeUnit; |
| 39 | 40 | ||
| 40 | /** | 41 | /** |
| @@ -89,6 +90,12 @@ public class DeviceServiceImpl implements IDeviceService { | @@ -89,6 +90,12 @@ public class DeviceServiceImpl implements IDeviceService { | ||
| 89 | @Autowired | 90 | @Autowired |
| 90 | private IMediaServerService mediaServerService; | 91 | private IMediaServerService mediaServerService; |
| 91 | 92 | ||
| 93 | + @Autowired | ||
| 94 | + private AudioBroadcastManager audioBroadcastManager; | ||
| 95 | + | ||
| 96 | + @Autowired | ||
| 97 | + private ZLMRESTfulUtils zlmresTfulUtils; | ||
| 98 | + | ||
| 92 | @Override | 99 | @Override |
| 93 | public void online(Device device) { | 100 | public void online(Device device) { |
| 94 | logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort()); | 101 | logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort()); |
| @@ -183,6 +190,23 @@ public class DeviceServiceImpl implements IDeviceService { | @@ -183,6 +190,23 @@ public class DeviceServiceImpl implements IDeviceService { | ||
| 183 | // 移除订阅 | 190 | // 移除订阅 |
| 184 | removeCatalogSubscribe(device); | 191 | removeCatalogSubscribe(device); |
| 185 | removeMobilePositionSubscribe(device); | 192 | removeMobilePositionSubscribe(device); |
| 193 | + List<AudioBroadcastCatch> audioBroadcastCatches = audioBroadcastManager.get(deviceId); | ||
| 194 | + if (audioBroadcastCatches.size() > 0) { | ||
| 195 | + for (AudioBroadcastCatch audioBroadcastCatch : audioBroadcastCatches) { | ||
| 196 | + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(deviceId, audioBroadcastCatch.getChannelId(), null, null); | ||
| 197 | + if (sendRtpItem != null) { | ||
| 198 | + redisCatchStorage.deleteSendRTPServer(deviceId, sendRtpItem.getChannelId(), null, null); | ||
| 199 | + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | ||
| 200 | + Map<String, Object> param = new HashMap<>(); | ||
| 201 | + param.put("vhost", "__defaultVhost__"); | ||
| 202 | + param.put("app", sendRtpItem.getApp()); | ||
| 203 | + param.put("stream", sendRtpItem.getStreamId()); | ||
| 204 | + zlmresTfulUtils.stopSendRtp(mediaInfo, param); | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + audioBroadcastManager.del(deviceId, audioBroadcastCatch.getChannelId()); | ||
| 208 | + } | ||
| 209 | + } | ||
| 186 | } | 210 | } |
| 187 | 211 | ||
| 188 | @Override | 212 | @Override |
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
| @@ -24,16 +24,15 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | @@ -24,16 +24,15 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | ||
| 24 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; | 24 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| 25 | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; | 25 | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; |
| 26 | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; | 26 | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; |
| 27 | +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRtpServerTimeout; | ||
| 27 | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; | 28 | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; |
| 28 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | 29 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 29 | import com.genersoft.iot.vmp.service.IDeviceService; | 30 | import com.genersoft.iot.vmp.service.IDeviceService; |
| 30 | import com.genersoft.iot.vmp.service.IMediaServerService; | 31 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 31 | import com.genersoft.iot.vmp.service.IMediaService; | 32 | import com.genersoft.iot.vmp.service.IMediaService; |
| 32 | import com.genersoft.iot.vmp.service.IPlayService; | 33 | import com.genersoft.iot.vmp.service.IPlayService; |
| 33 | -import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; | ||
| 34 | -import com.genersoft.iot.vmp.service.bean.PlayBackCallback; | ||
| 35 | -import com.genersoft.iot.vmp.service.bean.PlayBackResult; | ||
| 36 | -import com.genersoft.iot.vmp.service.bean.SSRCInfo; | 34 | +import com.genersoft.iot.vmp.service.bean.*; |
| 35 | +import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; | ||
| 37 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 36 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 38 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; | 37 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| 39 | import com.genersoft.iot.vmp.utils.DateUtil; | 38 | import com.genersoft.iot.vmp.utils.DateUtil; |
| @@ -42,6 +41,7 @@ import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; | @@ -42,6 +41,7 @@ import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; | ||
| 42 | import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; | 41 | import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; |
| 43 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | 42 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| 44 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; | 43 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; |
| 44 | +import gov.nist.javax.sip.message.SIPResponse; | ||
| 45 | import org.slf4j.Logger; | 45 | import org.slf4j.Logger; |
| 46 | import org.slf4j.LoggerFactory; | 46 | import org.slf4j.LoggerFactory; |
| 47 | import org.springframework.beans.factory.annotation.Autowired; | 47 | import org.springframework.beans.factory.annotation.Autowired; |
| @@ -54,13 +54,11 @@ import org.springframework.web.context.request.async.DeferredResult; | @@ -54,13 +54,11 @@ import org.springframework.web.context.request.async.DeferredResult; | ||
| 54 | import javax.sip.InvalidArgumentException; | 54 | import javax.sip.InvalidArgumentException; |
| 55 | import javax.sip.ResponseEvent; | 55 | import javax.sip.ResponseEvent; |
| 56 | import javax.sip.SipException; | 56 | import javax.sip.SipException; |
| 57 | +import javax.sip.header.CallIdHeader; | ||
| 57 | import java.math.BigDecimal; | 58 | import java.math.BigDecimal; |
| 58 | import java.math.RoundingMode; | 59 | import java.math.RoundingMode; |
| 59 | import java.text.ParseException; | 60 | import java.text.ParseException; |
| 60 | -import java.util.HashMap; | ||
| 61 | -import java.util.List; | ||
| 62 | -import java.util.Map; | ||
| 63 | -import java.util.UUID; | 61 | +import java.util.*; |
| 64 | 62 | ||
| 65 | @SuppressWarnings(value = {"rawtypes", "unchecked"}) | 63 | @SuppressWarnings(value = {"rawtypes", "unchecked"}) |
| 66 | @Service | 64 | @Service |
| @@ -119,11 +117,20 @@ public class PlayServiceImpl implements IPlayService { | @@ -119,11 +117,20 @@ public class PlayServiceImpl implements IPlayService { | ||
| 119 | @Autowired | 117 | @Autowired |
| 120 | private ZlmHttpHookSubscribe subscribe; | 118 | private ZlmHttpHookSubscribe subscribe; |
| 121 | 119 | ||
| 120 | + @Autowired | ||
| 121 | + private ISIPCommanderForPlatform commanderForPlatform; | ||
| 122 | + | ||
| 122 | 123 | ||
| 123 | @Qualifier("taskExecutor") | 124 | @Qualifier("taskExecutor") |
| 124 | @Autowired | 125 | @Autowired |
| 125 | private ThreadPoolTaskExecutor taskExecutor; | 126 | private ThreadPoolTaskExecutor taskExecutor; |
| 126 | 127 | ||
| 128 | + @Autowired | ||
| 129 | + private RedisGbPlayMsgListener redisGbPlayMsgListener; | ||
| 130 | + | ||
| 131 | + @Autowired | ||
| 132 | + private ZlmHttpHookSubscribe hookSubscribe; | ||
| 133 | + | ||
| 127 | 134 | ||
| 128 | @Override | 135 | @Override |
| 129 | public void play(MediaServerItem mediaServerItem, String deviceId, String channelId, | 136 | public void play(MediaServerItem mediaServerItem, String deviceId, String channelId, |
| @@ -1024,8 +1031,20 @@ public class PlayServiceImpl implements IPlayService { | @@ -1024,8 +1031,20 @@ public class PlayServiceImpl implements IPlayService { | ||
| 1024 | return false; | 1031 | return false; |
| 1025 | } | 1032 | } |
| 1026 | // 查询通道使用状态 | 1033 | // 查询通道使用状态 |
| 1027 | - if (audioBroadcastInUse(device, channelId)) { | ||
| 1028 | - return false; | 1034 | + if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) { |
| 1035 | + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); | ||
| 1036 | + if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) { | ||
| 1037 | + // 查询流是否存在,不存在则认为是异常状态 | ||
| 1038 | + MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | ||
| 1039 | + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStreamId()); | ||
| 1040 | + if (streamReady) { | ||
| 1041 | + logger.warn("语音广播已经开启: {}", channelId); | ||
| 1042 | + event.call("语音广播已经开启"); | ||
| 1043 | + return; | ||
| 1044 | + } else { | ||
| 1045 | + stopAudioBroadcast(device.getDeviceId(), channelId); | ||
| 1046 | + } | ||
| 1047 | + } | ||
| 1029 | } | 1048 | } |
| 1030 | 1049 | ||
| 1031 | // 发送通知 | 1050 | // 发送通知 |
| @@ -1063,28 +1082,31 @@ public class PlayServiceImpl implements IPlayService { | @@ -1063,28 +1082,31 @@ public class PlayServiceImpl implements IPlayService { | ||
| 1063 | 1082 | ||
| 1064 | @Override | 1083 | @Override |
| 1065 | public void stopAudioBroadcast(String deviceId, String channelId) { | 1084 | public void stopAudioBroadcast(String deviceId, String channelId) { |
| 1066 | - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(deviceId, channelId); | ||
| 1067 | - if (audioBroadcastCatch != null) { | 1085 | + List<AudioBroadcastCatch> audioBroadcastCatchList = new ArrayList<>(); |
| 1086 | + if (channelId == null) { | ||
| 1087 | + audioBroadcastCatchList.addAll(audioBroadcastManager.get(deviceId)); | ||
| 1088 | + }else { | ||
| 1089 | + audioBroadcastCatchList.add(audioBroadcastManager.get(deviceId, channelId)); | ||
| 1090 | + } | ||
| 1091 | + if (audioBroadcastCatchList.size() > 0) { | ||
| 1092 | + for (AudioBroadcastCatch audioBroadcastCatch : audioBroadcastCatchList) { | ||
| 1093 | + Device device = deviceService.getDevice(deviceId); | ||
| 1094 | + if (device == null || audioBroadcastCatch == null ) { | ||
| 1095 | + return; | ||
| 1096 | + } | ||
| 1097 | + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(deviceId, audioBroadcastCatch.getChannelId(), null, null); | ||
| 1098 | + if (sendRtpItem != null) { | ||
| 1099 | + redisCatchStorage.deleteSendRTPServer(deviceId, sendRtpItem.getChannelId(), null, null); | ||
| 1100 | + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | ||
| 1101 | + Map<String, Object> param = new HashMap<>(); | ||
| 1102 | + param.put("vhost", "__defaultVhost__"); | ||
| 1103 | + param.put("app", sendRtpItem.getApp()); | ||
| 1104 | + param.put("stream", sendRtpItem.getStreamId()); | ||
| 1105 | + zlmresTfulUtils.stopSendRtp(mediaInfo, param); | ||
| 1106 | + } | ||
| 1068 | 1107 | ||
| 1069 | - Device device = deviceService.getDevice(deviceId); | ||
| 1070 | - if (device == null) { | ||
| 1071 | - return; | ||
| 1072 | - } | ||
| 1073 | - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(deviceId, audioBroadcastCatch.getChannelId(), null, null); | ||
| 1074 | - if (sendRtpItem != null) { | ||
| 1075 | - redisCatchStorage.deleteSendRTPServer(deviceId, sendRtpItem.getChannelId(), null, null); | ||
| 1076 | - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | ||
| 1077 | - Map<String, Object> param = new HashMap<>(); | ||
| 1078 | - param.put("vhost", "__defaultVhost__"); | ||
| 1079 | - param.put("app", sendRtpItem.getApp()); | ||
| 1080 | - param.put("stream", sendRtpItem.getStreamId()); | ||
| 1081 | - zlmresTfulUtils.stopSendRtp(mediaInfo, param); | ||
| 1082 | - } | ||
| 1083 | - if (audioBroadcastCatch.isFromPlatform()) { | ||
| 1084 | - // TODO 向上级发送BYE结束语音喊话 | 1108 | + audioBroadcastManager.del(deviceId, channelId); |
| 1085 | } | 1109 | } |
| 1086 | - | ||
| 1087 | - audioBroadcastManager.del(deviceId, channelId); | ||
| 1088 | } | 1110 | } |
| 1089 | } | 1111 | } |
| 1090 | 1112 | ||
| @@ -1187,4 +1209,100 @@ public class PlayServiceImpl implements IPlayService { | @@ -1187,4 +1209,100 @@ public class PlayServiceImpl implements IPlayService { | ||
| 1187 | Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); | 1209 | Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); |
| 1188 | cmder.playResumeCmd(device, streamInfo); | 1210 | cmder.playResumeCmd(device, streamInfo); |
| 1189 | } | 1211 | } |
| 1212 | + | ||
| 1213 | + @Override | ||
| 1214 | + public void startPushStream(SendRtpItem sendRtpItem, SIPResponse sipResponse, ParentPlatform platform, CallIdHeader callIdHeader) { | ||
| 1215 | + | ||
| 1216 | + // 开始发流 | ||
| 1217 | + // 取消设置的超时任务 | ||
| 1218 | +// String channelId = request.getCallIdHeader().getCallId(); | ||
| 1219 | + | ||
| 1220 | + String is_Udp = sendRtpItem.isTcp() ? "0" : "1"; | ||
| 1221 | + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | ||
| 1222 | + logger.info("收到ACK,rtp/{}开始向上级推流, 目标={}:{},SSRC={}, RTCP={}", sendRtpItem.getStreamId(), | ||
| 1223 | + sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp()); | ||
| 1224 | + Map<String, Object> param = new HashMap<>(12); | ||
| 1225 | + param.put("vhost","__defaultVhost__"); | ||
| 1226 | + param.put("app",sendRtpItem.getApp()); | ||
| 1227 | + param.put("stream",sendRtpItem.getStreamId()); | ||
| 1228 | + param.put("ssrc", sendRtpItem.getSsrc()); | ||
| 1229 | + param.put("src_port", sendRtpItem.getLocalPort()); | ||
| 1230 | + param.put("pt", sendRtpItem.getPt()); | ||
| 1231 | + param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0"); | ||
| 1232 | + param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0"); | ||
| 1233 | + param.put("is_udp", is_Udp); | ||
| 1234 | + if (!sendRtpItem.isTcp()) { | ||
| 1235 | + // udp模式下开启rtcp保活 | ||
| 1236 | + param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0"); | ||
| 1237 | + } | ||
| 1238 | + | ||
| 1239 | + if (mediaInfo == null) { | ||
| 1240 | + RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance( | ||
| 1241 | + sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStreamId(), | ||
| 1242 | + sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(), | ||
| 1243 | + sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio()); | ||
| 1244 | + redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, json -> { | ||
| 1245 | + startSendRtpStreamHand(sendRtpItem, platform, json, param, callIdHeader); | ||
| 1246 | + }); | ||
| 1247 | + } else { | ||
| 1248 | + // 如果是非严格模式,需要关闭端口占用 | ||
| 1249 | + JSONObject startSendRtpStreamResult = null; | ||
| 1250 | + if (sendRtpItem.getLocalPort() != 0) { | ||
| 1251 | + HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(sendRtpItem.getSsrc(), null, mediaInfo.getId()); | ||
| 1252 | + hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout); | ||
| 1253 | + if (zlmrtpServerFactory.releasePort(mediaInfo, sendRtpItem.getSsrc())) { | ||
| 1254 | + if (sendRtpItem.isTcpActive()) { | ||
| 1255 | + startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, param); | ||
| 1256 | + }else { | ||
| 1257 | + param.put("dst_url", sendRtpItem.getIp()); | ||
| 1258 | + param.put("dst_port", sendRtpItem.getPort()); | ||
| 1259 | + startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); | ||
| 1260 | + } | ||
| 1261 | + } | ||
| 1262 | + }else { | ||
| 1263 | + if (sendRtpItem.isTcpActive()) { | ||
| 1264 | + startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, param); | ||
| 1265 | + }else { | ||
| 1266 | + param.put("dst_url", sendRtpItem.getIp()); | ||
| 1267 | + param.put("dst_port", sendRtpItem.getPort()); | ||
| 1268 | + startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); | ||
| 1269 | + } | ||
| 1270 | + } | ||
| 1271 | + if (startSendRtpStreamResult != null) { | ||
| 1272 | + startSendRtpStreamHand(sendRtpItem, platform, startSendRtpStreamResult, param, callIdHeader); | ||
| 1273 | + } | ||
| 1274 | + } | ||
| 1275 | + } | ||
| 1276 | + | ||
| 1277 | + @Override | ||
| 1278 | + public void startSendRtpStreamHand(SendRtpItem sendRtpItem, ParentPlatform parentPlatform, | ||
| 1279 | + JSONObject jsonObject, Map<String, Object> param, CallIdHeader callIdHeader) { | ||
| 1280 | + if (jsonObject == null) { | ||
| 1281 | + logger.error("RTP推流失败: 请检查ZLM服务"); | ||
| 1282 | + } else if (jsonObject.getInteger("code") == 0) { | ||
| 1283 | + logger.info("调用ZLM推流接口, 结果: {}", jsonObject); | ||
| 1284 | + logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); | ||
| 1285 | + } else { | ||
| 1286 | + logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param)); | ||
| 1287 | + if (sendRtpItem.isOnlyAudio()) { | ||
| 1288 | + Device device = deviceService.getDevice(sendRtpItem.getDeviceId()); | ||
| 1289 | + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); | ||
| 1290 | + if (audioBroadcastCatch != null) { | ||
| 1291 | + try { | ||
| 1292 | + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null); | ||
| 1293 | + } catch (SipException | ParseException | InvalidArgumentException | | ||
| 1294 | + SsrcTransactionNotFoundException e) { | ||
| 1295 | + logger.error("[命令发送失败] 停止语音对讲: {}", e.getMessage()); | ||
| 1296 | + } | ||
| 1297 | + } | ||
| 1298 | + }else { | ||
| 1299 | + // 向上级平台 | ||
| 1300 | + try { | ||
| 1301 | + commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId()); | ||
| 1302 | + } catch (SipException | InvalidArgumentException | ParseException e) { | ||
| 1303 | + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); | ||
| 1304 | + } | ||
| 1305 | + } | ||
| 1306 | + } | ||
| 1307 | + } | ||
| 1190 | } | 1308 | } |
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
| @@ -6,7 +6,6 @@ import com.genersoft.iot.vmp.gb28181.bean.*; | @@ -6,7 +6,6 @@ import com.genersoft.iot.vmp.gb28181.bean.*; | ||
| 6 | import com.genersoft.iot.vmp.gb28181.event.EventPublisher; | 6 | import com.genersoft.iot.vmp.gb28181.event.EventPublisher; |
| 7 | import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; | 7 | import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; |
| 8 | import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; | 8 | import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; |
| 9 | -import com.genersoft.iot.vmp.service.IGbStreamService; | ||
| 10 | import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; | 9 | import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; |
| 11 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 10 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 12 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; | 11 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| @@ -90,12 +89,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { | @@ -90,12 +89,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { | ||
| 90 | @Autowired | 89 | @Autowired |
| 91 | private PlatformGbStreamMapper platformGbStreamMapper; | 90 | private PlatformGbStreamMapper platformGbStreamMapper; |
| 92 | 91 | ||
| 93 | - @Autowired | ||
| 94 | - private IGbStreamService gbStreamService; | ||
| 95 | - | ||
| 96 | - @Autowired | ||
| 97 | - private ParentPlatformMapper parentPlatformMapper; | ||
| 98 | - | ||
| 99 | /** | 92 | /** |
| 100 | * 根据设备ID判断设备是否存在 | 93 | * 根据设备ID判断设备是否存在 |
| 101 | * | 94 | * |
src/main/resources/all-application.yml
| @@ -197,6 +197,8 @@ user-settings: | @@ -197,6 +197,8 @@ user-settings: | ||
| 197 | sync-channel-on-device-online: false | 197 | sync-channel-on-device-online: false |
| 198 | # 国标级联语音喊话发流模式 * UDP:udp传输 TCP-ACTIVE:tcp主动模式 TCP-PASSIVE:tcp被动模式 | 198 | # 国标级联语音喊话发流模式 * UDP:udp传输 TCP-ACTIVE:tcp主动模式 TCP-PASSIVE:tcp被动模式 |
| 199 | broadcast-for-platform: UDP | 199 | broadcast-for-platform: UDP |
| 200 | + # 收到ack消息后开始发流,默认false, 回复200ok后直接开始发流 | ||
| 201 | + push-stream-after-ack: false | ||
| 200 | 202 | ||
| 201 | # 关闭在线文档(生产环境建议关闭) | 203 | # 关闭在线文档(生产环境建议关闭) |
| 202 | springdoc: | 204 | springdoc: |