Commit a77628e8759901abc3219412fc2c4aced940db28

Authored by 648540858
1 parent ed035b74

优化端口预占用,防止占用无法释放

src/main/java/com/genersoft/iot/vmp/gb28181/bean/Gb28181Sdp.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.bean;
  2 +
  3 +import javax.sdp.SessionDescription;
  4 +
  5 +/**
  6 + * 28181 的SDP解析器
  7 + */
  8 +public class Gb28181Sdp {
  9 + private SessionDescription baseSdb;
  10 + private String ssrc;
  11 +
  12 + private String mediaDescription;
  13 +
  14 + public static Gb28181Sdp getInstance(SessionDescription baseSdb, String ssrc, String mediaDescription) {
  15 + Gb28181Sdp gb28181Sdp = new Gb28181Sdp();
  16 + gb28181Sdp.setBaseSdb(baseSdb);
  17 + gb28181Sdp.setSsrc(ssrc);
  18 + gb28181Sdp.setMediaDescription(mediaDescription);
  19 + return gb28181Sdp;
  20 + }
  21 +
  22 +
  23 + public SessionDescription getBaseSdb() {
  24 + return baseSdb;
  25 + }
  26 +
  27 + public void setBaseSdb(SessionDescription baseSdb) {
  28 + this.baseSdb = baseSdb;
  29 + }
  30 +
  31 + public String getSsrc() {
  32 + return ssrc;
  33 + }
  34 +
  35 + public void setSsrc(String ssrc) {
  36 + this.ssrc = ssrc;
  37 + }
  38 +
  39 + public String getMediaDescription() {
  40 + return mediaDescription;
  41 + }
  42 +
  43 + public void setMediaDescription(String mediaDescription) {
  44 + this.mediaDescription = mediaDescription;
  45 + }
  46 +}
0 \ No newline at end of file 47 \ No newline at end of file
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -241,21 +241,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -241,21 +241,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
241 // 解析sdp消息, 使用jainsip 自带的sdp解析方式 241 // 解析sdp消息, 使用jainsip 自带的sdp解析方式
242 String contentString = new String(request.getRawContent()); 242 String contentString = new String(request.getRawContent());
243 243
244 - // jainSip不支持y=字段, 移除以解析。  
245 - int ssrcIndex = contentString.indexOf("y=");  
246 - // 检查是否有y字段  
247 - String ssrcDefault = "0000000000";  
248 - String ssrc;  
249 - SessionDescription sdp;  
250 - if (ssrcIndex >= 0) {  
251 - //ssrc规定长度为10个字节,不取余下长度以避免后续还有“f=”字段  
252 - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);  
253 - String substring = contentString.substring(0, contentString.indexOf("y="));  
254 - sdp = SdpFactory.getInstance().createSessionDescription(substring);  
255 - } else {  
256 - ssrc = ssrcDefault;  
257 - sdp = SdpFactory.getInstance().createSessionDescription(contentString);  
258 - } 244 + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString);
  245 + SessionDescription sdp = gb28181Sdp.getBaseSdb();
259 String sessionName = sdp.getSessionName().getValue(); 246 String sessionName = sdp.getSessionName().getValue();
260 247
261 Long startTime = null; 248 Long startTime = null;
@@ -317,7 +304,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -317,7 +304,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
317 String username = sdp.getOrigin().getUsername(); 304 String username = sdp.getOrigin().getUsername();
318 String addressStr = sdp.getConnection().getAddress(); 305 String addressStr = sdp.getConnection().getAddress();
319 306
320 - logger.info("[上级点播]用户:{}, 通道:{}, 地址:{}:{}, ssrc:{}", username, channelId, addressStr, port, ssrc); 307 +
321 Device device = null; 308 Device device = null;
322 // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标 309 // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标
323 if (channel != null) { 310 if (channel != null) {
@@ -341,8 +328,30 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -341,8 +328,30 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
341 } 328 }
342 return; 329 return;
343 } 330 }
  331 +
  332 + String ssrc;
  333 + if (gb28181Sdp.getSsrc() == null) {
  334 + // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
  335 + ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
  336 + logger.warn("[上级Invite] {} 平台:{}, 通道:{}, 缺少 ssrc,补充为: {}", sessionName, username, channelId, ssrc);
  337 + }else {
  338 + ssrc = gb28181Sdp.getSsrc();
  339 + }
  340 + String streamTypeStr = null;
  341 + if (mediaTransmissionTCP) {
  342 + if (tcpActive) {
  343 + streamTypeStr = "TCP-ACTIVE";
  344 + }else {
  345 + streamTypeStr = "TCP-PASSIVE";
  346 + }
  347 + }else {
  348 + streamTypeStr = "UDP";
  349 + }
  350 + logger.info("[上级Invite] {}, 平台:{}, 通道:{}, 收流地址:{}:{},收流方式:{}, ssrc:{}", sessionName, username, channelId, addressStr, port, streamTypeStr, ssrc);
344 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, 351 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
345 - device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp()); 352 + device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback -> {
  353 + return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null;
  354 + });
346 355
347 if (tcpActive != null) { 356 if (tcpActive != null) {
348 sendRtpItem.setTcpActive(tcpActive); 357 sendRtpItem.setTcpActive(tcpActive);
@@ -469,7 +478,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -469,7 +478,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
469 SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam()); 478 SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam());
470 logger.info(JSONObject.toJSONString(ssrcInfo)); 479 logger.info(JSONObject.toJSONString(ssrcInfo));
471 sendRtpItem.setStreamId(ssrcInfo.getStream()); 480 sendRtpItem.setStreamId(ssrcInfo.getStream());
472 - sendRtpItem.setSsrc(ssrc.equals(ssrcDefault) ? ssrcInfo.getSsrc() : ssrc); 481 + sendRtpItem.setSsrc(ssrc);
473 482
474 // 写入redis, 超时时回复 483 // 写入redis, 超时时回复
475 redisCatchStorage.updateSendRTPSever(sendRtpItem); 484 redisCatchStorage.updateSendRTPSever(sendRtpItem);
@@ -480,12 +489,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -480,12 +489,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
480 }); 489 });
481 } else { 490 } else {
482 // 当前系统作为下级平台使用,当上级平台点播时不携带ssrc时,并且设备在当前系统中已经点播了。这个时候需要重新给生成一个ssrc,不使用默认的"0000000000"。 491 // 当前系统作为下级平台使用,当上级平台点播时不携带ssrc时,并且设备在当前系统中已经点播了。这个时候需要重新给生成一个ssrc,不使用默认的"0000000000"。
483 - if (ssrc.equals(ssrcDefault)) {  
484 - ssrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId());  
485 - ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc);  
486 - sendRtpItem.setSsrc(ssrc);  
487 - }  
488 - 492 + sendRtpItem.setSsrc(ssrc);
489 sendRtpItem.setStreamId(playTransaction.getStream()); 493 sendRtpItem.setStreamId(playTransaction.getStream());
490 // 写入redis, 超时时回复 494 // 写入redis, 超时时回复
491 redisCatchStorage.updateSendRTPSever(sendRtpItem); 495 redisCatchStorage.updateSendRTPSever(sendRtpItem);
@@ -496,11 +500,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -496,11 +500,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
496 } 500 }
497 } 501 }
498 } else if (gbStream != null) { 502 } else if (gbStream != null) {
499 - if(ssrc.equals(ssrcDefault))  
500 - {  
501 - ssrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId());  
502 - ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc); 503 +
  504 + String ssrc;
  505 + if (gb28181Sdp.getSsrc() == null) {
  506 + // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
  507 + ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
  508 + }else {
  509 + ssrc = gb28181Sdp.getSsrc();
503 } 510 }
  511 +
504 if("push".equals(gbStream.getStreamType())) { 512 if("push".equals(gbStream.getStreamType())) {
505 if (streamPushItem != null && streamPushItem.isPushIng()) { 513 if (streamPushItem != null && streamPushItem.isPushIng()) {
506 // 推流状态 514 // 推流状态
@@ -545,7 +553,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -545,7 +553,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
545 if (streamReady) { 553 if (streamReady) {
546 // 自平台内容 554 // 自平台内容
547 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, 555 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
548 - gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); 556 + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback ->{
  557 + return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null;
  558 + });
549 559
550 if (sendRtpItem == null) { 560 if (sendRtpItem == null) {
551 logger.warn("服务器端口资源不足"); 561 logger.warn("服务器端口资源不足");
@@ -584,7 +594,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -584,7 +594,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
584 if (streamReady) { 594 if (streamReady) {
585 // 自平台内容 595 // 自平台内容
586 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, 596 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
587 - gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); 597 + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback ->{
  598 + return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null;
  599 + });
588 600
589 if (sendRtpItem == null) { 601 if (sendRtpItem == null) {
590 logger.warn("服务器端口资源不足"); 602 logger.warn("服务器端口资源不足");
@@ -701,7 +713,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -701,7 +713,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
701 dynamicTask.stop(callIdHeader.getCallId()); 713 dynamicTask.stop(callIdHeader.getCallId());
702 if (serverId.equals(userSetting.getServerId())) { 714 if (serverId.equals(userSetting.getServerId())) {
703 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId, 715 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
704 - app, stream, channelId, mediaTransmissionTCP, platform.isRtcp()); 716 + app, stream, channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback -> {
  717 + return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null;
  718 + });
705 719
706 if (sendRtpItem == null) { 720 if (sendRtpItem == null) {
707 logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足"); 721 logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足");
src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java
1 package com.genersoft.iot.vmp.gb28181.utils; 1 package com.genersoft.iot.vmp.gb28181.utils;
2 2
3 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; 3 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  4 +import com.genersoft.iot.vmp.gb28181.bean.Gb28181Sdp;
4 import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; 5 import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo;
  6 +import com.genersoft.iot.vmp.utils.DateUtil;
5 import com.genersoft.iot.vmp.utils.GitUtil; 7 import com.genersoft.iot.vmp.utils.GitUtil;
6 import gov.nist.javax.sip.address.AddressImpl; 8 import gov.nist.javax.sip.address.AddressImpl;
7 import gov.nist.javax.sip.address.SipUri; 9 import gov.nist.javax.sip.address.SipUri;
8 import gov.nist.javax.sip.header.Subject; 10 import gov.nist.javax.sip.header.Subject;
9 import gov.nist.javax.sip.message.SIPRequest; 11 import gov.nist.javax.sip.message.SIPRequest;
  12 +import org.apache.commons.lang3.RandomStringUtils;
  13 +import org.slf4j.Logger;
  14 +import org.slf4j.LoggerFactory;
10 import org.springframework.util.ObjectUtils; 15 import org.springframework.util.ObjectUtils;
11 16
  17 +import javax.sdp.SdpFactory;
  18 +import javax.sdp.SdpParseException;
  19 +import javax.sdp.SessionDescription;
12 import javax.sip.PeerUnavailableException; 20 import javax.sip.PeerUnavailableException;
13 import javax.sip.SipFactory; 21 import javax.sip.SipFactory;
14 import javax.sip.header.FromHeader; 22 import javax.sip.header.FromHeader;
@@ -16,6 +24,8 @@ import javax.sip.header.Header; @@ -16,6 +24,8 @@ import javax.sip.header.Header;
16 import javax.sip.header.UserAgentHeader; 24 import javax.sip.header.UserAgentHeader;
17 import javax.sip.message.Request; 25 import javax.sip.message.Request;
18 import java.text.ParseException; 26 import java.text.ParseException;
  27 +import java.time.LocalDateTime;
  28 +import java.time.format.DateTimeParseException;
19 import java.util.ArrayList; 29 import java.util.ArrayList;
20 import java.util.List; 30 import java.util.List;
21 import java.util.UUID; 31 import java.util.UUID;
@@ -28,6 +38,8 @@ import java.util.UUID; @@ -28,6 +38,8 @@ import java.util.UUID;
28 */ 38 */
29 public class SipUtils { 39 public class SipUtils {
30 40
  41 + private final static Logger logger = LoggerFactory.getLogger(SipUtils.class);
  42 +
31 public static String getUserIdFromFromHeader(Request request) { 43 public static String getUserIdFromFromHeader(Request request) {
32 FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); 44 FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);
33 return getUserIdFromFromHeader(fromHeader); 45 return getUserIdFromFromHeader(fromHeader);
@@ -51,7 +63,7 @@ public class SipUtils { @@ -51,7 +63,7 @@ public class SipUtils {
51 } 63 }
52 64
53 public static String getNewViaTag() { 65 public static String getNewViaTag() {
54 - return "z9hG4bK" + System.currentTimeMillis(); 66 + return "z9hG4bK" + RandomStringUtils.randomNumeric(10);
55 } 67 }
56 68
57 public static UserAgentHeader createUserAgentHeader(GitUtil gitUtil) throws PeerUnavailableException, ParseException { 69 public static UserAgentHeader createUserAgentHeader(GitUtil gitUtil) throws PeerUnavailableException, ParseException {
@@ -113,6 +125,12 @@ public class SipUtils { @@ -113,6 +125,12 @@ public class SipUtils {
113 strTmp = String.format("%02X", moveSpeed); 125 strTmp = String.format("%02X", moveSpeed);
114 builder.append(strTmp, 0, 2); 126 builder.append(strTmp, 0, 2);
115 builder.append(strTmp, 0, 2); 127 builder.append(strTmp, 0, 2);
  128 +
  129 + //优化zoom低倍速下的变倍速率
  130 + if ((zoomSpeed > 0) && (zoomSpeed <16))
  131 + {
  132 + zoomSpeed = 16;
  133 + }
116 strTmp = String.format("%X", zoomSpeed); 134 strTmp = String.format("%X", zoomSpeed);
117 builder.append(strTmp, 0, 1).append("0"); 135 builder.append(strTmp, 0, 1).append("0");
118 //计算校验码 136 //计算校验码
@@ -183,4 +201,66 @@ public class SipUtils { @@ -183,4 +201,66 @@ public class SipUtils {
183 } 201 }
184 return deviceChannel; 202 return deviceChannel;
185 } 203 }
186 -} 204 +
  205 + public static Gb28181Sdp parseSDP(String sdpStr) throws SdpParseException {
  206 +
  207 + // jainSip不支持y= f=字段, 移除以解析。
  208 + int ssrcIndex = sdpStr.indexOf("y=");
  209 + int mediaDescriptionIndex = sdpStr.indexOf("f=");
  210 + // 检查是否有y字段
  211 + SessionDescription sdp;
  212 + String ssrc = null;
  213 + String mediaDescription = null;
  214 + if (mediaDescriptionIndex == 0 && ssrcIndex == 0) {
  215 + sdp = SdpFactory.getInstance().createSessionDescription(sdpStr);
  216 + }else {
  217 + String lines[] = sdpStr.split("\\r?\\n");
  218 + StringBuilder sdpBuffer = new StringBuilder();
  219 + for (String line : lines) {
  220 + if (line.trim().startsWith("y=")) {
  221 + ssrc = line.substring(2);
  222 + }else if (line.trim().startsWith("f=")) {
  223 + mediaDescription = line.substring(2);
  224 + }else {
  225 + sdpBuffer.append(line.trim()).append("\r\n");
  226 + }
  227 + }
  228 + sdp = SdpFactory.getInstance().createSessionDescription(sdpBuffer.toString());
  229 + }
  230 + return Gb28181Sdp.getInstance(sdp, ssrc, mediaDescription);
  231 + }
  232 +
  233 + public static String getSsrcFromSdp(String sdpStr) {
  234 +
  235 + // jainSip不支持y= f=字段, 移除以解析。
  236 + int ssrcIndex = sdpStr.indexOf("y=");
  237 + if (ssrcIndex == 0) {
  238 + return null;
  239 + }
  240 + String lines[] = sdpStr.split("\\r?\\n");
  241 + for (String line : lines) {
  242 + if (line.trim().startsWith("y=")) {
  243 + return line.substring(2);
  244 + }
  245 + }
  246 + return null;
  247 + }
  248 +
  249 + public static String parseTime(String timeStr) {
  250 + if (ObjectUtils.isEmpty(timeStr)){
  251 + return null;
  252 + }
  253 + LocalDateTime localDateTime;
  254 + try {
  255 + localDateTime = LocalDateTime.parse(timeStr);
  256 + }catch (DateTimeParseException e) {
  257 + try {
  258 + localDateTime = LocalDateTime.parse(timeStr, DateUtil.formatterISO8601);
  259 + }catch (DateTimeParseException e2) {
  260 + logger.error("[格式化时间] 无法格式化时间: {}", timeStr);
  261 + return null;
  262 + }
  263 + }
  264 + return localDateTime.format(DateUtil.formatterISO8601);
  265 + }
  266 +}
187 \ No newline at end of file 267 \ No newline at end of file
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
@@ -219,13 +219,14 @@ public class ZLMRTPServerFactory { @@ -219,13 +219,14 @@ public class ZLMRTPServerFactory {
219 * @param tcp 是否为tcp 219 * @param tcp 是否为tcp
220 * @return SendRtpItem 220 * @return SendRtpItem
221 */ 221 */
222 - public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp, boolean rtcp){ 222 + public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId,
  223 + String deviceId, String channelId, boolean tcp, boolean rtcp, KeepPortCallback callback){
223 224
224 // 默认为随机端口 225 // 默认为随机端口
225 int localPort = 0; 226 int localPort = 0;
226 if (userSetting.getGbSendStreamStrict()) { 227 if (userSetting.getGbSendStreamStrict()) {
227 if (userSetting.getGbSendStreamStrict()) { 228 if (userSetting.getGbSendStreamStrict()) {
228 - localPort = keepPort(serverItem, ssrc); 229 + localPort = keepPort(serverItem, ssrc, localPort, callback);
229 if (localPort == 0) { 230 if (localPort == 0) {
230 return null; 231 return null;
231 } 232 }
@@ -257,11 +258,12 @@ public class ZLMRTPServerFactory { @@ -257,11 +258,12 @@ public class ZLMRTPServerFactory {
257 * @param tcp 是否为tcp 258 * @param tcp 是否为tcp
258 * @return SendRtpItem 259 * @return SendRtpItem
259 */ 260 */
260 - public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp, boolean rtcp){ 261 + public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId,
  262 + String app, String stream, String channelId, boolean tcp, boolean rtcp, KeepPortCallback callback){
261 // 默认为随机端口 263 // 默认为随机端口
262 int localPort = 0; 264 int localPort = 0;
263 if (userSetting.getGbSendStreamStrict()) { 265 if (userSetting.getGbSendStreamStrict()) {
264 - localPort = keepPort(serverItem, ssrc); 266 + localPort = keepPort(serverItem, ssrc, localPort, callback);
265 if (localPort == 0) { 267 if (localPort == 0) {
266 return null; 268 return null;
267 } 269 }
@@ -282,13 +284,16 @@ public class ZLMRTPServerFactory { @@ -282,13 +284,16 @@ public class ZLMRTPServerFactory {
282 return sendRtpItem; 284 return sendRtpItem;
283 } 285 }
284 286
  287 + public interface KeepPortCallback{
  288 + Boolean keep(String ssrc);
  289 + }
  290 +
285 /** 291 /**
286 * 保持端口,直到需要需要发流时再释放 292 * 保持端口,直到需要需要发流时再释放
287 */ 293 */
288 - public int keepPort(MediaServerItem serverItem, String ssrc) {  
289 - int localPort = 0; 294 + public int keepPort(MediaServerItem serverItem, String ssrc, int localPort, KeepPortCallback keepPortCallback) {
290 Map<String, Object> param = new HashMap<>(3); 295 Map<String, Object> param = new HashMap<>(3);
291 - param.put("port", 0); 296 + param.put("port", localPort);
292 param.put("enable_tcp", 1); 297 param.put("enable_tcp", 1);
293 param.put("stream_id", ssrc); 298 param.put("stream_id", ssrc);
294 JSONObject jsonObject = zlmresTfulUtils.openRtpServer(serverItem, param); 299 JSONObject jsonObject = zlmresTfulUtils.openRtpServer(serverItem, param);
@@ -296,10 +301,21 @@ public class ZLMRTPServerFactory { @@ -296,10 +301,21 @@ public class ZLMRTPServerFactory {
296 localPort = jsonObject.getInteger("port"); 301 localPort = jsonObject.getInteger("port");
297 HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId()); 302 HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId());
298 // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 303 // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
  304 + Integer finalLocalPort = localPort;
299 hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout, 305 hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout,
300 (MediaServerItem mediaServerItem, JSONObject response)->{ 306 (MediaServerItem mediaServerItem, JSONObject response)->{
301 - logger.info("[上级点播] {}->监听端口到期继续保持监听", ssrc);  
302 - keepPort(serverItem, ssrc); 307 + System.out.println("监听端口到期继续保持监听");
  308 + System.out.println(response);
  309 + if (ssrc.equals(response.getString("stream_id"))) {
  310 + if (keepPortCallback.keep(ssrc)) {
  311 + logger.info("[上级点播] {}->监听端口到期继续保持监听", ssrc);
  312 + keepPort(serverItem, ssrc, finalLocalPort, keepPortCallback);
  313 + }else {
  314 + logger.info("[上级点播] {}->发送取消,无需继续监听", ssrc);
  315 + releasePort(serverItem, ssrc);
  316 + }
  317 + }
  318 +
303 }); 319 });
304 logger.info("[上级点播] {}->监听端口: {}", ssrc, localPort); 320 logger.info("[上级点播] {}->监听端口: {}", ssrc, localPort);
305 }else { 321 }else {
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
@@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; @@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
13 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 13 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
14 import com.genersoft.iot.vmp.service.IMediaServerService; 14 import com.genersoft.iot.vmp.service.IMediaServerService;
15 import com.genersoft.iot.vmp.service.bean.*; 15 import com.genersoft.iot.vmp.service.bean.*;
  16 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
16 import com.genersoft.iot.vmp.vmanager.bean.WVPResult; 17 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
17 import org.slf4j.Logger; 18 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory; 19 import org.slf4j.LoggerFactory;
@@ -26,6 +27,7 @@ import org.springframework.stereotype.Component; @@ -26,6 +27,7 @@ import org.springframework.stereotype.Component;
26 27
27 import java.text.ParseException; 28 import java.text.ParseException;
28 import java.util.HashMap; 29 import java.util.HashMap;
  30 +import java.util.List;
29 import java.util.Map; 31 import java.util.Map;
30 import java.util.UUID; 32 import java.util.UUID;
31 import java.util.concurrent.ConcurrentHashMap; 33 import java.util.concurrent.ConcurrentHashMap;
@@ -314,7 +316,9 @@ public class RedisGbPlayMsgListener implements MessageListener { @@ -314,7 +316,9 @@ public class RedisGbPlayMsgListener implements MessageListener {
314 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(), 316 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(),
315 content.getPort(), content.getSsrc(), content.getPlatformId(), 317 content.getPort(), content.getSsrc(), content.getPlatformId(),
316 content.getApp(), content.getStream(), content.getChannelId(), 318 content.getApp(), content.getStream(), content.getChannelId(),
317 - content.getTcp(), content.getRtcp()); 319 + content.getTcp(), content.getRtcp(), ssrcFromCallback -> {
  320 + return querySendRTPServer(content.getPlatformId(), content.getChannelId(), content.getStream(), null) != null;
  321 + });
318 322
319 WVPResult<ResponseSendItemMsg> result = new WVPResult<>(); 323 WVPResult<ResponseSendItemMsg> result = new WVPResult<>();
320 result.setCode(0); 324 result.setCode(0);
@@ -391,4 +395,31 @@ public class RedisGbPlayMsgListener implements MessageListener { @@ -391,4 +395,31 @@ public class RedisGbPlayMsgListener implements MessageListener {
391 }); 395 });
392 redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); 396 redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
393 } 397 }
  398 +
  399 + private SendRtpItem querySendRTPServer(String platformGbId, String channelId, String streamId, String callId) {
  400 + if (platformGbId == null) {
  401 + platformGbId = "*";
  402 + }
  403 + if (channelId == null) {
  404 + channelId = "*";
  405 + }
  406 + if (streamId == null) {
  407 + streamId = "*";
  408 + }
  409 + if (callId == null) {
  410 + callId = "*";
  411 + }
  412 + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX
  413 + + userSetting.getServerId() + "_*_"
  414 + + platformGbId + "_"
  415 + + channelId + "_"
  416 + + streamId + "_"
  417 + + callId;
  418 + List<Object> scan = RedisUtil.scan(redisTemplate, key);
  419 + if (scan.size() > 0) {
  420 + return (SendRtpItem)redisTemplate.opsForValue().get(scan.get(0));
  421 + }else {
  422 + return null;
  423 + }
  424 + }
394 } 425 }