优化国标级联发流并发能力

pull/673/head
648540858 2022-11-16 09:39:27 +08:00
parent 2466a24860
commit 694076dc8c
26 changed files with 277 additions and 166 deletions

View File

@ -281,7 +281,6 @@ CREATE TABLE `media_server` (
`secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`rtpEnable` int NOT NULL, `rtpEnable` int NOT NULL,
`rtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `rtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`sendRtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`recordAssistPort` int NOT NULL, `recordAssistPort` int NOT NULL,
`defaultServer` int NOT NULL, `defaultServer` int NOT NULL,
`createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,

View File

@ -1,6 +1,9 @@
alter table media_server alter table media_server
drop column streamNoneReaderDelayMS; drop column streamNoneReaderDelayMS;
alter table media_server
drop column sendRtpPortRange;
alter table stream_proxy alter table stream_proxy
add enable_disable_none_reader bit(1) default null; add enable_disable_none_reader bit(1) default null;

View File

@ -37,6 +37,8 @@ public class UserSetting {
private Boolean pushAuthority = Boolean.TRUE; private Boolean pushAuthority = Boolean.TRUE;
private Boolean gbSendStreamStrict = Boolean.FALSE;
private String serverId = "000000"; private String serverId = "000000";
private String thirdPartyGBIdReg = "[\\s\\S]*"; private String thirdPartyGBIdReg = "[\\s\\S]*";
@ -166,4 +168,12 @@ public class UserSetting {
public void setPushAuthority(Boolean pushAuthority) { public void setPushAuthority(Boolean pushAuthority) {
this.pushAuthority = pushAuthority; this.pushAuthority = pushAuthority;
} }
public Boolean getGbSendStreamStrict() {
return gbSendStreamStrict;
}
public void setGbSendStreamStrict(Boolean gbSendStreamStrict) {
this.gbSendStreamStrict = gbSendStreamStrict;
}
} }

View File

@ -7,10 +7,12 @@ import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.sip.*; import javax.sip.DialogTerminatedEvent;
import javax.sip.ResponseEvent;
import javax.sip.TimeoutEvent;
import javax.sip.TransactionTerminatedEvent;
import javax.sip.header.CallIdHeader; import javax.sip.header.CallIdHeader;
import javax.sip.message.Response; import javax.sip.message.Response;
import java.text.ParseException;
import java.time.Instant; import java.time.Instant;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -29,6 +31,7 @@ public class SipSubscribe {
private Map<String, SipSubscribe.Event> okSubscribes = new ConcurrentHashMap<>(); private Map<String, SipSubscribe.Event> okSubscribes = new ConcurrentHashMap<>();
private Map<String, Instant> okTimeSubscribes = new ConcurrentHashMap<>(); private Map<String, Instant> okTimeSubscribes = new ConcurrentHashMap<>();
private Map<String, Instant> errorTimeSubscribes = new ConcurrentHashMap<>(); private Map<String, Instant> errorTimeSubscribes = new ConcurrentHashMap<>();
// @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次 // @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次

View File

@ -1,18 +1,18 @@
package com.genersoft.iot.vmp.gb28181.event.record; package com.genersoft.iot.vmp.gb28181.event.record;
import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException; import java.util.HashMap;
import java.util.*; import java.util.Hashtable;
import java.util.Map;
/** /**
* @description: * @description:
* @author: pan * @author: pan
* @data: 2022-02-23 * @data: 2022-02-23
*/ */

View File

@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
@ -12,45 +11,31 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.SSRCInfo; import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.utils.GitUtil;
import gov.nist.javax.sip.SIPConstants;
import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.SipStackImpl;
import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse; import gov.nist.javax.sip.message.SIPResponse;
import gov.nist.javax.sip.stack.SIPClientTransaction;
import gov.nist.javax.sip.stack.SIPClientTransactionImpl;
import gov.nist.javax.sip.stack.SIPDialog;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import javax.sip.*; import javax.sip.InvalidArgumentException;
import javax.sip.address.Address; import javax.sip.ResponseEvent;
import javax.sip.address.SipURI; import javax.sip.SipException;
import javax.sip.header.*; import javax.sip.SipFactory;
import javax.sip.message.Message; import javax.sip.header.CallIdHeader;
import javax.sip.message.Request; import javax.sip.message.Request;
import javax.sip.message.Response;
import java.lang.reflect.Field;
import java.text.ParseException; import java.text.ParseException;
import java.util.HashSet;
/** /**
* @description: * @description:
@ -183,7 +168,7 @@ public class SIPCommander implements ISIPCommander {
public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed, public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,
int zoomSpeed) throws InvalidArgumentException, SipException, ParseException { int zoomSpeed) throws InvalidArgumentException, SipException, ParseException {
String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed); String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed);
StringBuffer ptzXml = new StringBuffer(200); StringBuilder ptzXml = new StringBuilder(200);
String charset = device.getCharset(); String charset = device.getCharset();
ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n"); ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
ptzXml.append("<Control>\r\n"); ptzXml.append("<Control>\r\n");

View File

@ -1,7 +1,6 @@
package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
@ -15,15 +14,12 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.message.MessageFactoryImpl; import gov.nist.javax.sip.message.MessageFactoryImpl;
import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPRequest;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@ -638,7 +634,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
if (mediaServerItem != null) { if (mediaServerItem != null) {
mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
zlmrtpServerFactory.closeRTPServer(mediaServerItem, sendRtpItem.getStreamId()); zlmrtpServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStreamId());
} }
SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem); SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem);
if (byeRequest == null) { if (byeRequest == null) {

View File

@ -10,8 +10,8 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
@ -33,7 +33,8 @@ import javax.sip.header.FromHeader;
import javax.sip.header.HeaderAddress; import javax.sip.header.HeaderAddress;
import javax.sip.header.ToHeader; import javax.sip.header.ToHeader;
import java.text.ParseException; import java.text.ParseException;
import java.util.*; import java.util.HashMap;
import java.util.Map;
/** /**
* SIP ACK * SIP ACK
@ -62,6 +63,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
@Autowired @Autowired
private ZLMRTPServerFactory zlmrtpServerFactory; private ZLMRTPServerFactory zlmrtpServerFactory;
@Autowired
private ZlmHttpHookSubscribe hookSubscribe;
@Autowired @Autowired
private IMediaServerService mediaServerService; private IMediaServerService mediaServerService;
@ -130,8 +134,18 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader); startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader);
}); });
}else { }else {
JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); // 如果是非严格模式,需要关闭端口占用
startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader); JSONObject startSendRtpStreamResult = null;
if (sendRtpItem.getLocalPort() != 0) {
if (zlmrtpServerFactory.releasePort(mediaInfo, sendRtpItem.getSsrc())) {
startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
}
}else {
startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
}
if (startSendRtpStreamResult != null) {
startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader);
}
} }
} }
private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform, private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform,

View File

@ -45,6 +45,7 @@ import javax.sip.header.CallIdHeader;
import javax.sip.message.Response; import javax.sip.message.Response;
import java.text.ParseException; import java.text.ParseException;
import java.time.Instant; import java.time.Instant;
import java.util.Random;
import java.util.Vector; import java.util.Vector;
/** /**
@ -157,11 +158,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
StreamProxyItem proxyByAppAndStream =null; StreamProxyItem proxyByAppAndStream =null;
// 不是通道可能是直播流 // 不是通道可能是直播流
if (channel != null && gbStream == null) { if (channel != null && gbStream == null) {
// if (channel.getStatus() == 0) {
// logger.info("通道离线返回400");
// responseAck(request, Response.BAD_REQUEST, "channel [" + channel.getChannelId() + "] offline");
// return;
// }
// 通道存在发100TRYING // 通道存在发100TRYING
try { try {
responseAck(request, Response.TRYING); responseAck(request, Response.TRYING);
@ -385,7 +381,12 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
} else { } else {
content.append("t=0 0\r\n"); content.append("t=0 0\r\n");
} }
content.append("m=video " + sendRtpItem.getLocalPort() + " RTP/AVP 96\r\n"); int localPort = sendRtpItem.getLocalPort();
if (localPort == 0) {
// 非严格模式端口不统一, 增加兼容性修改为一个不为0的端口
localPort = new Random().nextInt(65535) + 1;
}
content.append("m=video " + localPort + " RTP/AVP 96\r\n");
content.append("a=sendonly\r\n"); content.append("a=sendonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n"); content.append("a=rtpmap:96 PS/90000\r\n");
content.append("y=" + sendRtpItem.getSsrc() + "\r\n"); content.append("y=" + sendRtpItem.getSsrc() + "\r\n");
@ -476,9 +477,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
// 写入redis 超时时回复 // 写入redis 超时时回复
redisCatchStorage.updateSendRTPSever(sendRtpItem); redisCatchStorage.updateSendRTPSever(sendRtpItem);
MediaServerItem finalMediaServerItem = mediaServerItem;
playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> { playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> {
logger.info("[上级点播]超时, 用户:{} 通道:{}", username, channelId); logger.info("[上级点播]超时, 用户:{} 通道:{}", username, channelId);
redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
zlmrtpServerFactory.releasePort(finalMediaServerItem, sendRtpItem.getSsrc());
}, null); }, null);
} else { } else {
sendRtpItem.setStreamId(playTransaction.getStream()); sendRtpItem.setStreamId(playTransaction.getStream());

View File

@ -626,6 +626,32 @@ public class ZLMHttpHookListener {
return ret; return ret;
} }
/**
* rtpServer
*/
@ResponseBody
@PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
public JSONObject onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param){
System.out.println(param);
logger.info("[ZLM HOOK] rtpServer收流超时{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
taskExecutor.execute(()->{
JSONObject json = (JSONObject) JSON.toJSON(param);
List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
if (subscribes != null && subscribes.size() > 0) {
for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
subscribe.response(null, json);
}
}
});
return ret;
}
private Map<String, String> urlParamToMap(String params) { private Map<String, String> urlParamToMap(String params) {
HashMap<String, String> map = new HashMap<>(); HashMap<String, String> map = new HashMap<>();
if (ObjectUtils.isEmpty(params)) { if (ObjectUtils.isEmpty(params)) {

View File

@ -1,15 +1,15 @@
package com.genersoft.iot.vmp.media.zlm; package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.*;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import java.util.*; import java.util.*;
@ -24,6 +24,9 @@ public class ZLMRTPServerFactory {
@Autowired @Autowired
private UserSetting userSetting; private UserSetting userSetting;
@Autowired
private ZlmHttpHookSubscribe hookSubscribe;
private int[] portRangeArray = new int[2]; private int[] portRangeArray = new int[2];
public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List<Integer> usedFreelist) { public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List<Integer> usedFreelist) {
@ -141,7 +144,7 @@ public class ZLMRTPServerFactory {
return result; return result;
} }
public boolean closeRTPServer(MediaServerItem serverItem, String streamId) { public boolean closeRtpServer(MediaServerItem serverItem, String streamId) {
boolean result = false; boolean result = false;
if (serverItem !=null){ if (serverItem !=null){
Map<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
@ -161,32 +164,6 @@ public class ZLMRTPServerFactory {
return result; return result;
} }
// private int getPortFromportRange(MediaServerItem mediaServerItem) {
// int currentPort = mediaServerItem.getCurrentPort();
// if (currentPort == 0) {
// String[] portRangeStrArray = mediaServerItem.getSendRtpPortRange().split(",");
// if (portRangeStrArray.length != 2) {
// portRangeArray[0] = 30000;
// portRangeArray[1] = 30500;
// }else {
// portRangeArray[0] = Integer.parseInt(portRangeStrArray[0]);
// portRangeArray[1] = Integer.parseInt(portRangeStrArray[1]);
// }
// }
//
// if (currentPort == 0 || currentPort++ > portRangeArray[1]) {
// currentPort = portRangeArray[0];
// mediaServerItem.setCurrentPort(currentPort);
// return portRangeArray[0];
// } else {
// if (currentPort % 2 == 1) {
// currentPort++;
// }
// currentPort++;
// mediaServerItem.setCurrentPort(currentPort);
// return currentPort;
// }
// }
/** /**
* *
@ -200,21 +177,15 @@ public class ZLMRTPServerFactory {
*/ */
public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){ public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){
// 使用RTPServer 功能找一个可用的端口 // 默认为随机端口
String sendRtpPortRange = serverItem.getSendRtpPortRange(); int localPort = 0;
if (ObjectUtils.isEmpty(sendRtpPortRange)) { if (userSetting.getGbSendStreamStrict()) {
return null; if (userSetting.getGbSendStreamStrict()) {
} localPort = keepPort(serverItem, ssrc);
String[] portRangeStrArray = serverItem.getSendRtpPortRange().split(","); if (localPort == 0) {
int localPort = -1; return null;
if (portRangeStrArray.length != 2) { }
localPort = getFreePort(serverItem, 30000, 30500, null); }
}else {
localPort = getFreePort(serverItem, Integer.parseInt(portRangeStrArray[0]), Integer.parseInt(portRangeStrArray[1]), null);
}
if (localPort == -1) {
logger.error("没有可用的端口");
return null;
} }
SendRtpItem sendRtpItem = new SendRtpItem(); SendRtpItem sendRtpItem = new SendRtpItem();
sendRtpItem.setIp(ip); sendRtpItem.setIp(ip);
@ -242,21 +213,13 @@ public class ZLMRTPServerFactory {
* @return SendRtpItem * @return SendRtpItem
*/ */
public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){ public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){
// 使用RTPServer 功能找一个可用的端口 // 默认为随机端口
String sendRtpPortRange = serverItem.getSendRtpPortRange(); int localPort = 0;
if (ObjectUtils.isEmpty(sendRtpPortRange)) { if (userSetting.getGbSendStreamStrict()) {
return null; localPort = keepPort(serverItem, ssrc);
} if (localPort == 0) {
String[] portRangeStrArray = serverItem.getSendRtpPortRange().split(","); return null;
int localPort = -1; }
if (portRangeStrArray.length != 2) {
localPort = getFreePort(serverItem, 30000, 30500, null);
}else {
localPort = getFreePort(serverItem, Integer.parseInt(portRangeStrArray[0]), Integer.parseInt(portRangeStrArray[1]), null);
}
if (localPort == -1) {
logger.error("没有可用的端口");
return null;
} }
SendRtpItem sendRtpItem = new SendRtpItem(); SendRtpItem sendRtpItem = new SendRtpItem();
sendRtpItem.setIp(ip); sendRtpItem.setIp(ip);
@ -273,6 +236,42 @@ public class ZLMRTPServerFactory {
return sendRtpItem; return sendRtpItem;
} }
/**
*
*/
public int keepPort(MediaServerItem serverItem, String ssrc) {
int localPort = 0;
Map<String, Object> param = new HashMap<>(3);
param.put("port", 0);
param.put("enable_tcp", 1);
param.put("stream_id", ssrc);
JSONObject jsonObject = zlmresTfulUtils.openRtpServer(serverItem, param);
if (jsonObject.getInteger("code") == 0) {
localPort = jsonObject.getInteger("port");
HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId());
// 订阅 zlm启动事件, 新的zlm也会从这里进入系统
hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout,
(MediaServerItem mediaServerItem, JSONObject response)->{
logger.info("[上级点播] {}->监听端口到期继续保持监听", ssrc);
keepPort(serverItem, ssrc);
});
}
logger.info("[上级点播] {}->监听端口: {}", ssrc, localPort);
return localPort;
}
/**
*
*/
public boolean releasePort(MediaServerItem serverItem, String ssrc) {
logger.info("[上级点播] {}->释放监听端口,等待推流", ssrc);
boolean closeRTPServerResult = closeRtpServer(serverItem, ssrc);
HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId());
// 订阅 zlm启动事件, 新的zlm也会从这里进入系统
hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout);
return closeRTPServerResult;
}
/** /**
* zlm RESTFUL API startSendRtp * zlm RESTFUL API startSendRtp
*/ */
@ -333,7 +332,7 @@ public class ZLMRTPServerFactory {
result= true; result= true;
logger.info("[停止RTP推流] 成功"); logger.info("[停止RTP推流] 成功");
} else { } else {
logger.error("[停止RTP推流] 失败: {}, 参数:{}->\r\n{}",jsonObject.getString("msg"),jsonObject.toJSONString(param)); logger.error("[停止RTP推流] 失败: {}, 参数:{}->\r\n{}",jsonObject.getString("msg"), JSON.toJSON(param), jsonObject);
} }
return result; return result;
} }

View File

@ -18,7 +18,11 @@ import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.*; import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Component @Component
@Order(value=1) @Order(value=1)
@ -73,8 +77,6 @@ public class ZLMRunner implements CommandLineRunner {
} }
}); });
// 获取zlm信息 // 获取zlm信息
logger.info("[zlm] 等待默认zlm中..."); logger.info("[zlm] 等待默认zlm中...");
@ -87,7 +89,7 @@ public class ZLMRunner implements CommandLineRunner {
} }
for (MediaServerItem mediaServerItem : all) { for (MediaServerItem mediaServerItem : all) {
if (startGetMedia == null) { if (startGetMedia == null) {
startGetMedia = new HashMap<>(); startGetMedia = new ConcurrentHashMap<>();
} }
startGetMedia.put(mediaServerItem.getId(), true); startGetMedia.put(mediaServerItem.getId(), true);
connectZlmServer(mediaServerItem); connectZlmServer(mediaServerItem);
@ -95,7 +97,7 @@ public class ZLMRunner implements CommandLineRunner {
} }
String taskKey = "zlm-connect-timeout"; String taskKey = "zlm-connect-timeout";
dynamicTask.startDelay(taskKey, ()->{ dynamicTask.startDelay(taskKey, ()->{
if (startGetMedia != null) { if (startGetMedia != null && startGetMedia.size() > 0) {
Set<String> allZlmId = startGetMedia.keySet(); Set<String> allZlmId = startGetMedia.keySet();
for (String id : allZlmId) { for (String id : allZlmId) {
logger.error("[ {} ]]主动连接失败,不再尝试连接", id); logger.error("[ {} ]]主动连接失败,不再尝试连接", id);

View File

@ -24,6 +24,17 @@ public class HookSubscribeFactory {
return hookSubscribe; return hookSubscribe;
} }
public static HookSubscribeForRtpServerTimeout on_rtp_server_timeout(String stream, String ssrc, String mediaServerId) {
HookSubscribeForRtpServerTimeout hookSubscribe = new HookSubscribeForRtpServerTimeout();
JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
subscribeKey.put("stream_id", stream);
subscribeKey.put("ssrc", ssrc);
subscribeKey.put("mediaServerId", mediaServerId);
hookSubscribe.setContent(subscribeKey);
return hookSubscribe;
}
public static HookSubscribeForServerStarted on_server_started() { public static HookSubscribeForServerStarted on_server_started() {
HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted(); HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted();
hookSubscribe.setContent(new JSONObject()); hookSubscribe.setContent(new JSONObject());

View File

@ -0,0 +1,44 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import java.time.Instant;
/**
* hook-
* @author lin
*/
public class HookSubscribeForRtpServerTimeout implements IHookSubscribe{
private HookType hookType = HookType.on_rtp_server_timeout;
private JSONObject content;
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Instant expires;
@Override
public HookType getHookType() {
return hookType;
}
@Override
public JSONObject getContent() {
return content;
}
public void setContent(JSONObject content) {
this.content = content;
}
@Override
public Instant getExpires() {
return expires;
}
@Override
public void setExpires(Instant expires) {
this.expires = expires;
}
}

View File

@ -15,6 +15,7 @@ public class HookSubscribeForStreamChange implements IHookSubscribe{
private JSONObject content; private JSONObject content;
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Instant expires; private Instant expires;
@Override @Override

View File

@ -19,5 +19,7 @@ public enum HookType {
on_stream_none_reader, on_stream_none_reader,
on_stream_not_found, on_stream_not_found,
on_server_started, on_server_started,
on_rtp_server_timeout,
on_server_keepalive on_server_keepalive
} }

View File

@ -65,9 +65,6 @@ public class MediaServerItem{
@Schema(description = "多端口RTP收流端口范围") @Schema(description = "多端口RTP收流端口范围")
private String rtpPortRange; private String rtpPortRange;
@Schema(description = "RTP发流端口范围")
private String sendRtpPortRange;
@Schema(description = "assist服务端口") @Schema(description = "assist服务端口")
private int recordAssistPort; private int recordAssistPort;
@ -118,7 +115,6 @@ public class MediaServerItem{
hookAliveInterval = zlmServerConfig.getHookAliveInterval(); hookAliveInterval = zlmServerConfig.getHookAliveInterval();
rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口 rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口
rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号 rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号
sendRtpPortRange = "30000,30500"; // 默认使用30000,30500作为级联时发送流的端口号
recordAssistPort = 0; // 默认关闭 recordAssistPort = 0; // 默认关闭
} }
@ -323,14 +319,6 @@ public class MediaServerItem{
this.lastKeepaliveTime = lastKeepaliveTime; this.lastKeepaliveTime = lastKeepaliveTime;
} }
public String getSendRtpPortRange() {
return sendRtpPortRange;
}
public void setSendRtpPortRange(String sendRtpPortRange) {
this.sendRtpPortRange = sendRtpPortRange;
}
public Float getHookAliveInterval() { public Float getHookAliveInterval() {
return hookAliveInterval; return hookAliveInterval;
} }

View File

@ -0,0 +1,53 @@
package com.genersoft.iot.vmp.media.zlm.dto.hook;
/**
* zlm hookon_rtp_server_timeout
* @author lin
*/
public class OnRtpServerTimeoutHookParam extends HookParam{
private int local_port;
private String stream_id;
private int tcpMode;
private boolean re_use_port;
private String ssrc;
public int getLocal_port() {
return local_port;
}
public void setLocal_port(int local_port) {
this.local_port = local_port;
}
public String getStream_id() {
return stream_id;
}
public void setStream_id(String stream_id) {
this.stream_id = stream_id;
}
public int getTcpMode() {
return tcpMode;
}
public void setTcpMode(int tcpMode) {
this.tcpMode = tcpMode;
}
public boolean isRe_use_port() {
return re_use_port;
}
public void setRe_use_port(boolean re_use_port) {
this.re_use_port = re_use_port;
}
public String getSsrc() {
return ssrc;
}
public void setSsrc(String ssrc) {
this.ssrc = ssrc;
}
}

View File

@ -4,13 +4,12 @@ import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler;
import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
import com.genersoft.iot.vmp.service.IDeviceChannelService;
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;
import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler;
import com.genersoft.iot.vmp.service.IDeviceChannelService;
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
@ -24,12 +23,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.support.incrementer.AbstractIdentityColumnMaxValueIncrementer;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.sip.InvalidArgumentException; import javax.sip.InvalidArgumentException;
import javax.sip.SipException; import javax.sip.SipException;
@ -171,7 +168,7 @@ public class DeviceServiceImpl implements IDeviceService {
redisCatchStorage.updateDevice(device); redisCatchStorage.updateDevice(device);
deviceMapper.update(device); deviceMapper.update(device);
//进行通道离线 //进行通道离线
deviceChannelMapper.offlineByDeviceId(deviceId); // deviceChannelMapper.offlineByDeviceId(deviceId);
// 离线释放所有ssrc // 离线释放所有ssrc
List<SsrcTransaction> ssrcTransactions = streamSession.getSsrcTransactionForAll(deviceId, null, null, null); List<SsrcTransaction> ssrcTransactions = streamSession.getSsrcTransactionForAll(deviceId, null, null, null);
if (ssrcTransactions != null && ssrcTransactions.size() > 0) { if (ssrcTransactions != null && ssrcTransactions.size() > 0) {

View File

@ -168,7 +168,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
if (mediaServerItem == null) { if (mediaServerItem == null) {
return; return;
} }
zlmrtpServerFactory.closeRTPServer(mediaServerItem, streamId); zlmrtpServerFactory.closeRtpServer(mediaServerItem, streamId);
releaseSsrc(mediaServerItem.getId(), streamId); releaseSsrc(mediaServerItem.getId(), streamId);
} }
@ -535,6 +535,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex)); param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex));
param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrex)); param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrex));
param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrex)); param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrex));
param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrex));
if (mediaServerItem.getRecordAssistPort() > 0) { if (mediaServerItem.getRecordAssistPort() > 0) {
param.put("hook.on_record_mp4",String.format("http://127.0.0.1:%s/api/record/on_record_mp4", mediaServerItem.getRecordAssistPort())); param.put("hook.on_record_mp4",String.format("http://127.0.0.1:%s/api/record/on_record_mp4", mediaServerItem.getRecordAssistPort()));
}else { }else {
@ -545,8 +546,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
// 置0关闭此特性(推流断开会导致立即断开播放器) // 置0关闭此特性(推流断开会导致立即断开播放器)
// 此参数不应大于播放器超时时间 // 此参数不应大于播放器超时时间
// 优化此消息以更快的收到流注销事件 // 优化此消息以更快的收到流注销事件
param.put("general.continue_push_ms", "3000" ); param.put("protocol.continue_push_ms", "3000" );
param.put("general.publishToHls", "0" );
// 最多等待未初始化的Track时间单位毫秒超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流, // 最多等待未初始化的Track时间单位毫秒超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流,
// 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项 // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项
// param.put("general.wait_track_ready_ms", "3000" ); // param.put("general.wait_track_ready_ms", "3000" );

View File

@ -283,7 +283,7 @@ public class PlayServiceImpl implements IPlayService {
try { try {
cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString()); logger.info("收到订阅消息: " + response.toJSONString());
System.out.println("停止超时任务: " + timeOutTaskKey); dynamicTask.stop(timeOutTaskKey);
// hook响应 // hook响应
onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid); onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid);

View File

@ -28,7 +28,6 @@ public interface MediaServerMapper {
"secret, " + "secret, " +
"rtpEnable, " + "rtpEnable, " +
"rtpPortRange, " + "rtpPortRange, " +
"sendRtpPortRange, " +
"recordAssistPort, " + "recordAssistPort, " +
"defaultServer, " + "defaultServer, " +
"createTime, " + "createTime, " +
@ -52,7 +51,6 @@ public interface MediaServerMapper {
"'${secret}', " + "'${secret}', " +
"${rtpEnable}, " + "${rtpEnable}, " +
"'${rtpPortRange}', " + "'${rtpPortRange}', " +
"'${sendRtpPortRange}', " +
"${recordAssistPort}, " + "${recordAssistPort}, " +
"${defaultServer}, " + "${defaultServer}, " +
"'${createTime}', " + "'${createTime}', " +
@ -77,7 +75,6 @@ public interface MediaServerMapper {
"<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" + "<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" +
"<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" + "<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" +
"<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" + "<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" +
"<if test=\"sendRtpPortRange != null\">, sendRtpPortRange='${sendRtpPortRange}'</if>" +
"<if test=\"secret != null\">, secret='${secret}'</if>" + "<if test=\"secret != null\">, secret='${secret}'</if>" +
"<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" + "<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" +
"<if test=\"hookAliveInterval != null\">, hookAliveInterval=${hookAliveInterval}</if>" + "<if test=\"hookAliveInterval != null\">, hookAliveInterval=${hookAliveInterval}</if>" +
@ -101,7 +98,6 @@ public interface MediaServerMapper {
"<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" + "<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" +
"<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" + "<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" +
"<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" + "<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" +
"<if test=\"sendRtpPortRange != null\">, sendRtpPortRange='${sendRtpPortRange}'</if>" +
"<if test=\"secret != null\">, secret='${secret}'</if>" + "<if test=\"secret != null\">, secret='${secret}'</if>" +
"<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" + "<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" +
"<if test=\"hookAliveInterval != null\">, hookAliveInterval=${hookAliveInterval}</if>" + "<if test=\"hookAliveInterval != null\">, hookAliveInterval=${hookAliveInterval}</if>" +

View File

@ -62,7 +62,9 @@ public class MediaController {
if (callId != null) { if (callId != null) {
// 权限校验 // 权限校验
StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream); StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream);
if (streamAuthorityInfo.getCallId().equals(callId)) { if (streamAuthorityInfo != null
&& streamAuthorityInfo.getCallId() != null
&& streamAuthorityInfo.getCallId().equals(callId)) {
authority = true; authority = true;
}else { }else {
throw new ControllerException(ErrorCode.ERROR400); throw new ControllerException(ErrorCode.ERROR400);

View File

@ -135,14 +135,8 @@ public class ServerController {
MediaServerItem mediaServerItemInDatabase = mediaServerService.getOne(mediaServerItem.getId()); MediaServerItem mediaServerItemInDatabase = mediaServerService.getOne(mediaServerItem.getId());
if (mediaServerItemInDatabase != null) { if (mediaServerItemInDatabase != null) {
if (ObjectUtils.isEmpty(mediaServerItemInDatabase.getSendRtpPortRange()) && ObjectUtils.isEmpty(mediaServerItem.getSendRtpPortRange())) {
mediaServerItem.setSendRtpPortRange("30000,30500");
}
mediaServerService.update(mediaServerItem); mediaServerService.update(mediaServerItem);
} else { } else {
if (ObjectUtils.isEmpty(mediaServerItem.getSendRtpPortRange())) {
mediaServerItem.setSendRtpPortRange("30000,30500");
}
mediaServerService.add(mediaServerItem); mediaServerService.add(mediaServerItem);
} }
} }

View File

@ -192,6 +192,9 @@ user-settings:
stream-on-demand: true stream-on-demand: true
# 推流鉴权, 默认开启 # 推流鉴权, 默认开启
push-authority: true push-authority: true
# 国标级联发流严格模式严格模式会使用与sdp信息中一致的端口发流端口共享media.rtp.port-range这会损失一些性能
# 非严格模式使用随机端口发流,性能更好, 默认关闭
gb-send-stream-strict: false
# 关闭在线文档(生产环境建议关闭) # 关闭在线文档(生产环境建议关闭)
springdoc: springdoc:

View File

@ -89,11 +89,6 @@
- -
<el-input v-model="rtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input> <el-input v-model="rtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="推流端口" prop="sendRtpPortRange1">
<el-input v-model="sendRtpPortRange1" placeholder="起始" @change="portRangeChange" clearable style="width: 100px" prop="sendRtpPortRange1" :disabled="mediaServerForm.defaultServer"></el-input>
-
<el-input v-model="sendRtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="sendRtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="录像管理服务端口" prop="recordAssistPort"> <el-form-item label="录像管理服务端口" prop="recordAssistPort">
<el-input v-model.number="mediaServerForm.recordAssistPort" :disabled="mediaServerForm.defaultServer"> <el-input v-model.number="mediaServerForm.recordAssistPort" :disabled="mediaServerForm.defaultServer">
<!-- <el-button v-if="mediaServerForm.recordAssistPort > 0" slot="append" type="primary" @click="checkRecordServer"></el-button>--> <!-- <el-button v-if="mediaServerForm.recordAssistPort > 0" slot="append" type="primary" @click="checkRecordServer"></el-button>-->
@ -177,15 +172,12 @@ export default {
rtmpSSlPort: "", rtmpSSlPort: "",
rtpEnable: false, rtpEnable: false,
rtpPortRange: "", rtpPortRange: "",
sendRtpPortRange: "",
rtpProxyPort: "", rtpProxyPort: "",
rtspPort: "", rtspPort: "",
rtspSSLPort: "", rtspSSLPort: "",
}, },
rtpPortRange1:30000, rtpPortRange1:30000,
rtpPortRange2:30500, rtpPortRange2:30500,
sendRtpPortRange1:30000,
sendRtpPortRange2:30500,
rules: { rules: {
ip: [{ required: true, validator: isValidIp, message: '请输入有效的IP地址', trigger: 'blur' }], ip: [{ required: true, validator: isValidIp, message: '请输入有效的IP地址', trigger: 'blur' }],
@ -196,8 +188,6 @@ export default {
rtmpSSlPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], rtmpSSlPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
rtpPortRange1: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], rtpPortRange1: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
rtpPortRange2: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], rtpPortRange2: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
sendRtpPortRange1: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
sendRtpPortRange2: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
rtpProxyPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], rtpProxyPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
rtspPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], rtspPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
rtspSSLPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], rtspSSLPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
@ -229,9 +219,6 @@ export default {
this.rtpPortRange2 = rtpPortRange[1] this.rtpPortRange2 = rtpPortRange[1]
} }
} }
let sendRtpPortRange = this.mediaServerForm.sendRtpPortRange.split(",");
this.sendRtpPortRange1 = sendRtpPortRange[0]
this.sendRtpPortRange2 = sendRtpPortRange[1]
} }
}, },
checkServer: function() { checkServer: function() {
@ -251,8 +238,6 @@ export default {
that.mediaServerForm = data.data; that.mediaServerForm = data.data;
that.mediaServerForm.httpPort = httpPort; that.mediaServerForm.httpPort = httpPort;
that.mediaServerForm.autoConfig = true; that.mediaServerForm.autoConfig = true;
that.sendRtpPortRange1 = 30000
that.sendRtpPortRange2 = 30500
that.rtpPortRange1 = 30000 that.rtpPortRange1 = 30000
that.rtpPortRange2 = 30500 that.rtpPortRange2 = 30500
that.serverCheck = 1; that.serverCheck = 1;
@ -336,13 +321,10 @@ export default {
rtmpSSlPort: "", rtmpSSlPort: "",
rtpEnable: false, rtpEnable: false,
rtpPortRange: "", rtpPortRange: "",
sendRtpPortRange: "",
rtpProxyPort: "", rtpProxyPort: "",
rtspPort: "", rtspPort: "",
rtspSSLPort: "", rtspSSLPort: "",
}; };
this.sendRtpPortRange1 = 30000;
this.sendRtpPortRange2 = 30500;
this.rtpPortRange1 = 30500; this.rtpPortRange1 = 30500;
this.rtpPortRange2 = 30500; this.rtpPortRange2 = 30500;
this.listChangeCallback = null this.listChangeCallback = null
@ -367,9 +349,7 @@ export default {
} }
}, },
portRangeChange: function() { portRangeChange: function() {
this.mediaServerForm.sendRtpPortRange = this.sendRtpPortRange1 + "," + this.sendRtpPortRange2
this.mediaServerForm.rtpPortRange = this.rtpPortRange1 + "," + this.rtpPortRange2 this.mediaServerForm.rtpPortRange = this.rtpPortRange1 + "," + this.rtpPortRange2
console.log(this.mediaServerForm.sendRtpPortRange)
console.log(this.mediaServerForm.rtpPortRange) console.log(this.mediaServerForm.rtpPortRange)
} }
}, },