Commit 6fa5b37b962b05b3aa4b7bf019eb47c4cdbb5738

Authored by 648540858
2 parents 4b827f38 16f3b055

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
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
... ... @@ -69,6 +69,7 @@ public class VideoManagerConstants {
69 69 public static final String SYSTEM_INFO_NET_PREFIX = "VMP_SYSTEM_INFO_NET_";
70 70  
71 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 43  
44 44 private Boolean syncChannelOnDeviceOnline = Boolean.FALSE;
45 45  
  46 + private Boolean pushStreamAfterAck = Boolean.FALSE;
  47 +
46 48 private String serverId = "000000";
47 49  
48 50 private String thirdPartyGBIdReg = "[\\s\\S]*";
... ... @@ -206,4 +208,12 @@ public class UserSetting {
206 208 public void setBroadcastForPlatform(String broadcastForPlatform) {
207 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 83  
84 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 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request;
2 2  
3   -import com.genersoft.iot.vmp.conf.SipConfig;
4 3 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
5   -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
6 4 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
7 5 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
8   -import gov.nist.javax.sip.SipProviderImpl;
9 6 import gov.nist.javax.sip.message.SIPRequest;
10 7 import gov.nist.javax.sip.message.SIPResponse;
11   -import gov.nist.javax.sip.stack.SIPServerTransactionImpl;
12 8 import org.apache.commons.lang3.ArrayUtils;
13 9 import org.dom4j.Document;
14 10 import org.dom4j.DocumentException;
... ... @@ -17,14 +13,14 @@ import org.dom4j.io.SAXReader;
17 13 import org.slf4j.Logger;
18 14 import org.slf4j.LoggerFactory;
19 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 17 import javax.sip.*;
24 18 import javax.sip.address.Address;
25 19 import javax.sip.address.AddressFactory;
26 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 24 import javax.sip.message.MessageFactory;
29 25 import javax.sip.message.Request;
30 26 import javax.sip.message.Response;
... ... @@ -157,7 +153,10 @@ public abstract class SIPRequestProcessorParent {
157 153 responseAckExtraParam.content = sdp;
158 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 189 reader.setEncoding(charset);
191 190 // 对海康出现的未转义字符做处理。
192 191 String[] destStrArray = new String[]{"&lt;","&gt;","&amp;","&apos;","&quot;"};
193   - char despChar = '&'; // 或许可扩展兼容其他字符
  192 + // 或许可扩展兼容其他字符
  193 + char despChar = '&';
194 194 byte destBye = (byte) despChar;
195 195 List<Byte> result = new ArrayList<>();
196 196 byte[] rawContent = request.getRawContent();
... ... @@ -220,4 +220,5 @@ public abstract class SIPRequestProcessorParent {
220 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 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
2 2  
3   -import com.alibaba.fastjson2.JSON;
4 3 import com.alibaba.fastjson2.JSONObject;
5 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 6 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
10 7 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
11   -import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
12 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 9 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
16 10 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
17 11 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
18 12 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
19 13 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
20   -import com.genersoft.iot.vmp.service.IDeviceService;
21 14 import com.genersoft.iot.vmp.service.IMediaServerService;
  15 +import com.genersoft.iot.vmp.service.IPlayService;
22 16 import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
23 17 import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
24 18 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
... ... @@ -29,15 +23,15 @@ import org.springframework.beans.factory.InitializingBean;
29 23 import org.springframework.beans.factory.annotation.Autowired;
30 24 import org.springframework.stereotype.Component;
31 25  
32   -import javax.sip.InvalidArgumentException;
33 26 import javax.sip.RequestEvent;
34   -import javax.sip.SipException;
35 27 import javax.sip.address.SipURI;
36 28 import javax.sip.header.CallIdHeader;
37 29 import javax.sip.header.FromHeader;
38 30 import javax.sip.header.HeaderAddress;
39 31 import javax.sip.header.ToHeader;
40 32 import java.text.ParseException;
  33 +import java.util.HashMap;
  34 +import java.util.Map;
41 35  
42 36 /**
43 37 * SIP命令类型: ACK请求
... ... @@ -46,7 +40,7 @@ import java.text.ParseException;
46 40 @Component
47 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 44 private final String method = "ACK";
51 45  
52 46 @Autowired
... ... @@ -74,31 +68,20 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
74 68 private IMediaServerService mediaServerService;
75 69  
76 70 @Autowired
77   - private ZlmHttpHookSubscribe subscribe;
78   -
79   - @Autowired
80 71 private DynamicTask dynamicTask;
81 72  
82 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 76 @Autowired
92   - private AudioBroadcastManager audioBroadcastManager;
  77 + private UserSetting userSetting;
93 78  
94 79 @Autowired
95   - private RedisGbPlayMsgListener redisGbPlayMsgListener;
  80 + private IPlayService playService;
96 81  
97 82  
98 83 /**
99 84 * 处理 ACK请求
100   - *
101   - * @param evt
102 85 */
103 86 @Override
104 87 public void process(RequestEvent evt) {
... ... @@ -106,44 +89,45 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
106 89  
107 90 String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
108 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 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 124 if (jsonObject == null) {
141 125 logger.error("RTP推流失败: 请检查ZLM服务");
142 126 } else if (jsonObject.getInteger("code") == 0) {
143 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 129 } else {
146   - logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(sendRtpItem));
  130 + logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param));
147 131 if (sendRtpItem.isOnlyAudio()) {
148 132 Device device = deviceService.getDevice(sendRtpItem.getDeviceId());
149 133 AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
... ... @@ -152,17 +136,12 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
152 136 cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
153 137 } catch (SipException | ParseException | InvalidArgumentException |
154 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 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
2 2  
3 3 import com.alibaba.fastjson2.JSONObject;
  4 +import com.genersoft.iot.vmp.common.VideoManagerConstants;
4 5 import com.genersoft.iot.vmp.conf.DynamicTask;
5 6 import com.genersoft.iot.vmp.conf.SipConfig;
6 7 import com.genersoft.iot.vmp.conf.UserSetting;
... ... @@ -439,18 +440,23 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
439 440  
440 441 try {
441 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 460 } catch (SipException e) {
455 461 e.printStackTrace();
456 462 } catch (InvalidArgumentException e) {
... ... @@ -878,7 +884,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
878 884 content.append("f=\r\n");
879 885  
880 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 892 } catch (SipException e) {
883 893 e.printStackTrace();
884 894 } catch (InvalidArgumentException e) {
... ... @@ -905,11 +915,14 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
905 915 }
906 916 if (device != null) {
907 917 logger.info("收到设备" + requesterId + "的语音广播Invite请求");
908   -
  918 + String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId() + audioBroadcastCatch.getChannelId();
  919 + dynamicTask.stop(key);
909 920 try {
910 921 responseAck(request, Response.TRYING);
911 922 } catch (SipException | InvalidArgumentException | ParseException e) {
912 923 logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage());
  924 + playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId());
  925 + return;
913 926 }
914 927 String contentString = new String(request.getRawContent());
915 928 // jainSip不支持y=字段, 移除移除以解析。
... ... @@ -964,11 +977,14 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
964 977 responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
965 978 } catch (SipException | InvalidArgumentException | ParseException e) {
966 979 logger.error("[命令发送失败] invite 不支持的媒体格式: {}", e.getMessage());
  980 + playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId());
  981 + return;
967 982 }
968 983 return;
969 984 }
970 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 989 MediaServerItem mediaServerItem = playService.getNewMediaServerItem(device);
974 990 if (mediaServerItem == null) {
... ... @@ -977,6 +993,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
977 993 responseAck(request, Response.BUSY_HERE);
978 994 } catch (SipException | InvalidArgumentException | ParseException e) {
979 995 logger.error("[命令发送失败] invite 未找到可用的zlm: {}", e.getMessage());
  996 + playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId());
980 997 }
981 998 return;
982 999 }
... ... @@ -990,13 +1007,12 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
990 1007 responseAck(request, Response.BUSY_HERE);
991 1008 } catch (SipException | InvalidArgumentException | ParseException e) {
992 1009 logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage());
  1010 + playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId());
  1011 + return;
993 1012 }
994 1013 return;
995 1014 }
996   - sendRtpItem.setTcp(mediaTransmissionTCP);
997   - if (tcpActive != null) {
998   - sendRtpItem.setTcpActive(tcpActive);
999   - }
  1015 +
1000 1016 String app = "broadcast";
1001 1017 String stream = device.getDeviceId() + "_" + audioBroadcastCatch.getChannelId();
1002 1018  
... ... @@ -1011,6 +1027,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
1011 1027 sendRtpItem.setUsePs(false);
1012 1028 sendRtpItem.setRtcp(false);
1013 1029 sendRtpItem.setOnlyAudio(true);
  1030 + sendRtpItem.setTcp(mediaTransmissionTCP);
  1031 + if (tcpActive != null) {
  1032 + sendRtpItem.setTcpActive(tcpActive);
  1033 + }
  1034 +
1014 1035 redisCatchStorage.updateSendRTPSever(sendRtpItem);
1015 1036  
1016 1037  
... ... @@ -1023,11 +1044,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
1023 1044 responseAck(request, Response.GONE);
1024 1045 } catch (SipException | InvalidArgumentException | ParseException e) {
1025 1046 logger.error("[命令发送失败] 语音通话 回复410失败, {}", e.getMessage());
  1047 + return;
1026 1048 }
1027 1049 playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId());
1028 1050 }
1029 1051 } catch (SdpException e) {
1030 1052 logger.error("[SDP解析异常]", e);
  1053 + playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId());
1031 1054 }
1032 1055 } else {
1033 1056 logger.warn("来自无效设备/平台的请求");
... ... @@ -1084,6 +1107,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
1084 1107 audioBroadcastCatch.setSipTransactionInfoByRequset(sipResponse);
1085 1108 audioBroadcastManager.update(audioBroadcastCatch);
1086 1109  
  1110 + // 开启发流,大华在收到200OK后就会开始建立连接
  1111 + if (!userSetting.getPushStreamAfterAck()) {
  1112 + playService.startPushStream(sendRtpItem, sipResponse, parentPlatform, request.getCallIdHeader());
  1113 + }
  1114 +
1087 1115 } catch (SipException | InvalidArgumentException | ParseException | SdpParseException e) {
1088 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 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 5 import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch;
4 6 import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatchStatus;
5 7 import com.genersoft.iot.vmp.gb28181.bean.Device;
... ... @@ -9,6 +11,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
9 11 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
10 12 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
11 13 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
  14 +import com.genersoft.iot.vmp.service.IPlayService;
12 15 import gov.nist.javax.sip.message.SIPRequest;
13 16 import org.dom4j.Element;
14 17 import org.slf4j.Logger;
... ... @@ -35,11 +38,14 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i
35 38 private ResponseMessageHandler responseMessageHandler;
36 39  
37 40 @Autowired
38   - private DeferredResultHolder deferredResultHolder;
  41 + private DynamicTask dynamicTask;
39 42  
40 43 @Autowired
41 44 private AudioBroadcastManager audioBroadcastManager;
42 45  
  46 + @Autowired
  47 + private IPlayService playService;
  48 +
43 49 @Override
44 50 public void afterPropertiesSet() throws Exception {
45 51 responseMessageHandler.addHandler(cmdType, this);
... ... @@ -47,6 +53,8 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i
47 53  
48 54 @Override
49 55 public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
  56 +
  57 + SIPRequest request = (SIPRequest) evt.getRequest();
50 58 try {
51 59 String channelId = getText(rootElement, "DeviceID");
52 60 if (!audioBroadcastManager.exit(device.getDeviceId(), channelId)) {
... ... @@ -55,12 +63,23 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i
55 63 return;
56 64 }
57 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 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 83 } catch (ParseException | SipException | InvalidArgumentException e) {
65 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 35 public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
36 36  
37 37 private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class);
  38 +
38 39 private final String cmdType = "Catalog";
39 40  
40 41 @Autowired
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -323,7 +323,7 @@ public class ZLMHttpHookListener {
323 323 });
324 324  
325 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 327 if (param.isRegist()) {
328 328 mediaServerService.addCount(param.getMediaServerId());
329 329 }else {
... ... @@ -383,10 +383,10 @@ public class ZLMHttpHookListener {
383 383 }
384 384  
385 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 391 }else {
392 392 logger.info("[语音喊话] 推流格式有误, 格式为: broadcast/设备编号_通道编号 ");
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
... ... @@ -36,7 +36,7 @@ public class ZLMRESTfulUtils {
36 36 // 设置连接超时时间
37 37 httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS);
38 38 // 设置读取超时时间
39   - httpClientBuilder.readTimeout(5,TimeUnit.SECONDS);
  39 + httpClientBuilder.readTimeout(15,TimeUnit.SECONDS);
40 40 // 设置连接池
41 41 httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES));
42 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 3 import com.alibaba.fastjson2.JSONObject;
4 4 import com.genersoft.iot.vmp.common.StreamInfo;
5 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 7 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
10 8 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
11 9 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
... ... @@ -15,11 +13,14 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo;
15 13 import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
16 14 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
17 15 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
  16 +import gov.nist.javax.sip.message.SIPResponse;
18 17 import org.springframework.web.context.request.async.DeferredResult;
19 18  
20 19 import javax.sip.InvalidArgumentException;
21 20 import javax.sip.SipException;
  21 +import javax.sip.header.CallIdHeader;
22 22 import java.text.ParseException;
  23 +import java.util.Map;
23 24  
24 25 /**
25 26 * 点播处理
... ... @@ -64,4 +65,9 @@ public interface IPlayService {
64 65 void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException;
65 66  
66 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 3 import com.genersoft.iot.vmp.conf.DynamicTask;
4 4 import com.genersoft.iot.vmp.conf.UserSetting;
5 5 import com.genersoft.iot.vmp.gb28181.bean.*;
  6 +import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
6 7 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
7 8 import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
8 9 import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;
9 10 import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
10 11 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
11 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 15 import com.genersoft.iot.vmp.service.IDeviceChannelService;
13 16 import com.genersoft.iot.vmp.service.IDeviceService;
14 17 import com.genersoft.iot.vmp.service.IMediaServerService;
... ... @@ -32,9 +35,7 @@ import javax.sip.InvalidArgumentException;
32 35 import javax.sip.SipException;
33 36 import java.text.ParseException;
34 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 39 import java.util.concurrent.TimeUnit;
39 40  
40 41 /**
... ... @@ -89,6 +90,12 @@ public class DeviceServiceImpl implements IDeviceService {
89 90 @Autowired
90 91 private IMediaServerService mediaServerService;
91 92  
  93 + @Autowired
  94 + private AudioBroadcastManager audioBroadcastManager;
  95 +
  96 + @Autowired
  97 + private ZLMRESTfulUtils zlmresTfulUtils;
  98 +
92 99 @Override
93 100 public void online(Device device) {
94 101 logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort());
... ... @@ -183,6 +190,23 @@ public class DeviceServiceImpl implements IDeviceService {
183 190 // 移除订阅
184 191 removeCatalogSubscribe(device);
185 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 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 24 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
25 25 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
26 26 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
  27 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRtpServerTimeout;
27 28 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
28 29 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
29 30 import com.genersoft.iot.vmp.service.IDeviceService;
30 31 import com.genersoft.iot.vmp.service.IMediaServerService;
31 32 import com.genersoft.iot.vmp.service.IMediaService;
32 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 36 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
38 37 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
39 38 import com.genersoft.iot.vmp.utils.DateUtil;
... ... @@ -42,6 +41,7 @@ import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
42 41 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
43 42 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
44 43 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
  44 +import gov.nist.javax.sip.message.SIPResponse;
45 45 import org.slf4j.Logger;
46 46 import org.slf4j.LoggerFactory;
47 47 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -54,13 +54,11 @@ import org.springframework.web.context.request.async.DeferredResult;
54 54 import javax.sip.InvalidArgumentException;
55 55 import javax.sip.ResponseEvent;
56 56 import javax.sip.SipException;
  57 +import javax.sip.header.CallIdHeader;
57 58 import java.math.BigDecimal;
58 59 import java.math.RoundingMode;
59 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 63 @SuppressWarnings(value = {"rawtypes", "unchecked"})
66 64 @Service
... ... @@ -119,11 +117,20 @@ public class PlayServiceImpl implements IPlayService {
119 117 @Autowired
120 118 private ZlmHttpHookSubscribe subscribe;
121 119  
  120 + @Autowired
  121 + private ISIPCommanderForPlatform commanderForPlatform;
  122 +
122 123  
123 124 @Qualifier("taskExecutor")
124 125 @Autowired
125 126 private ThreadPoolTaskExecutor taskExecutor;
126 127  
  128 + @Autowired
  129 + private RedisGbPlayMsgListener redisGbPlayMsgListener;
  130 +
  131 + @Autowired
  132 + private ZlmHttpHookSubscribe hookSubscribe;
  133 +
127 134  
128 135 @Override
129 136 public void play(MediaServerItem mediaServerItem, String deviceId, String channelId,
... ... @@ -1024,8 +1031,20 @@ public class PlayServiceImpl implements IPlayService {
1024 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 1082  
1064 1083 @Override
1065 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 1209 Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
1188 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 6 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
7 7 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
8 8 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
9   -import com.genersoft.iot.vmp.service.IGbStreamService;
10 9 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
11 10 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
12 11 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
... ... @@ -90,12 +89,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
90 89 @Autowired
91 90 private PlatformGbStreamMapper platformGbStreamMapper;
92 91  
93   - @Autowired
94   - private IGbStreamService gbStreamService;
95   -
96   - @Autowired
97   - private ParentPlatformMapper parentPlatformMapper;
98   -
99 92 /**
100 93 * 根据设备ID判断设备是否存在
101 94 *
... ...
src/main/resources/all-application.yml
... ... @@ -197,6 +197,8 @@ user-settings:
197 197 sync-channel-on-device-online: false
198 198 # 国标级联语音喊话发流模式 * UDP:udp传输 TCP-ACTIVE:tcp主动模式 TCP-PASSIVE:tcp被动模式
199 199 broadcast-for-platform: UDP
  200 + # 收到ack消息后开始发流,默认false, 回复200ok后直接开始发流
  201 + push-stream-after-ack: false
200 202  
201 203 # 关闭在线文档(生产环境建议关闭)
202 204 springdoc:
... ...