Commit 694076dc8c447754b02dff24a891249f54427ffb

Authored by 648540858
1 parent 2466a248

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

Showing 26 changed files with 275 additions and 164 deletions
sql/mysql.sql
@@ -281,7 +281,6 @@ CREATE TABLE `media_server` ( @@ -281,7 +281,6 @@ CREATE TABLE `media_server` (
281 `secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 281 `secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
282 `rtpEnable` int NOT NULL, 282 `rtpEnable` int NOT NULL,
283 `rtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 283 `rtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
284 - `sendRtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,  
285 `recordAssistPort` int NOT NULL, 284 `recordAssistPort` int NOT NULL,
286 `defaultServer` int NOT NULL, 285 `defaultServer` int NOT NULL,
287 `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 286 `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
sql/update.sql
1 alter table media_server 1 alter table media_server
2 drop column streamNoneReaderDelayMS; 2 drop column streamNoneReaderDelayMS;
3 3
  4 +alter table media_server
  5 + drop column sendRtpPortRange;
  6 +
4 alter table stream_proxy 7 alter table stream_proxy
5 add enable_disable_none_reader bit(1) default null; 8 add enable_disable_none_reader bit(1) default null;
6 9
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
@@ -37,6 +37,8 @@ public class UserSetting { @@ -37,6 +37,8 @@ public class UserSetting {
37 37
38 private Boolean pushAuthority = Boolean.TRUE; 38 private Boolean pushAuthority = Boolean.TRUE;
39 39
  40 + private Boolean gbSendStreamStrict = Boolean.FALSE;
  41 +
40 private String serverId = "000000"; 42 private String serverId = "000000";
41 43
42 private String thirdPartyGBIdReg = "[\\s\\S]*"; 44 private String thirdPartyGBIdReg = "[\\s\\S]*";
@@ -166,4 +168,12 @@ public class UserSetting { @@ -166,4 +168,12 @@ public class UserSetting {
166 public void setPushAuthority(Boolean pushAuthority) { 168 public void setPushAuthority(Boolean pushAuthority) {
167 this.pushAuthority = pushAuthority; 169 this.pushAuthority = pushAuthority;
168 } 170 }
  171 +
  172 + public Boolean getGbSendStreamStrict() {
  173 + return gbSendStreamStrict;
  174 + }
  175 +
  176 + public void setGbSendStreamStrict(Boolean gbSendStreamStrict) {
  177 + this.gbSendStreamStrict = gbSendStreamStrict;
  178 + }
169 } 179 }
src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
@@ -7,10 +7,12 @@ import org.slf4j.LoggerFactory; @@ -7,10 +7,12 @@ import org.slf4j.LoggerFactory;
7 import org.springframework.scheduling.annotation.Scheduled; 7 import org.springframework.scheduling.annotation.Scheduled;
8 import org.springframework.stereotype.Component; 8 import org.springframework.stereotype.Component;
9 9
10 -import javax.sip.*; 10 +import javax.sip.DialogTerminatedEvent;
  11 +import javax.sip.ResponseEvent;
  12 +import javax.sip.TimeoutEvent;
  13 +import javax.sip.TransactionTerminatedEvent;
11 import javax.sip.header.CallIdHeader; 14 import javax.sip.header.CallIdHeader;
12 import javax.sip.message.Response; 15 import javax.sip.message.Response;
13 -import java.text.ParseException;  
14 import java.time.Instant; 16 import java.time.Instant;
15 import java.util.Map; 17 import java.util.Map;
16 import java.util.concurrent.ConcurrentHashMap; 18 import java.util.concurrent.ConcurrentHashMap;
@@ -29,6 +31,7 @@ public class SipSubscribe { @@ -29,6 +31,7 @@ public class SipSubscribe {
29 private Map<String, SipSubscribe.Event> okSubscribes = new ConcurrentHashMap<>(); 31 private Map<String, SipSubscribe.Event> okSubscribes = new ConcurrentHashMap<>();
30 32
31 private Map<String, Instant> okTimeSubscribes = new ConcurrentHashMap<>(); 33 private Map<String, Instant> okTimeSubscribes = new ConcurrentHashMap<>();
  34 +
32 private Map<String, Instant> errorTimeSubscribes = new ConcurrentHashMap<>(); 35 private Map<String, Instant> errorTimeSubscribes = new ConcurrentHashMap<>();
33 36
34 // @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次 37 // @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次
src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java
1 package com.genersoft.iot.vmp.gb28181.event.record; 1 package com.genersoft.iot.vmp.gb28181.event.record;
2 2
3 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; 3 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
4 -import com.genersoft.iot.vmp.gb28181.bean.RecordItem;  
5 import org.slf4j.Logger; 4 import org.slf4j.Logger;
6 import org.slf4j.LoggerFactory; 5 import org.slf4j.LoggerFactory;
7 import org.springframework.context.ApplicationListener; 6 import org.springframework.context.ApplicationListener;
8 import org.springframework.stereotype.Component; 7 import org.springframework.stereotype.Component;
9 import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 8 import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
10 9
11 -import java.io.IOException;  
12 -import java.util.*; 10 +import java.util.HashMap;
  11 +import java.util.Hashtable;
  12 +import java.util.Map;
13 13
14 /** 14 /**
15 - * @description: 录像查询结束时间 15 + * @description: 录像查询结束事件
16 * @author: pan 16 * @author: pan
17 * @data: 2022-02-23 17 * @data: 2022-02-23
18 */ 18 */
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; @@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
2 2
3 import com.alibaba.fastjson2.JSONObject; 3 import com.alibaba.fastjson2.JSONObject;
4 import com.genersoft.iot.vmp.common.StreamInfo; 4 import com.genersoft.iot.vmp.common.StreamInfo;
5 -import com.genersoft.iot.vmp.conf.DynamicTask;  
6 import com.genersoft.iot.vmp.conf.SipConfig; 5 import com.genersoft.iot.vmp.conf.SipConfig;
7 import com.genersoft.iot.vmp.conf.UserSetting; 6 import com.genersoft.iot.vmp.conf.UserSetting;
8 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; 7 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
@@ -12,45 +11,31 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; @@ -12,45 +11,31 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
12 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; 11 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
13 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; 12 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
14 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; 13 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
  14 +import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
15 import com.genersoft.iot.vmp.gb28181.utils.SipUtils; 15 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  16 +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
16 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; 17 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
17 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; 18 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
18 -import com.genersoft.iot.vmp.utils.DateUtil;  
19 -import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;  
20 -import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;  
21 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 19 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
22 import com.genersoft.iot.vmp.service.IMediaServerService; 20 import com.genersoft.iot.vmp.service.IMediaServerService;
23 import com.genersoft.iot.vmp.service.bean.SSRCInfo; 21 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
24 -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;  
25 -import com.genersoft.iot.vmp.storager.IVideoManagerStorage;  
26 -import com.genersoft.iot.vmp.utils.GitUtil;  
27 -import gov.nist.javax.sip.SIPConstants;  
28 -import gov.nist.javax.sip.SipProviderImpl;  
29 -import gov.nist.javax.sip.SipStackImpl; 22 +import com.genersoft.iot.vmp.utils.DateUtil;
30 import gov.nist.javax.sip.message.SIPRequest; 23 import gov.nist.javax.sip.message.SIPRequest;
31 import gov.nist.javax.sip.message.SIPResponse; 24 import gov.nist.javax.sip.message.SIPResponse;
32 -import gov.nist.javax.sip.stack.SIPClientTransaction;  
33 -import gov.nist.javax.sip.stack.SIPClientTransactionImpl;  
34 -import gov.nist.javax.sip.stack.SIPDialog;  
35 import org.slf4j.Logger; 25 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory; 26 import org.slf4j.LoggerFactory;
37 import org.springframework.beans.factory.annotation.Autowired; 27 import org.springframework.beans.factory.annotation.Autowired;
38 -import org.springframework.beans.factory.annotation.Qualifier;  
39 import org.springframework.context.annotation.DependsOn; 28 import org.springframework.context.annotation.DependsOn;
40 -import org.springframework.context.annotation.Lazy;  
41 import org.springframework.stereotype.Component; 29 import org.springframework.stereotype.Component;
42 import org.springframework.util.ObjectUtils; 30 import org.springframework.util.ObjectUtils;
43 31
44 -import javax.sip.*;  
45 -import javax.sip.address.Address;  
46 -import javax.sip.address.SipURI;  
47 -import javax.sip.header.*;  
48 -import javax.sip.message.Message; 32 +import javax.sip.InvalidArgumentException;
  33 +import javax.sip.ResponseEvent;
  34 +import javax.sip.SipException;
  35 +import javax.sip.SipFactory;
  36 +import javax.sip.header.CallIdHeader;
49 import javax.sip.message.Request; 37 import javax.sip.message.Request;
50 -import javax.sip.message.Response;  
51 -import java.lang.reflect.Field;  
52 import java.text.ParseException; 38 import java.text.ParseException;
53 -import java.util.HashSet;  
54 39
55 /** 40 /**
56 * @description:设备能力接口,用于定义设备的控制、查询能力 41 * @description:设备能力接口,用于定义设备的控制、查询能力
@@ -183,7 +168,7 @@ public class SIPCommander implements ISIPCommander { @@ -183,7 +168,7 @@ public class SIPCommander implements ISIPCommander {
183 public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed, 168 public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,
184 int zoomSpeed) throws InvalidArgumentException, SipException, ParseException { 169 int zoomSpeed) throws InvalidArgumentException, SipException, ParseException {
185 String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed); 170 String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed);
186 - StringBuffer ptzXml = new StringBuffer(200); 171 + StringBuilder ptzXml = new StringBuilder(200);
187 String charset = device.getCharset(); 172 String charset = device.getCharset();
188 ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n"); 173 ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
189 ptzXml.append("<Control>\r\n"); 174 ptzXml.append("<Control>\r\n");
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
1 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; 1 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
2 2
3 import com.alibaba.fastjson2.JSON; 3 import com.alibaba.fastjson2.JSON;
4 -import com.alibaba.fastjson2.JSONObject;  
5 import com.genersoft.iot.vmp.gb28181.bean.*; 4 import com.genersoft.iot.vmp.gb28181.bean.*;
6 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; 5 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
7 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; 6 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
@@ -15,15 +14,12 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; @@ -15,15 +14,12 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
15 import com.genersoft.iot.vmp.service.IMediaServerService; 14 import com.genersoft.iot.vmp.service.IMediaServerService;
16 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; 15 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
17 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 16 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
18 -import gov.nist.javax.sip.SipProviderImpl;  
19 import gov.nist.javax.sip.message.MessageFactoryImpl; 17 import gov.nist.javax.sip.message.MessageFactoryImpl;
20 import gov.nist.javax.sip.message.SIPRequest; 18 import gov.nist.javax.sip.message.SIPRequest;
21 import org.slf4j.Logger; 19 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory; 20 import org.slf4j.LoggerFactory;
23 import org.springframework.beans.factory.annotation.Autowired; 21 import org.springframework.beans.factory.annotation.Autowired;
24 -import org.springframework.beans.factory.annotation.Qualifier;  
25 import org.springframework.context.annotation.DependsOn; 22 import org.springframework.context.annotation.DependsOn;
26 -import org.springframework.context.annotation.Lazy;  
27 import org.springframework.lang.Nullable; 23 import org.springframework.lang.Nullable;
28 import org.springframework.stereotype.Component; 24 import org.springframework.stereotype.Component;
29 import org.springframework.util.ObjectUtils; 25 import org.springframework.util.ObjectUtils;
@@ -638,7 +634,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -638,7 +634,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
638 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); 634 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
639 if (mediaServerItem != null) { 635 if (mediaServerItem != null) {
640 mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); 636 mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
641 - zlmrtpServerFactory.closeRTPServer(mediaServerItem, sendRtpItem.getStreamId()); 637 + zlmrtpServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStreamId());
642 } 638 }
643 SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem); 639 SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem);
644 if (byeRequest == null) { 640 if (byeRequest == null) {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
@@ -10,8 +10,8 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; @@ -10,8 +10,8 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
10 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; 10 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
11 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; 11 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
12 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; 12 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
13 -import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;  
14 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; 13 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
  14 +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
15 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 15 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
16 import com.genersoft.iot.vmp.service.IMediaServerService; 16 import com.genersoft.iot.vmp.service.IMediaServerService;
17 import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; 17 import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
@@ -33,7 +33,8 @@ import javax.sip.header.FromHeader; @@ -33,7 +33,8 @@ import javax.sip.header.FromHeader;
33 import javax.sip.header.HeaderAddress; 33 import javax.sip.header.HeaderAddress;
34 import javax.sip.header.ToHeader; 34 import javax.sip.header.ToHeader;
35 import java.text.ParseException; 35 import java.text.ParseException;
36 -import java.util.*; 36 +import java.util.HashMap;
  37 +import java.util.Map;
37 38
38 /** 39 /**
39 * SIP命令类型: ACK请求 40 * SIP命令类型: ACK请求
@@ -63,6 +64,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In @@ -63,6 +64,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
63 private ZLMRTPServerFactory zlmrtpServerFactory; 64 private ZLMRTPServerFactory zlmrtpServerFactory;
64 65
65 @Autowired 66 @Autowired
  67 + private ZlmHttpHookSubscribe hookSubscribe;
  68 +
  69 + @Autowired
66 private IMediaServerService mediaServerService; 70 private IMediaServerService mediaServerService;
67 71
68 @Autowired 72 @Autowired
@@ -130,8 +134,18 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In @@ -130,8 +134,18 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
130 startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader); 134 startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader);
131 }); 135 });
132 }else { 136 }else {
133 - JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);  
134 - startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader); 137 + // 如果是非严格模式,需要关闭端口占用
  138 + JSONObject startSendRtpStreamResult = null;
  139 + if (sendRtpItem.getLocalPort() != 0) {
  140 + if (zlmrtpServerFactory.releasePort(mediaInfo, sendRtpItem.getSsrc())) {
  141 + startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
  142 + }
  143 + }else {
  144 + startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
  145 + }
  146 + if (startSendRtpStreamResult != null) {
  147 + startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader);
  148 + }
135 } 149 }
136 } 150 }
137 private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform, 151 private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform,
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -45,6 +45,7 @@ import javax.sip.header.CallIdHeader; @@ -45,6 +45,7 @@ import javax.sip.header.CallIdHeader;
45 import javax.sip.message.Response; 45 import javax.sip.message.Response;
46 import java.text.ParseException; 46 import java.text.ParseException;
47 import java.time.Instant; 47 import java.time.Instant;
  48 +import java.util.Random;
48 import java.util.Vector; 49 import java.util.Vector;
49 50
50 /** 51 /**
@@ -157,11 +158,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -157,11 +158,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
157 StreamProxyItem proxyByAppAndStream =null; 158 StreamProxyItem proxyByAppAndStream =null;
158 // 不是通道可能是直播流 159 // 不是通道可能是直播流
159 if (channel != null && gbStream == null) { 160 if (channel != null && gbStream == null) {
160 -// if (channel.getStatus() == 0) {  
161 -// logger.info("通道离线,返回400");  
162 -// responseAck(request, Response.BAD_REQUEST, "channel [" + channel.getChannelId() + "] offline");  
163 -// return;  
164 -// }  
165 // 通道存在,发100,TRYING 161 // 通道存在,发100,TRYING
166 try { 162 try {
167 responseAck(request, Response.TRYING); 163 responseAck(request, Response.TRYING);
@@ -385,7 +381,12 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -385,7 +381,12 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
385 } else { 381 } else {
386 content.append("t=0 0\r\n"); 382 content.append("t=0 0\r\n");
387 } 383 }
388 - content.append("m=video " + sendRtpItem.getLocalPort() + " RTP/AVP 96\r\n"); 384 + int localPort = sendRtpItem.getLocalPort();
  385 + if (localPort == 0) {
  386 + // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口
  387 + localPort = new Random().nextInt(65535) + 1;
  388 + }
  389 + content.append("m=video " + localPort + " RTP/AVP 96\r\n");
389 content.append("a=sendonly\r\n"); 390 content.append("a=sendonly\r\n");
390 content.append("a=rtpmap:96 PS/90000\r\n"); 391 content.append("a=rtpmap:96 PS/90000\r\n");
391 content.append("y=" + sendRtpItem.getSsrc() + "\r\n"); 392 content.append("y=" + sendRtpItem.getSsrc() + "\r\n");
@@ -476,9 +477,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -476,9 +477,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
476 477
477 // 写入redis, 超时时回复 478 // 写入redis, 超时时回复
478 redisCatchStorage.updateSendRTPSever(sendRtpItem); 479 redisCatchStorage.updateSendRTPSever(sendRtpItem);
  480 + MediaServerItem finalMediaServerItem = mediaServerItem;
479 playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> { 481 playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> {
480 logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId); 482 logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId);
481 redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); 483 redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
  484 + zlmrtpServerFactory.releasePort(finalMediaServerItem, sendRtpItem.getSsrc());
482 }, null); 485 }, null);
483 } else { 486 } else {
484 sendRtpItem.setStreamId(playTransaction.getStream()); 487 sendRtpItem.setStreamId(playTransaction.getStream());
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -626,6 +626,32 @@ public class ZLMHttpHookListener { @@ -626,6 +626,32 @@ public class ZLMHttpHookListener {
626 return ret; 626 return ret;
627 } 627 }
628 628
  629 + /**
  630 + * rtpServer收流超时
  631 + */
  632 + @ResponseBody
  633 + @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
  634 + public JSONObject onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param){
  635 + System.out.println(param);
  636 + logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
  637 +
  638 + JSONObject ret = new JSONObject();
  639 + ret.put("code", 0);
  640 + ret.put("msg", "success");
  641 +
  642 + taskExecutor.execute(()->{
  643 + JSONObject json = (JSONObject) JSON.toJSON(param);
  644 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
  645 + if (subscribes != null && subscribes.size() > 0) {
  646 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  647 + subscribe.response(null, json);
  648 + }
  649 + }
  650 + });
  651 +
  652 + return ret;
  653 + }
  654 +
629 private Map<String, String> urlParamToMap(String params) { 655 private Map<String, String> urlParamToMap(String params) {
630 HashMap<String, String> map = new HashMap<>(); 656 HashMap<String, String> map = new HashMap<>();
631 if (ObjectUtils.isEmpty(params)) { 657 if (ObjectUtils.isEmpty(params)) {
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
1 package com.genersoft.iot.vmp.media.zlm; 1 package com.genersoft.iot.vmp.media.zlm;
2 2
  3 +import com.alibaba.fastjson2.JSON;
3 import com.alibaba.fastjson2.JSONArray; 4 import com.alibaba.fastjson2.JSONArray;
4 import com.alibaba.fastjson2.JSONObject; 5 import com.alibaba.fastjson2.JSONObject;
5 import com.genersoft.iot.vmp.conf.UserSetting; 6 import com.genersoft.iot.vmp.conf.UserSetting;
6 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; 7 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
7 -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 8 +import com.genersoft.iot.vmp.media.zlm.dto.*;
8 import org.slf4j.Logger; 9 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory; 10 import org.slf4j.LoggerFactory;
10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.beans.factory.annotation.Autowired;
11 import org.springframework.stereotype.Component; 12 import org.springframework.stereotype.Component;
12 -import org.springframework.util.ObjectUtils;  
13 13
14 import java.util.*; 14 import java.util.*;
15 15
@@ -24,6 +24,9 @@ public class ZLMRTPServerFactory { @@ -24,6 +24,9 @@ public class ZLMRTPServerFactory {
24 @Autowired 24 @Autowired
25 private UserSetting userSetting; 25 private UserSetting userSetting;
26 26
  27 + @Autowired
  28 + private ZlmHttpHookSubscribe hookSubscribe;
  29 +
27 private int[] portRangeArray = new int[2]; 30 private int[] portRangeArray = new int[2];
28 31
29 public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List<Integer> usedFreelist) { 32 public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List<Integer> usedFreelist) {
@@ -141,7 +144,7 @@ public class ZLMRTPServerFactory { @@ -141,7 +144,7 @@ public class ZLMRTPServerFactory {
141 return result; 144 return result;
142 } 145 }
143 146
144 - public boolean closeRTPServer(MediaServerItem serverItem, String streamId) { 147 + public boolean closeRtpServer(MediaServerItem serverItem, String streamId) {
145 boolean result = false; 148 boolean result = false;
146 if (serverItem !=null){ 149 if (serverItem !=null){
147 Map<String, Object> param = new HashMap<>(); 150 Map<String, Object> param = new HashMap<>();
@@ -161,32 +164,6 @@ public class ZLMRTPServerFactory { @@ -161,32 +164,6 @@ public class ZLMRTPServerFactory {
161 return result; 164 return result;
162 } 165 }
163 166
164 -// private int getPortFromportRange(MediaServerItem mediaServerItem) {  
165 -// int currentPort = mediaServerItem.getCurrentPort();  
166 -// if (currentPort == 0) {  
167 -// String[] portRangeStrArray = mediaServerItem.getSendRtpPortRange().split(",");  
168 -// if (portRangeStrArray.length != 2) {  
169 -// portRangeArray[0] = 30000;  
170 -// portRangeArray[1] = 30500;  
171 -// }else {  
172 -// portRangeArray[0] = Integer.parseInt(portRangeStrArray[0]);  
173 -// portRangeArray[1] = Integer.parseInt(portRangeStrArray[1]);  
174 -// }  
175 -// }  
176 -//  
177 -// if (currentPort == 0 || currentPort++ > portRangeArray[1]) {  
178 -// currentPort = portRangeArray[0];  
179 -// mediaServerItem.setCurrentPort(currentPort);  
180 -// return portRangeArray[0];  
181 -// } else {  
182 -// if (currentPort % 2 == 1) {  
183 -// currentPort++;  
184 -// }  
185 -// currentPort++;  
186 -// mediaServerItem.setCurrentPort(currentPort);  
187 -// return currentPort;  
188 -// }  
189 -// }  
190 167
191 /** 168 /**
192 * 创建一个国标推流 169 * 创建一个国标推流
@@ -200,21 +177,15 @@ public class ZLMRTPServerFactory { @@ -200,21 +177,15 @@ public class ZLMRTPServerFactory {
200 */ 177 */
201 public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){ 178 public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){
202 179
203 - // 使用RTPServer 功能找一个可用的端口  
204 - String sendRtpPortRange = serverItem.getSendRtpPortRange();  
205 - if (ObjectUtils.isEmpty(sendRtpPortRange)) {  
206 - return null;  
207 - }  
208 - String[] portRangeStrArray = serverItem.getSendRtpPortRange().split(",");  
209 - int localPort = -1;  
210 - if (portRangeStrArray.length != 2) {  
211 - localPort = getFreePort(serverItem, 30000, 30500, null);  
212 - }else {  
213 - localPort = getFreePort(serverItem, Integer.parseInt(portRangeStrArray[0]), Integer.parseInt(portRangeStrArray[1]), null);  
214 - }  
215 - if (localPort == -1) {  
216 - logger.error("没有可用的端口");  
217 - return null; 180 + // 默认为随机端口
  181 + int localPort = 0;
  182 + if (userSetting.getGbSendStreamStrict()) {
  183 + if (userSetting.getGbSendStreamStrict()) {
  184 + localPort = keepPort(serverItem, ssrc);
  185 + if (localPort == 0) {
  186 + return null;
  187 + }
  188 + }
218 } 189 }
219 SendRtpItem sendRtpItem = new SendRtpItem(); 190 SendRtpItem sendRtpItem = new SendRtpItem();
220 sendRtpItem.setIp(ip); 191 sendRtpItem.setIp(ip);
@@ -242,21 +213,13 @@ public class ZLMRTPServerFactory { @@ -242,21 +213,13 @@ public class ZLMRTPServerFactory {
242 * @return SendRtpItem 213 * @return SendRtpItem
243 */ 214 */
244 public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){ 215 public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){
245 - // 使用RTPServer 功能找一个可用的端口  
246 - String sendRtpPortRange = serverItem.getSendRtpPortRange();  
247 - if (ObjectUtils.isEmpty(sendRtpPortRange)) {  
248 - return null;  
249 - }  
250 - String[] portRangeStrArray = serverItem.getSendRtpPortRange().split(",");  
251 - int localPort = -1;  
252 - if (portRangeStrArray.length != 2) {  
253 - localPort = getFreePort(serverItem, 30000, 30500, null);  
254 - }else {  
255 - localPort = getFreePort(serverItem, Integer.parseInt(portRangeStrArray[0]), Integer.parseInt(portRangeStrArray[1]), null);  
256 - }  
257 - if (localPort == -1) {  
258 - logger.error("没有可用的端口");  
259 - return null; 216 + // 默认为随机端口
  217 + int localPort = 0;
  218 + if (userSetting.getGbSendStreamStrict()) {
  219 + localPort = keepPort(serverItem, ssrc);
  220 + if (localPort == 0) {
  221 + return null;
  222 + }
260 } 223 }
261 SendRtpItem sendRtpItem = new SendRtpItem(); 224 SendRtpItem sendRtpItem = new SendRtpItem();
262 sendRtpItem.setIp(ip); 225 sendRtpItem.setIp(ip);
@@ -274,6 +237,42 @@ public class ZLMRTPServerFactory { @@ -274,6 +237,42 @@ public class ZLMRTPServerFactory {
274 } 237 }
275 238
276 /** 239 /**
  240 + * 保持端口,直到需要需要发流时再释放
  241 + */
  242 + public int keepPort(MediaServerItem serverItem, String ssrc) {
  243 + int localPort = 0;
  244 + Map<String, Object> param = new HashMap<>(3);
  245 + param.put("port", 0);
  246 + param.put("enable_tcp", 1);
  247 + param.put("stream_id", ssrc);
  248 + JSONObject jsonObject = zlmresTfulUtils.openRtpServer(serverItem, param);
  249 + if (jsonObject.getInteger("code") == 0) {
  250 + localPort = jsonObject.getInteger("port");
  251 + HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId());
  252 + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
  253 + hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout,
  254 + (MediaServerItem mediaServerItem, JSONObject response)->{
  255 + logger.info("[上级点播] {}->监听端口到期继续保持监听", ssrc);
  256 + keepPort(serverItem, ssrc);
  257 + });
  258 + }
  259 + logger.info("[上级点播] {}->监听端口: {}", ssrc, localPort);
  260 + return localPort;
  261 + }
  262 +
  263 + /**
  264 + * 释放保持的端口
  265 + */
  266 + public boolean releasePort(MediaServerItem serverItem, String ssrc) {
  267 + logger.info("[上级点播] {}->释放监听端口,等待推流", ssrc);
  268 + boolean closeRTPServerResult = closeRtpServer(serverItem, ssrc);
  269 + HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId());
  270 + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
  271 + hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout);
  272 + return closeRTPServerResult;
  273 + }
  274 +
  275 + /**
277 * 调用zlm RESTFUL API —— startSendRtp 276 * 调用zlm RESTFUL API —— startSendRtp
278 */ 277 */
279 public JSONObject startSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) { 278 public JSONObject startSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
@@ -333,7 +332,7 @@ public class ZLMRTPServerFactory { @@ -333,7 +332,7 @@ public class ZLMRTPServerFactory {
333 result= true; 332 result= true;
334 logger.info("[停止RTP推流] 成功"); 333 logger.info("[停止RTP推流] 成功");
335 } else { 334 } else {
336 - logger.error("[停止RTP推流] 失败: {}, 参数:{}->\r\n{}",jsonObject.getString("msg"),jsonObject.toJSONString(param)); 335 + logger.error("[停止RTP推流] 失败: {}, 参数:{}->\r\n{}",jsonObject.getString("msg"), JSON.toJSON(param), jsonObject);
337 } 336 }
338 return result; 337 return result;
339 } 338 }
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
@@ -18,7 +18,11 @@ import org.springframework.core.annotation.Order; @@ -18,7 +18,11 @@ import org.springframework.core.annotation.Order;
18 import org.springframework.scheduling.annotation.Async; 18 import org.springframework.scheduling.annotation.Async;
19 import org.springframework.stereotype.Component; 19 import org.springframework.stereotype.Component;
20 20
21 -import java.util.*; 21 +import java.util.HashMap;
  22 +import java.util.List;
  23 +import java.util.Map;
  24 +import java.util.Set;
  25 +import java.util.concurrent.ConcurrentHashMap;
22 26
23 @Component 27 @Component
24 @Order(value=1) 28 @Order(value=1)
@@ -73,8 +77,6 @@ public class ZLMRunner implements CommandLineRunner { @@ -73,8 +77,6 @@ public class ZLMRunner implements CommandLineRunner {
73 } 77 }
74 }); 78 });
75 79
76 -  
77 -  
78 // 获取zlm信息 80 // 获取zlm信息
79 logger.info("[zlm] 等待默认zlm中..."); 81 logger.info("[zlm] 等待默认zlm中...");
80 82
@@ -87,7 +89,7 @@ public class ZLMRunner implements CommandLineRunner { @@ -87,7 +89,7 @@ public class ZLMRunner implements CommandLineRunner {
87 } 89 }
88 for (MediaServerItem mediaServerItem : all) { 90 for (MediaServerItem mediaServerItem : all) {
89 if (startGetMedia == null) { 91 if (startGetMedia == null) {
90 - startGetMedia = new HashMap<>(); 92 + startGetMedia = new ConcurrentHashMap<>();
91 } 93 }
92 startGetMedia.put(mediaServerItem.getId(), true); 94 startGetMedia.put(mediaServerItem.getId(), true);
93 connectZlmServer(mediaServerItem); 95 connectZlmServer(mediaServerItem);
@@ -95,7 +97,7 @@ public class ZLMRunner implements CommandLineRunner { @@ -95,7 +97,7 @@ public class ZLMRunner implements CommandLineRunner {
95 } 97 }
96 String taskKey = "zlm-connect-timeout"; 98 String taskKey = "zlm-connect-timeout";
97 dynamicTask.startDelay(taskKey, ()->{ 99 dynamicTask.startDelay(taskKey, ()->{
98 - if (startGetMedia != null) { 100 + if (startGetMedia != null && startGetMedia.size() > 0) {
99 Set<String> allZlmId = startGetMedia.keySet(); 101 Set<String> allZlmId = startGetMedia.keySet();
100 for (String id : allZlmId) { 102 for (String id : allZlmId) {
101 logger.error("[ {} ]]主动连接失败,不再尝试连接", id); 103 logger.error("[ {} ]]主动连接失败,不再尝试连接", id);
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java
@@ -24,6 +24,17 @@ public class HookSubscribeFactory { @@ -24,6 +24,17 @@ public class HookSubscribeFactory {
24 return hookSubscribe; 24 return hookSubscribe;
25 } 25 }
26 26
  27 + public static HookSubscribeForRtpServerTimeout on_rtp_server_timeout(String stream, String ssrc, String mediaServerId) {
  28 + HookSubscribeForRtpServerTimeout hookSubscribe = new HookSubscribeForRtpServerTimeout();
  29 + JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
  30 + subscribeKey.put("stream_id", stream);
  31 + subscribeKey.put("ssrc", ssrc);
  32 + subscribeKey.put("mediaServerId", mediaServerId);
  33 + hookSubscribe.setContent(subscribeKey);
  34 +
  35 + return hookSubscribe;
  36 + }
  37 +
27 public static HookSubscribeForServerStarted on_server_started() { 38 public static HookSubscribeForServerStarted on_server_started() {
28 HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted(); 39 HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted();
29 hookSubscribe.setContent(new JSONObject()); 40 hookSubscribe.setContent(new JSONObject());
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForRtpServerTimeout.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.dto;
  2 +
  3 +import com.alibaba.fastjson2.JSONObject;
  4 +import com.alibaba.fastjson2.annotation.JSONField;
  5 +
  6 +import java.time.Instant;
  7 +
  8 +/**
  9 + * hook订阅-收流超时
  10 + * @author lin
  11 + */
  12 +public class HookSubscribeForRtpServerTimeout implements IHookSubscribe{
  13 +
  14 + private HookType hookType = HookType.on_rtp_server_timeout;
  15 +
  16 + private JSONObject content;
  17 +
  18 + @JSONField(format="yyyy-MM-dd HH:mm:ss")
  19 + private Instant expires;
  20 +
  21 + @Override
  22 + public HookType getHookType() {
  23 + return hookType;
  24 + }
  25 +
  26 + @Override
  27 + public JSONObject getContent() {
  28 + return content;
  29 + }
  30 +
  31 + public void setContent(JSONObject content) {
  32 + this.content = content;
  33 + }
  34 +
  35 + @Override
  36 + public Instant getExpires() {
  37 + return expires;
  38 + }
  39 +
  40 + @Override
  41 + public void setExpires(Instant expires) {
  42 + this.expires = expires;
  43 + }
  44 +}
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java
@@ -15,6 +15,7 @@ public class HookSubscribeForStreamChange implements IHookSubscribe{ @@ -15,6 +15,7 @@ public class HookSubscribeForStreamChange implements IHookSubscribe{
15 15
16 private JSONObject content; 16 private JSONObject content;
17 17
  18 + @JSONField(format="yyyy-MM-dd HH:mm:ss")
18 private Instant expires; 19 private Instant expires;
19 20
20 @Override 21 @Override
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java
@@ -19,5 +19,7 @@ public enum HookType { @@ -19,5 +19,7 @@ public enum HookType {
19 on_stream_none_reader, 19 on_stream_none_reader,
20 on_stream_not_found, 20 on_stream_not_found,
21 on_server_started, 21 on_server_started,
  22 +
  23 + on_rtp_server_timeout,
22 on_server_keepalive 24 on_server_keepalive
23 } 25 }
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
@@ -65,9 +65,6 @@ public class MediaServerItem{ @@ -65,9 +65,6 @@ public class MediaServerItem{
65 @Schema(description = "多端口RTP收流端口范围") 65 @Schema(description = "多端口RTP收流端口范围")
66 private String rtpPortRange; 66 private String rtpPortRange;
67 67
68 - @Schema(description = "RTP发流端口范围")  
69 - private String sendRtpPortRange;  
70 -  
71 @Schema(description = "assist服务端口") 68 @Schema(description = "assist服务端口")
72 private int recordAssistPort; 69 private int recordAssistPort;
73 70
@@ -118,7 +115,6 @@ public class MediaServerItem{ @@ -118,7 +115,6 @@ public class MediaServerItem{
118 hookAliveInterval = zlmServerConfig.getHookAliveInterval(); 115 hookAliveInterval = zlmServerConfig.getHookAliveInterval();
119 rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口 116 rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口
120 rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号 117 rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号
121 - sendRtpPortRange = "30000,30500"; // 默认使用30000,30500作为级联时发送流的端口号  
122 recordAssistPort = 0; // 默认关闭 118 recordAssistPort = 0; // 默认关闭
123 119
124 } 120 }
@@ -323,14 +319,6 @@ public class MediaServerItem{ @@ -323,14 +319,6 @@ public class MediaServerItem{
323 this.lastKeepaliveTime = lastKeepaliveTime; 319 this.lastKeepaliveTime = lastKeepaliveTime;
324 } 320 }
325 321
326 - public String getSendRtpPortRange() {  
327 - return sendRtpPortRange;  
328 - }  
329 -  
330 - public void setSendRtpPortRange(String sendRtpPortRange) {  
331 - this.sendRtpPortRange = sendRtpPortRange;  
332 - }  
333 -  
334 public Float getHookAliveInterval() { 322 public Float getHookAliveInterval() {
335 return hookAliveInterval; 323 return hookAliveInterval;
336 } 324 }
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnRtpServerTimeoutHookParam.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.dto.hook;
  2 +
  3 +/**
  4 + * zlm hook事件中的on_rtp_server_timeout事件的参数
  5 + * @author lin
  6 + */
  7 +public class OnRtpServerTimeoutHookParam extends HookParam{
  8 + private int local_port;
  9 + private String stream_id;
  10 + private int tcpMode;
  11 + private boolean re_use_port;
  12 + private String ssrc;
  13 +
  14 + public int getLocal_port() {
  15 + return local_port;
  16 + }
  17 +
  18 + public void setLocal_port(int local_port) {
  19 + this.local_port = local_port;
  20 + }
  21 +
  22 + public String getStream_id() {
  23 + return stream_id;
  24 + }
  25 +
  26 + public void setStream_id(String stream_id) {
  27 + this.stream_id = stream_id;
  28 + }
  29 +
  30 + public int getTcpMode() {
  31 + return tcpMode;
  32 + }
  33 +
  34 + public void setTcpMode(int tcpMode) {
  35 + this.tcpMode = tcpMode;
  36 + }
  37 +
  38 + public boolean isRe_use_port() {
  39 + return re_use_port;
  40 + }
  41 +
  42 + public void setRe_use_port(boolean re_use_port) {
  43 + this.re_use_port = re_use_port;
  44 + }
  45 +
  46 + public String getSsrc() {
  47 + return ssrc;
  48 + }
  49 +
  50 + public void setSsrc(String ssrc) {
  51 + this.ssrc = ssrc;
  52 + }
  53 +}
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
@@ -4,13 +4,12 @@ import com.genersoft.iot.vmp.conf.DynamicTask; @@ -4,13 +4,12 @@ import com.genersoft.iot.vmp.conf.DynamicTask;
4 import com.genersoft.iot.vmp.gb28181.bean.*; 4 import com.genersoft.iot.vmp.gb28181.bean.*;
5 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 5 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
6 import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; 6 import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
  7 +import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;
  8 +import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
7 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; 9 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; 10 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler;
9 -import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;  
10 import com.genersoft.iot.vmp.service.IDeviceChannelService; 11 import com.genersoft.iot.vmp.service.IDeviceChannelService;
11 import com.genersoft.iot.vmp.service.IDeviceService; 12 import com.genersoft.iot.vmp.service.IDeviceService;
12 -import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;  
13 -import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;  
14 import com.genersoft.iot.vmp.service.IMediaServerService; 13 import com.genersoft.iot.vmp.service.IMediaServerService;
15 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 14 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
16 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 15 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
@@ -24,12 +23,10 @@ import org.slf4j.Logger; @@ -24,12 +23,10 @@ import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory; 23 import org.slf4j.LoggerFactory;
25 import org.springframework.beans.factory.annotation.Autowired; 24 import org.springframework.beans.factory.annotation.Autowired;
26 import org.springframework.jdbc.datasource.DataSourceTransactionManager; 25 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
27 -import org.springframework.jdbc.support.incrementer.AbstractIdentityColumnMaxValueIncrementer;  
28 import org.springframework.stereotype.Service; 26 import org.springframework.stereotype.Service;
29 import org.springframework.transaction.TransactionDefinition; 27 import org.springframework.transaction.TransactionDefinition;
30 import org.springframework.transaction.TransactionStatus; 28 import org.springframework.transaction.TransactionStatus;
31 import org.springframework.util.ObjectUtils; 29 import org.springframework.util.ObjectUtils;
32 -import org.springframework.util.StringUtils;  
33 30
34 import javax.sip.InvalidArgumentException; 31 import javax.sip.InvalidArgumentException;
35 import javax.sip.SipException; 32 import javax.sip.SipException;
@@ -171,7 +168,7 @@ public class DeviceServiceImpl implements IDeviceService { @@ -171,7 +168,7 @@ public class DeviceServiceImpl implements IDeviceService {
171 redisCatchStorage.updateDevice(device); 168 redisCatchStorage.updateDevice(device);
172 deviceMapper.update(device); 169 deviceMapper.update(device);
173 //进行通道离线 170 //进行通道离线
174 - deviceChannelMapper.offlineByDeviceId(deviceId); 171 +// deviceChannelMapper.offlineByDeviceId(deviceId);
175 // 离线释放所有ssrc 172 // 离线释放所有ssrc
176 List<SsrcTransaction> ssrcTransactions = streamSession.getSsrcTransactionForAll(deviceId, null, null, null); 173 List<SsrcTransaction> ssrcTransactions = streamSession.getSsrcTransactionForAll(deviceId, null, null, null);
177 if (ssrcTransactions != null && ssrcTransactions.size() > 0) { 174 if (ssrcTransactions != null && ssrcTransactions.size() > 0) {
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -168,7 +168,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -168,7 +168,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
168 if (mediaServerItem == null) { 168 if (mediaServerItem == null) {
169 return; 169 return;
170 } 170 }
171 - zlmrtpServerFactory.closeRTPServer(mediaServerItem, streamId); 171 + zlmrtpServerFactory.closeRtpServer(mediaServerItem, streamId);
172 releaseSsrc(mediaServerItem.getId(), streamId); 172 releaseSsrc(mediaServerItem.getId(), streamId);
173 } 173 }
174 174
@@ -535,6 +535,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -535,6 +535,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
535 param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex)); 535 param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex));
536 param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrex)); 536 param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrex));
537 param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrex)); 537 param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrex));
  538 + param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrex));
538 if (mediaServerItem.getRecordAssistPort() > 0) { 539 if (mediaServerItem.getRecordAssistPort() > 0) {
539 param.put("hook.on_record_mp4",String.format("http://127.0.0.1:%s/api/record/on_record_mp4", mediaServerItem.getRecordAssistPort())); 540 param.put("hook.on_record_mp4",String.format("http://127.0.0.1:%s/api/record/on_record_mp4", mediaServerItem.getRecordAssistPort()));
540 }else { 541 }else {
@@ -545,8 +546,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -545,8 +546,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
545 // 置0关闭此特性(推流断开会导致立即断开播放器) 546 // 置0关闭此特性(推流断开会导致立即断开播放器)
546 // 此参数不应大于播放器超时时间 547 // 此参数不应大于播放器超时时间
547 // 优化此消息以更快的收到流注销事件 548 // 优化此消息以更快的收到流注销事件
548 - param.put("general.continue_push_ms", "3000" );  
549 - param.put("general.publishToHls", "0" ); 549 + param.put("protocol.continue_push_ms", "3000" );
550 // 最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流, 550 // 最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流,
551 // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项 551 // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项
552 // param.put("general.wait_track_ready_ms", "3000" ); 552 // param.put("general.wait_track_ready_ms", "3000" );
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -283,7 +283,7 @@ public class PlayServiceImpl implements IPlayService { @@ -283,7 +283,7 @@ public class PlayServiceImpl implements IPlayService {
283 try { 283 try {
284 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { 284 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
285 logger.info("收到订阅消息: " + response.toJSONString()); 285 logger.info("收到订阅消息: " + response.toJSONString());
286 - System.out.println("停止超时任务: " + timeOutTaskKey); 286 + dynamicTask.stop(timeOutTaskKey);
287 287
288 // hook响应 288 // hook响应
289 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid); 289 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid);
src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
@@ -28,7 +28,6 @@ public interface MediaServerMapper { @@ -28,7 +28,6 @@ public interface MediaServerMapper {
28 "secret, " + 28 "secret, " +
29 "rtpEnable, " + 29 "rtpEnable, " +
30 "rtpPortRange, " + 30 "rtpPortRange, " +
31 - "sendRtpPortRange, " +  
32 "recordAssistPort, " + 31 "recordAssistPort, " +
33 "defaultServer, " + 32 "defaultServer, " +
34 "createTime, " + 33 "createTime, " +
@@ -52,7 +51,6 @@ public interface MediaServerMapper { @@ -52,7 +51,6 @@ public interface MediaServerMapper {
52 "'${secret}', " + 51 "'${secret}', " +
53 "${rtpEnable}, " + 52 "${rtpEnable}, " +
54 "'${rtpPortRange}', " + 53 "'${rtpPortRange}', " +
55 - "'${sendRtpPortRange}', " +  
56 "${recordAssistPort}, " + 54 "${recordAssistPort}, " +
57 "${defaultServer}, " + 55 "${defaultServer}, " +
58 "'${createTime}', " + 56 "'${createTime}', " +
@@ -77,7 +75,6 @@ public interface MediaServerMapper { @@ -77,7 +75,6 @@ public interface MediaServerMapper {
77 "<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" + 75 "<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" +
78 "<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" + 76 "<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" +
79 "<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" + 77 "<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" +
80 - "<if test=\"sendRtpPortRange != null\">, sendRtpPortRange='${sendRtpPortRange}'</if>" +  
81 "<if test=\"secret != null\">, secret='${secret}'</if>" + 78 "<if test=\"secret != null\">, secret='${secret}'</if>" +
82 "<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" + 79 "<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" +
83 "<if test=\"hookAliveInterval != null\">, hookAliveInterval=${hookAliveInterval}</if>" + 80 "<if test=\"hookAliveInterval != null\">, hookAliveInterval=${hookAliveInterval}</if>" +
@@ -101,7 +98,6 @@ public interface MediaServerMapper { @@ -101,7 +98,6 @@ public interface MediaServerMapper {
101 "<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" + 98 "<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" +
102 "<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" + 99 "<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" +
103 "<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" + 100 "<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" +
104 - "<if test=\"sendRtpPortRange != null\">, sendRtpPortRange='${sendRtpPortRange}'</if>" +  
105 "<if test=\"secret != null\">, secret='${secret}'</if>" + 101 "<if test=\"secret != null\">, secret='${secret}'</if>" +
106 "<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" + 102 "<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" +
107 "<if test=\"hookAliveInterval != null\">, hookAliveInterval=${hookAliveInterval}</if>" + 103 "<if test=\"hookAliveInterval != null\">, hookAliveInterval=${hookAliveInterval}</if>" +
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java
@@ -62,7 +62,9 @@ public class MediaController { @@ -62,7 +62,9 @@ public class MediaController {
62 if (callId != null) { 62 if (callId != null) {
63 // 权限校验 63 // 权限校验
64 StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream); 64 StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream);
65 - if (streamAuthorityInfo.getCallId().equals(callId)) { 65 + if (streamAuthorityInfo != null
  66 + && streamAuthorityInfo.getCallId() != null
  67 + && streamAuthorityInfo.getCallId().equals(callId)) {
66 authority = true; 68 authority = true;
67 }else { 69 }else {
68 throw new ControllerException(ErrorCode.ERROR400); 70 throw new ControllerException(ErrorCode.ERROR400);
src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
@@ -135,14 +135,8 @@ public class ServerController { @@ -135,14 +135,8 @@ public class ServerController {
135 MediaServerItem mediaServerItemInDatabase = mediaServerService.getOne(mediaServerItem.getId()); 135 MediaServerItem mediaServerItemInDatabase = mediaServerService.getOne(mediaServerItem.getId());
136 136
137 if (mediaServerItemInDatabase != null) { 137 if (mediaServerItemInDatabase != null) {
138 - if (ObjectUtils.isEmpty(mediaServerItemInDatabase.getSendRtpPortRange()) && ObjectUtils.isEmpty(mediaServerItem.getSendRtpPortRange())) {  
139 - mediaServerItem.setSendRtpPortRange("30000,30500");  
140 - }  
141 mediaServerService.update(mediaServerItem); 138 mediaServerService.update(mediaServerItem);
142 } else { 139 } else {
143 - if (ObjectUtils.isEmpty(mediaServerItem.getSendRtpPortRange())) {  
144 - mediaServerItem.setSendRtpPortRange("30000,30500");  
145 - }  
146 mediaServerService.add(mediaServerItem); 140 mediaServerService.add(mediaServerItem);
147 } 141 }
148 } 142 }
src/main/resources/all-application.yml
@@ -192,6 +192,9 @@ user-settings: @@ -192,6 +192,9 @@ user-settings:
192 stream-on-demand: true 192 stream-on-demand: true
193 # 推流鉴权, 默认开启 193 # 推流鉴权, 默认开启
194 push-authority: true 194 push-authority: true
  195 + # 国标级联发流严格模式,严格模式会使用与sdp信息中一致的端口发流,端口共享media.rtp.port-range,这会损失一些性能,
  196 + # 非严格模式使用随机端口发流,性能更好, 默认关闭
  197 + gb-send-stream-strict: false
195 198
196 # 关闭在线文档(生产环境建议关闭) 199 # 关闭在线文档(生产环境建议关闭)
197 springdoc: 200 springdoc:
web_src/src/components/dialog/MediaServerEdit.vue
@@ -89,11 +89,6 @@ @@ -89,11 +89,6 @@
89 - 89 -
90 <el-input v-model="rtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input> 90 <el-input v-model="rtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input>
91 </el-form-item> 91 </el-form-item>
92 - <el-form-item label="推流端口" prop="sendRtpPortRange1">  
93 - <el-input v-model="sendRtpPortRange1" placeholder="起始" @change="portRangeChange" clearable style="width: 100px" prop="sendRtpPortRange1" :disabled="mediaServerForm.defaultServer"></el-input>  
94 - -  
95 - <el-input v-model="sendRtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="sendRtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input>  
96 - </el-form-item>  
97 <el-form-item label="录像管理服务端口" prop="recordAssistPort"> 92 <el-form-item label="录像管理服务端口" prop="recordAssistPort">
98 <el-input v-model.number="mediaServerForm.recordAssistPort" :disabled="mediaServerForm.defaultServer"> 93 <el-input v-model.number="mediaServerForm.recordAssistPort" :disabled="mediaServerForm.defaultServer">
99 <!-- <el-button v-if="mediaServerForm.recordAssistPort > 0" slot="append" type="primary" @click="checkRecordServer">测试</el-button>--> 94 <!-- <el-button v-if="mediaServerForm.recordAssistPort > 0" slot="append" type="primary" @click="checkRecordServer">测试</el-button>-->
@@ -177,15 +172,12 @@ export default { @@ -177,15 +172,12 @@ export default {
177 rtmpSSlPort: "", 172 rtmpSSlPort: "",
178 rtpEnable: false, 173 rtpEnable: false,
179 rtpPortRange: "", 174 rtpPortRange: "",
180 - sendRtpPortRange: "",  
181 rtpProxyPort: "", 175 rtpProxyPort: "",
182 rtspPort: "", 176 rtspPort: "",
183 rtspSSLPort: "", 177 rtspSSLPort: "",
184 }, 178 },
185 rtpPortRange1:30000, 179 rtpPortRange1:30000,
186 rtpPortRange2:30500, 180 rtpPortRange2:30500,
187 - sendRtpPortRange1:30000,  
188 - sendRtpPortRange2:30500,  
189 181
190 rules: { 182 rules: {
191 ip: [{ required: true, validator: isValidIp, message: '请输入有效的IP地址', trigger: 'blur' }], 183 ip: [{ required: true, validator: isValidIp, message: '请输入有效的IP地址', trigger: 'blur' }],
@@ -196,8 +188,6 @@ export default { @@ -196,8 +188,6 @@ export default {
196 rtmpSSlPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], 188 rtmpSSlPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
197 rtpPortRange1: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], 189 rtpPortRange1: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
198 rtpPortRange2: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], 190 rtpPortRange2: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
199 - sendRtpPortRange1: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],  
200 - sendRtpPortRange2: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],  
201 rtpProxyPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], 191 rtpProxyPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
202 rtspPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], 192 rtspPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
203 rtspSSLPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], 193 rtspSSLPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
@@ -229,9 +219,6 @@ export default { @@ -229,9 +219,6 @@ export default {
229 this.rtpPortRange2 = rtpPortRange[1] 219 this.rtpPortRange2 = rtpPortRange[1]
230 } 220 }
231 } 221 }
232 - let sendRtpPortRange = this.mediaServerForm.sendRtpPortRange.split(",");  
233 - this.sendRtpPortRange1 = sendRtpPortRange[0]  
234 - this.sendRtpPortRange2 = sendRtpPortRange[1]  
235 } 222 }
236 }, 223 },
237 checkServer: function() { 224 checkServer: function() {
@@ -251,8 +238,6 @@ export default { @@ -251,8 +238,6 @@ export default {
251 that.mediaServerForm = data.data; 238 that.mediaServerForm = data.data;
252 that.mediaServerForm.httpPort = httpPort; 239 that.mediaServerForm.httpPort = httpPort;
253 that.mediaServerForm.autoConfig = true; 240 that.mediaServerForm.autoConfig = true;
254 - that.sendRtpPortRange1 = 30000  
255 - that.sendRtpPortRange2 = 30500  
256 that.rtpPortRange1 = 30000 241 that.rtpPortRange1 = 30000
257 that.rtpPortRange2 = 30500 242 that.rtpPortRange2 = 30500
258 that.serverCheck = 1; 243 that.serverCheck = 1;
@@ -336,13 +321,10 @@ export default { @@ -336,13 +321,10 @@ export default {
336 rtmpSSlPort: "", 321 rtmpSSlPort: "",
337 rtpEnable: false, 322 rtpEnable: false,
338 rtpPortRange: "", 323 rtpPortRange: "",
339 - sendRtpPortRange: "",  
340 rtpProxyPort: "", 324 rtpProxyPort: "",
341 rtspPort: "", 325 rtspPort: "",
342 rtspSSLPort: "", 326 rtspSSLPort: "",
343 }; 327 };
344 - this.sendRtpPortRange1 = 30000;  
345 - this.sendRtpPortRange2 = 30500;  
346 this.rtpPortRange1 = 30500; 328 this.rtpPortRange1 = 30500;
347 this.rtpPortRange2 = 30500; 329 this.rtpPortRange2 = 30500;
348 this.listChangeCallback = null 330 this.listChangeCallback = null
@@ -367,9 +349,7 @@ export default { @@ -367,9 +349,7 @@ export default {
367 } 349 }
368 }, 350 },
369 portRangeChange: function() { 351 portRangeChange: function() {
370 - this.mediaServerForm.sendRtpPortRange = this.sendRtpPortRange1 + "," + this.sendRtpPortRange2  
371 this.mediaServerForm.rtpPortRange = this.rtpPortRange1 + "," + this.rtpPortRange2 352 this.mediaServerForm.rtpPortRange = this.rtpPortRange1 + "," + this.rtpPortRange2
372 - console.log(this.mediaServerForm.sendRtpPortRange)  
373 console.log(this.mediaServerForm.rtpPortRange) 353 console.log(this.mediaServerForm.rtpPortRange)
374 } 354 }
375 }, 355 },