Commit d340a37a00c8d5ea2605ca0f40a920efbeb9546f

Authored by 648540858
2 parents 8870f5f5 c04de4cd

Merge branch 'wvp-28181-2.0' into wvp-28181-2.0-multi-network

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
Showing 33 changed files with 370 additions and 260 deletions
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 11
12 <groupId>com.genersoft</groupId> 12 <groupId>com.genersoft</groupId>
13 <artifactId>wvp-pro</artifactId> 13 <artifactId>wvp-pro</artifactId>
14 - <version>2.3.2</version> 14 + <version>2.6.6</version>
15 <name>web video platform</name> 15 <name>web video platform</name>
16 <description>国标28181视频平台</description> 16 <description>国标28181视频平台</description>
17 17
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/DynamicTask.java
1 package com.genersoft.iot.vmp.conf; 1 package com.genersoft.iot.vmp.conf;
2 2
3 -import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;  
4 import org.slf4j.Logger; 3 import org.slf4j.Logger;
5 import org.slf4j.LoggerFactory; 4 import org.slf4j.LoggerFactory;
6 -import org.springframework.beans.factory.annotation.Autowired;  
7 -import org.springframework.context.annotation.Bean;  
8 import org.springframework.scheduling.annotation.Scheduled; 5 import org.springframework.scheduling.annotation.Scheduled;
9 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 6 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
10 import org.springframework.stereotype.Component; 7 import org.springframework.stereotype.Component;
@@ -101,12 +98,14 @@ public class DynamicTask { @@ -101,12 +98,14 @@ public class DynamicTask {
101 } 98 }
102 } 99 }
103 100
104 - public void stop(String key) {  
105 - if (futureMap.get(key) != null && !futureMap.get(key).isCancelled()) {  
106 - futureMap.get(key).cancel(false); 101 + public boolean stop(String key) {
  102 + boolean result = false;
  103 + if (futureMap.get(key) != null && !futureMap.get(key).isCancelled() && !futureMap.get(key).isDone()) {
  104 + result = futureMap.get(key).cancel(false);
107 futureMap.remove(key); 105 futureMap.remove(key);
108 runnableMap.remove(key); 106 runnableMap.remove(key);
109 } 107 }
  108 + return result;
110 } 109 }
111 110
112 public boolean contains(String key) { 111 public boolean contains(String key) {
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
@@ -2,13 +2,11 @@ package com.genersoft.iot.vmp.conf; @@ -2,13 +2,11 @@ package com.genersoft.iot.vmp.conf;
2 2
3 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 3 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
4 import com.genersoft.iot.vmp.utils.DateUtil; 4 import com.genersoft.iot.vmp.utils.DateUtil;
5 -import com.genersoft.iot.vmp.vmanager.gb28181.device.DeviceQuery;  
6 import org.slf4j.Logger; 5 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory; 6 import org.slf4j.LoggerFactory;
8 import org.springframework.beans.factory.annotation.Value; 7 import org.springframework.beans.factory.annotation.Value;
9 import org.springframework.context.annotation.Configuration; 8 import org.springframework.context.annotation.Configuration;
10 import org.springframework.util.ObjectUtils; 9 import org.springframework.util.ObjectUtils;
11 -import org.springframework.util.StringUtils;  
12 10
13 import java.net.InetAddress; 11 import java.net.InetAddress;
14 import java.net.UnknownHostException; 12 import java.net.UnknownHostException;
@@ -75,10 +73,6 @@ public class MediaConfig{ @@ -75,10 +73,6 @@ public class MediaConfig{
75 @Value("${media.rtp.port-range}") 73 @Value("${media.rtp.port-range}")
76 private String rtpPortRange; 74 private String rtpPortRange;
77 75
78 -  
79 - @Value("${media.rtp.send-port-range}")  
80 - private String sendRtpPortRange;  
81 -  
82 @Value("${media.record-assist-port:0}") 76 @Value("${media.record-assist-port:0}")
83 private Integer recordAssistPort = 0; 77 private Integer recordAssistPort = 0;
84 78
@@ -191,10 +185,6 @@ public class MediaConfig{ @@ -191,10 +185,6 @@ public class MediaConfig{
191 return sipDomain; 185 return sipDomain;
192 } 186 }
193 187
194 - public String getSendRtpPortRange() {  
195 - return sendRtpPortRange;  
196 - }  
197 -  
198 public MediaServerItem getMediaSerItem(){ 188 public MediaServerItem getMediaSerItem(){
199 MediaServerItem mediaServerItem = new MediaServerItem(); 189 MediaServerItem mediaServerItem = new MediaServerItem();
200 mediaServerItem.setId(id); 190 mediaServerItem.setId(id);
@@ -214,9 +204,8 @@ public class MediaConfig{ @@ -214,9 +204,8 @@ public class MediaConfig{
214 mediaServerItem.setSecret(secret); 204 mediaServerItem.setSecret(secret);
215 mediaServerItem.setRtpEnable(rtpEnable); 205 mediaServerItem.setRtpEnable(rtpEnable);
216 mediaServerItem.setRtpPortRange(rtpPortRange); 206 mediaServerItem.setRtpPortRange(rtpPortRange);
217 - mediaServerItem.setSendRtpPortRange(sendRtpPortRange);  
218 mediaServerItem.setRecordAssistPort(recordAssistPort); 207 mediaServerItem.setRecordAssistPort(recordAssistPort);
219 - mediaServerItem.setHookAliveInterval(120); 208 + mediaServerItem.setHookAliveInterval(30.00f);
220 209
221 mediaServerItem.setCreateTime(DateUtil.getNow()); 210 mediaServerItem.setCreateTime(DateUtil.getNow());
222 mediaServerItem.setUpdateTime(DateUtil.getNow()); 211 mediaServerItem.setUpdateTime(DateUtil.getNow());
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
@@ -39,6 +39,8 @@ public class UserSetting { @@ -39,6 +39,8 @@ public class UserSetting {
39 39
40 private Boolean pushAuthority = Boolean.TRUE; 40 private Boolean pushAuthority = Boolean.TRUE;
41 41
  42 + private Boolean gbSendStreamStrict = Boolean.FALSE;
  43 +
42 private String serverId = "000000"; 44 private String serverId = "000000";
43 45
44 private String thirdPartyGBIdReg = "[\\s\\S]*"; 46 private String thirdPartyGBIdReg = "[\\s\\S]*";
@@ -176,4 +178,12 @@ public class UserSetting { @@ -176,4 +178,12 @@ public class UserSetting {
176 public void setPushAuthority(Boolean pushAuthority) { 178 public void setPushAuthority(Boolean pushAuthority) {
177 this.pushAuthority = pushAuthority; 179 this.pushAuthority = pushAuthority;
178 } 180 }
  181 +
  182 + public Boolean getGbSendStreamStrict() {
  183 + return gbSendStreamStrict;
  184 + }
  185 +
  186 + public void setGbSendStreamStrict(Boolean gbSendStreamStrict) {
  187 + this.gbSendStreamStrict = gbSendStreamStrict;
  188 + }
179 } 189 }
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;
@@ -13,15 +12,15 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; @@ -13,15 +12,15 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
13 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; 12 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
14 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; 13 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
15 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; 14 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
  15 +import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
16 import com.genersoft.iot.vmp.gb28181.utils.SipUtils; 16 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  17 +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
17 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; 18 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
18 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; 19 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
19 -import com.genersoft.iot.vmp.utils.DateUtil;  
20 -import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;  
21 -import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;  
22 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 20 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
23 import com.genersoft.iot.vmp.service.IMediaServerService; 21 import com.genersoft.iot.vmp.service.IMediaServerService;
24 import com.genersoft.iot.vmp.service.bean.SSRCInfo; 22 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
  23 +import com.genersoft.iot.vmp.utils.DateUtil;
25 import gov.nist.javax.sip.message.SIPRequest; 24 import gov.nist.javax.sip.message.SIPRequest;
26 import gov.nist.javax.sip.message.SIPResponse; 25 import gov.nist.javax.sip.message.SIPResponse;
27 import org.slf4j.Logger; 26 import org.slf4j.Logger;
@@ -31,16 +30,13 @@ import org.springframework.context.annotation.DependsOn; @@ -31,16 +30,13 @@ import org.springframework.context.annotation.DependsOn;
31 import org.springframework.stereotype.Component; 30 import org.springframework.stereotype.Component;
32 import org.springframework.util.ObjectUtils; 31 import org.springframework.util.ObjectUtils;
33 32
34 -import javax.sip.*;  
35 -import javax.sip.address.Address;  
36 -import javax.sip.address.SipURI;  
37 -import javax.sip.header.*;  
38 -import javax.sip.message.Message; 33 +import javax.sip.InvalidArgumentException;
  34 +import javax.sip.ResponseEvent;
  35 +import javax.sip.SipException;
  36 +import javax.sip.SipFactory;
  37 +import javax.sip.header.CallIdHeader;
39 import javax.sip.message.Request; 38 import javax.sip.message.Request;
40 -import javax.sip.message.Response;  
41 -import java.lang.reflect.Field;  
42 import java.text.ParseException; 39 import java.text.ParseException;
43 -import java.util.HashSet;  
44 40
45 /** 41 /**
46 * @description:设备能力接口,用于定义设备的控制、查询能力 42 * @description:设备能力接口,用于定义设备的控制、查询能力
@@ -173,7 +169,7 @@ public class SIPCommander implements ISIPCommander { @@ -173,7 +169,7 @@ public class SIPCommander implements ISIPCommander {
173 public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed, 169 public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,
174 int zoomSpeed) throws InvalidArgumentException, SipException, ParseException { 170 int zoomSpeed) throws InvalidArgumentException, SipException, ParseException {
175 String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed); 171 String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed);
176 - StringBuffer ptzXml = new StringBuffer(200); 172 + StringBuilder ptzXml = new StringBuilder(200);
177 String charset = device.getCharset(); 173 String charset = device.getCharset();
178 ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n"); 174 ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
179 ptzXml.append("<Control>\r\n"); 175 ptzXml.append("<Control>\r\n");
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -16,15 +16,12 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; @@ -16,15 +16,12 @@ 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.GPSMsgInfo; 17 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
18 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 18 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
19 -import gov.nist.javax.sip.SipProviderImpl;  
20 import gov.nist.javax.sip.message.MessageFactoryImpl; 19 import gov.nist.javax.sip.message.MessageFactoryImpl;
21 import gov.nist.javax.sip.message.SIPRequest; 20 import gov.nist.javax.sip.message.SIPRequest;
22 import org.slf4j.Logger; 21 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory; 22 import org.slf4j.LoggerFactory;
24 import org.springframework.beans.factory.annotation.Autowired; 23 import org.springframework.beans.factory.annotation.Autowired;
25 -import org.springframework.beans.factory.annotation.Qualifier;  
26 import org.springframework.context.annotation.DependsOn; 24 import org.springframework.context.annotation.DependsOn;
27 -import org.springframework.context.annotation.Lazy;  
28 import org.springframework.lang.Nullable; 25 import org.springframework.lang.Nullable;
29 import org.springframework.stereotype.Component; 26 import org.springframework.stereotype.Component;
30 import org.springframework.util.ObjectUtils; 27 import org.springframework.util.ObjectUtils;
@@ -639,7 +636,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -639,7 +636,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
639 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); 636 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
640 if (mediaServerItem != null) { 637 if (mediaServerItem != null) {
641 mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); 638 mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
642 - zlmrtpServerFactory.closeRTPServer(mediaServerItem, sendRtpItem.getStreamId()); 639 + zlmrtpServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStreamId());
643 } 640 }
644 SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(parentPlatform, sendRtpItem); 641 SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(parentPlatform, sendRtpItem);
645 if (byeRequest == null) { 642 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,6 +477,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -476,6 +477,7 @@ 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);
@@ -656,6 +658,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -656,6 +658,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
656 if (!platform.isStartOfflinePush()) { 658 if (!platform.isStartOfflinePush()) {
657 // 平台设置中关闭了拉起离线的推流则直接回复 659 // 平台设置中关闭了拉起离线的推流则直接回复
658 try { 660 try {
  661 + logger.info("[上级点播] 失败,推流设备未推流,channel: {}, app: {}, stream: {}", gbStream.getGbId(), gbStream.getApp(), gbStream.getStream());
659 responseAck(request, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing"); 662 responseAck(request, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing");
660 } catch (SipException | InvalidArgumentException | ParseException e) { 663 } catch (SipException | InvalidArgumentException | ParseException e) {
661 logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage()); 664 logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
@@ -85,7 +85,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen @@ -85,7 +85,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
85 String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword(); 85 String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword();
86 AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); 86 AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
87 if (authHead == null && !ObjectUtils.isEmpty(password)) { 87 if (authHead == null && !ObjectUtils.isEmpty(password)) {
88 - logger.info("[注册请求] 未携带授权头 回复401: {}", requestAddress); 88 + logger.info("[注册请求] 回复401: {}", requestAddress);
89 response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request); 89 response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
90 new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain()); 90 new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain());
91 sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); 91 sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
1 package com.genersoft.iot.vmp.media.zlm; 1 package com.genersoft.iot.vmp.media.zlm;
2 2
3 -import java.text.ParseException;  
4 -import java.util.HashMap;  
5 -import java.util.List;  
6 -import java.util.Map;  
7 -  
8 import com.alibaba.fastjson2.JSON; 3 import com.alibaba.fastjson2.JSON;
  4 +import com.alibaba.fastjson2.JSONObject;
9 import com.genersoft.iot.vmp.common.StreamInfo; 5 import com.genersoft.iot.vmp.common.StreamInfo;
10 import com.genersoft.iot.vmp.conf.UserSetting; 6 import com.genersoft.iot.vmp.conf.UserSetting;
11 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; 7 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
12 import com.genersoft.iot.vmp.gb28181.bean.*; 8 import com.genersoft.iot.vmp.gb28181.bean.*;
13 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 9 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
14 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 10 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  11 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
15 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; 12 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
16 -import com.genersoft.iot.vmp.media.zlm.dto.*; 13 +import com.genersoft.iot.vmp.media.zlm.dto.HookType;
  14 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  15 +import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
  16 +import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
17 import com.genersoft.iot.vmp.media.zlm.dto.hook.*; 17 import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
18 import com.genersoft.iot.vmp.service.*; 18 import com.genersoft.iot.vmp.service.*;
19 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 19 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -24,18 +24,15 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -24,18 +24,15 @@ import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.beans.factory.annotation.Qualifier; 24 import org.springframework.beans.factory.annotation.Qualifier;
25 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 25 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
26 import org.springframework.util.ObjectUtils; 26 import org.springframework.util.ObjectUtils;
27 -import org.springframework.web.bind.annotation.PostMapping;  
28 -import org.springframework.web.bind.annotation.RequestBody;  
29 -import org.springframework.web.bind.annotation.RequestMapping;  
30 -import org.springframework.web.bind.annotation.ResponseBody;  
31 -import org.springframework.web.bind.annotation.RestController;  
32 -  
33 -import com.alibaba.fastjson2.JSONObject;  
34 -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; 27 +import org.springframework.web.bind.annotation.*;
35 28
36 import javax.servlet.http.HttpServletRequest; 29 import javax.servlet.http.HttpServletRequest;
37 import javax.sip.InvalidArgumentException; 30 import javax.sip.InvalidArgumentException;
38 import javax.sip.SipException; 31 import javax.sip.SipException;
  32 +import java.text.ParseException;
  33 +import java.util.HashMap;
  34 +import java.util.List;
  35 +import java.util.Map;
39 36
40 /** 37 /**
41 * @description:针对 ZLMediaServer的hook事件监听 38 * @description:针对 ZLMediaServer的hook事件监听
@@ -186,7 +183,7 @@ public class ZLMHttpHookListener { @@ -186,7 +183,7 @@ public class ZLMHttpHookListener {
186 183
187 if (!"rtp".equals(param.getApp())) { 184 if (!"rtp".equals(param.getApp())) {
188 if (userSetting.getPushAuthority()) { 185 if (userSetting.getPushAuthority()) {
189 -// 推流鉴权 186 + // 推流鉴权
190 if (param.getParams() == null) { 187 if (param.getParams() == null) {
191 logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)"); 188 logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
192 ret.put("code", 401); 189 ret.put("code", 401);
@@ -571,6 +568,8 @@ public class ZLMHttpHookListener { @@ -571,6 +568,8 @@ public class ZLMHttpHookListener {
571 public JSONObject onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject){ 568 public JSONObject onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject){
572 569
573 jsonObject.put("ip", request.getRemoteAddr()); 570 jsonObject.put("ip", request.getRemoteAddr());
  571 + System.out.println(jsonObject.toJSONString()
  572 + );
574 ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject); 573 ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
575 zlmServerConfig.setIp(request.getRemoteAddr()); 574 zlmServerConfig.setIp(request.getRemoteAddr());
576 logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId()); 575 logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
@@ -627,6 +626,32 @@ public class ZLMHttpHookListener { @@ -627,6 +626,32 @@ public class ZLMHttpHookListener {
627 return ret; 626 return ret;
628 } 627 }
629 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 +
630 private Map<String, String> urlParamToMap(String params) { 655 private Map<String, String> urlParamToMap(String params) {
631 HashMap<String, String> map = new HashMap<>(); 656 HashMap<String, String> map = new HashMap<>();
632 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 -import org.springframework.util.StringUtils;  
14 13
15 import java.util.*; 14 import java.util.*;
16 15
@@ -25,6 +24,9 @@ public class ZLMRTPServerFactory { @@ -25,6 +24,9 @@ public class ZLMRTPServerFactory {
25 @Autowired 24 @Autowired
26 private UserSetting userSetting; 25 private UserSetting userSetting;
27 26
  27 + @Autowired
  28 + private ZlmHttpHookSubscribe hookSubscribe;
  29 +
28 private int[] portRangeArray = new int[2]; 30 private int[] portRangeArray = new int[2];
29 31
30 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) {
@@ -142,7 +144,7 @@ public class ZLMRTPServerFactory { @@ -142,7 +144,7 @@ public class ZLMRTPServerFactory {
142 return result; 144 return result;
143 } 145 }
144 146
145 - public boolean closeRTPServer(MediaServerItem serverItem, String streamId) { 147 + public boolean closeRtpServer(MediaServerItem serverItem, String streamId) {
146 boolean result = false; 148 boolean result = false;
147 if (serverItem !=null){ 149 if (serverItem !=null){
148 Map<String, Object> param = new HashMap<>(); 150 Map<String, Object> param = new HashMap<>();
@@ -162,32 +164,6 @@ public class ZLMRTPServerFactory { @@ -162,32 +164,6 @@ public class ZLMRTPServerFactory {
162 return result; 164 return result;
163 } 165 }
164 166
165 -// private int getPortFromportRange(MediaServerItem mediaServerItem) {  
166 -// int currentPort = mediaServerItem.getCurrentPort();  
167 -// if (currentPort == 0) {  
168 -// String[] portRangeStrArray = mediaServerItem.getSendRtpPortRange().split(",");  
169 -// if (portRangeStrArray.length != 2) {  
170 -// portRangeArray[0] = 30000;  
171 -// portRangeArray[1] = 30500;  
172 -// }else {  
173 -// portRangeArray[0] = Integer.parseInt(portRangeStrArray[0]);  
174 -// portRangeArray[1] = Integer.parseInt(portRangeStrArray[1]);  
175 -// }  
176 -// }  
177 -//  
178 -// if (currentPort == 0 || currentPort++ > portRangeArray[1]) {  
179 -// currentPort = portRangeArray[0];  
180 -// mediaServerItem.setCurrentPort(currentPort);  
181 -// return portRangeArray[0];  
182 -// } else {  
183 -// if (currentPort % 2 == 1) {  
184 -// currentPort++;  
185 -// }  
186 -// currentPort++;  
187 -// mediaServerItem.setCurrentPort(currentPort);  
188 -// return currentPort;  
189 -// }  
190 -// }  
191 167
192 /** 168 /**
193 * 创建一个国标推流 169 * 创建一个国标推流
@@ -201,21 +177,15 @@ public class ZLMRTPServerFactory { @@ -201,21 +177,15 @@ public class ZLMRTPServerFactory {
201 */ 177 */
202 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){
203 179
204 - // 使用RTPServer 功能找一个可用的端口  
205 - String sendRtpPortRange = serverItem.getSendRtpPortRange();  
206 - if (ObjectUtils.isEmpty(sendRtpPortRange)) {  
207 - return null;  
208 - }  
209 - String[] portRangeStrArray = serverItem.getSendRtpPortRange().split(",");  
210 - int localPort = -1;  
211 - if (portRangeStrArray.length != 2) {  
212 - localPort = getFreePort(serverItem, 30000, 30500, null);  
213 - }else {  
214 - localPort = getFreePort(serverItem, Integer.parseInt(portRangeStrArray[0]), Integer.parseInt(portRangeStrArray[1]), null);  
215 - }  
216 - if (localPort == -1) {  
217 - logger.error("没有可用的端口");  
218 - 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 + }
219 } 189 }
220 SendRtpItem sendRtpItem = new SendRtpItem(); 190 SendRtpItem sendRtpItem = new SendRtpItem();
221 sendRtpItem.setIp(ip); 191 sendRtpItem.setIp(ip);
@@ -243,21 +213,13 @@ public class ZLMRTPServerFactory { @@ -243,21 +213,13 @@ public class ZLMRTPServerFactory {
243 * @return SendRtpItem 213 * @return SendRtpItem
244 */ 214 */
245 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){
246 - // 使用RTPServer 功能找一个可用的端口  
247 - String sendRtpPortRange = serverItem.getSendRtpPortRange();  
248 - if (ObjectUtils.isEmpty(sendRtpPortRange)) {  
249 - return null;  
250 - }  
251 - String[] portRangeStrArray = serverItem.getSendRtpPortRange().split(",");  
252 - int localPort = -1;  
253 - if (portRangeStrArray.length != 2) {  
254 - localPort = getFreePort(serverItem, 30000, 30500, null);  
255 - }else {  
256 - localPort = getFreePort(serverItem, Integer.parseInt(portRangeStrArray[0]), Integer.parseInt(portRangeStrArray[1]), null);  
257 - }  
258 - if (localPort == -1) {  
259 - logger.error("没有可用的端口");  
260 - return null; 216 + // 默认为随机端口
  217 + int localPort = 0;
  218 + if (userSetting.getGbSendStreamStrict()) {
  219 + localPort = keepPort(serverItem, ssrc);
  220 + if (localPort == 0) {
  221 + return null;
  222 + }
261 } 223 }
262 SendRtpItem sendRtpItem = new SendRtpItem(); 224 SendRtpItem sendRtpItem = new SendRtpItem();
263 sendRtpItem.setIp(ip); 225 sendRtpItem.setIp(ip);
@@ -275,6 +237,42 @@ public class ZLMRTPServerFactory { @@ -275,6 +237,42 @@ public class ZLMRTPServerFactory {
275 } 237 }
276 238
277 /** 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 + /**
278 * 调用zlm RESTFUL API —— startSendRtp 276 * 调用zlm RESTFUL API —— startSendRtp
279 */ 277 */
280 public JSONObject startSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) { 278 public JSONObject startSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
@@ -294,7 +292,8 @@ public class ZLMRTPServerFactory { @@ -294,7 +292,8 @@ public class ZLMRTPServerFactory {
294 */ 292 */
295 public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) { 293 public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) {
296 JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId); 294 JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId);
297 - return (mediaInfo.getInteger("code") == 0 295 + return mediaInfo != null && (mediaInfo.getInteger("code") == 0
  296 +
298 && mediaInfo.getJSONArray("data") != null 297 && mediaInfo.getJSONArray("data") != null
299 && mediaInfo.getJSONArray("data").size() > 0); 298 && mediaInfo.getJSONArray("data").size() > 0);
300 } 299 }
@@ -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=2) 28 @Order(value=2)
@@ -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/ZLMServerConfig.java
@@ -66,7 +66,7 @@ public class ZLMServerConfig { @@ -66,7 +66,7 @@ public class ZLMServerConfig {
66 private String hookAdminParams; 66 private String hookAdminParams;
67 67
68 @JSONField(name = "hook.alive_interval") 68 @JSONField(name = "hook.alive_interval")
69 - private int hookAliveInterval; 69 + private Float hookAliveInterval;
70 70
71 @JSONField(name = "hook.enable") 71 @JSONField(name = "hook.enable")
72 private String hookEnable; 72 private String hookEnable;
@@ -798,11 +798,11 @@ public class ZLMServerConfig { @@ -798,11 +798,11 @@ public class ZLMServerConfig {
798 this.shellPhell = shellPhell; 798 this.shellPhell = shellPhell;
799 } 799 }
800 800
801 - public int getHookAliveInterval() { 801 + public Float getHookAliveInterval() {
802 return hookAliveInterval; 802 return hookAliveInterval;
803 } 803 }
804 804
805 - public void setHookAliveInterval(int hookAliveInterval) { 805 + public void setHookAliveInterval(Float hookAliveInterval) {
806 this.hookAliveInterval = hookAliveInterval; 806 this.hookAliveInterval = hookAliveInterval;
807 } 807 }
808 808
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
@@ -5,7 +5,6 @@ import com.genersoft.iot.vmp.gb28181.session.SsrcConfig; @@ -5,7 +5,6 @@ import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
5 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; 5 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
6 import io.swagger.v3.oas.annotations.media.Schema; 6 import io.swagger.v3.oas.annotations.media.Schema;
7 import org.springframework.util.ObjectUtils; 7 import org.springframework.util.ObjectUtils;
8 -import org.springframework.util.StringUtils;  
9 8
10 import java.util.HashMap; 9 import java.util.HashMap;
11 10
@@ -55,7 +54,7 @@ public class MediaServerItem{ @@ -55,7 +54,7 @@ public class MediaServerItem{
55 private String secret; 54 private String secret;
56 55
57 @Schema(description = "keepalive hook触发间隔,单位秒") 56 @Schema(description = "keepalive hook触发间隔,单位秒")
58 - private int hookAliveInterval; 57 + private Float hookAliveInterval;
59 58
60 @Schema(description = "是否使用多端口模式") 59 @Schema(description = "是否使用多端口模式")
61 private boolean rtpEnable; 60 private boolean rtpEnable;
@@ -66,9 +65,6 @@ public class MediaServerItem{ @@ -66,9 +65,6 @@ public class MediaServerItem{
66 @Schema(description = "多端口RTP收流端口范围") 65 @Schema(description = "多端口RTP收流端口范围")
67 private String rtpPortRange; 66 private String rtpPortRange;
68 67
69 - @Schema(description = "RTP发流端口范围")  
70 - private String sendRtpPortRange;  
71 -  
72 @Schema(description = "assist服务端口") 68 @Schema(description = "assist服务端口")
73 private int recordAssistPort; 69 private int recordAssistPort;
74 70
@@ -119,7 +115,6 @@ public class MediaServerItem{ @@ -119,7 +115,6 @@ public class MediaServerItem{
119 hookAliveInterval = zlmServerConfig.getHookAliveInterval(); 115 hookAliveInterval = zlmServerConfig.getHookAliveInterval();
120 rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口 116 rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口
121 rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号 117 rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号
122 - sendRtpPortRange = "30000,30500"; // 默认使用30000,30500作为级联时发送流的端口号  
123 recordAssistPort = 0; // 默认关闭 118 recordAssistPort = 0; // 默认关闭
124 119
125 } 120 }
@@ -324,19 +319,11 @@ public class MediaServerItem{ @@ -324,19 +319,11 @@ public class MediaServerItem{
324 this.lastKeepaliveTime = lastKeepaliveTime; 319 this.lastKeepaliveTime = lastKeepaliveTime;
325 } 320 }
326 321
327 - public String getSendRtpPortRange() {  
328 - return sendRtpPortRange;  
329 - }  
330 -  
331 - public void setSendRtpPortRange(String sendRtpPortRange) {  
332 - this.sendRtpPortRange = sendRtpPortRange;  
333 - }  
334 -  
335 - public int getHookAliveInterval() { 322 + public Float getHookAliveInterval() {
336 return hookAliveInterval; 323 return hookAliveInterval;
337 } 324 }
338 325
339 - public void setHookAliveInterval(int hookAliveInterval) { 326 + public void setHookAliveInterval(Float hookAliveInterval) {
340 this.hookAliveInterval = hookAliveInterval; 327 this.hookAliveInterval = hookAliveInterval;
341 } 328 }
342 } 329 }
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/IStreamPushService.java
@@ -11,6 +11,9 @@ import com.github.pagehelper.PageInfo; @@ -11,6 +11,9 @@ import com.github.pagehelper.PageInfo;
11 import java.util.List; 11 import java.util.List;
12 import java.util.Map; 12 import java.util.Map;
13 13
  14 +/**
  15 + * @author lin
  16 + */
14 public interface IStreamPushService { 17 public interface IStreamPushService {
15 18
16 List<StreamPushItem> handleJSON(String json, MediaServerItem mediaServerItem); 19 List<StreamPushItem> handleJSON(String json, MediaServerItem mediaServerItem);
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
1 package com.genersoft.iot.vmp.service.impl; 1 package com.genersoft.iot.vmp.service.impl;
2 2
3 -import java.time.LocalDateTime;  
4 -import java.util.ArrayList;  
5 -import java.util.Collections;  
6 -import java.util.HashMap;  
7 -import java.util.List;  
8 -import java.util.Map;  
9 -import java.util.Set;  
10 -  
11 -import com.genersoft.iot.vmp.conf.DynamicTask;  
12 -import com.genersoft.iot.vmp.conf.exception.ControllerException;  
13 -import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData;  
14 -import com.genersoft.iot.vmp.service.bean.MediaServerLoad;  
15 -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;  
16 -import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;  
17 -import org.slf4j.Logger;  
18 -import org.slf4j.LoggerFactory;  
19 -import org.springframework.beans.factory.annotation.Autowired;  
20 -import org.springframework.beans.factory.annotation.Value;  
21 -import org.springframework.jdbc.datasource.DataSourceTransactionManager;  
22 -import org.springframework.stereotype.Service;  
23 -import org.springframework.transaction.TransactionDefinition;  
24 -import org.springframework.transaction.TransactionStatus;  
25 -import org.springframework.util.ObjectUtils;  
26 -  
27 import com.alibaba.fastjson2.JSON; 3 import com.alibaba.fastjson2.JSON;
28 import com.alibaba.fastjson2.JSONArray; 4 import com.alibaba.fastjson2.JSONArray;
29 import com.alibaba.fastjson2.JSONObject; 5 import com.alibaba.fastjson2.JSONObject;
30 import com.genersoft.iot.vmp.common.VideoManagerConstants; 6 import com.genersoft.iot.vmp.common.VideoManagerConstants;
  7 +import com.genersoft.iot.vmp.conf.DynamicTask;
31 import com.genersoft.iot.vmp.conf.SipConfig; 8 import com.genersoft.iot.vmp.conf.SipConfig;
32 import com.genersoft.iot.vmp.conf.UserSetting; 9 import com.genersoft.iot.vmp.conf.UserSetting;
  10 +import com.genersoft.iot.vmp.conf.exception.ControllerException;
33 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 11 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
34 import com.genersoft.iot.vmp.gb28181.session.SsrcConfig; 12 import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
35 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 13 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
@@ -37,15 +15,30 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; @@ -37,15 +15,30 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
37 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; 15 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
38 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; 16 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
39 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 17 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  18 +import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData;
40 import com.genersoft.iot.vmp.service.IMediaServerService; 19 import com.genersoft.iot.vmp.service.IMediaServerService;
  20 +import com.genersoft.iot.vmp.service.bean.MediaServerLoad;
41 import com.genersoft.iot.vmp.service.bean.SSRCInfo; 21 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
  22 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
42 import com.genersoft.iot.vmp.storager.dao.MediaServerMapper; 23 import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
43 import com.genersoft.iot.vmp.utils.DateUtil; 24 import com.genersoft.iot.vmp.utils.DateUtil;
44 import com.genersoft.iot.vmp.utils.redis.RedisUtil; 25 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
45 - 26 +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
46 import okhttp3.OkHttpClient; 27 import okhttp3.OkHttpClient;
47 import okhttp3.Request; 28 import okhttp3.Request;
48 import okhttp3.Response; 29 import okhttp3.Response;
  30 +import org.slf4j.Logger;
  31 +import org.slf4j.LoggerFactory;
  32 +import org.springframework.beans.factory.annotation.Autowired;
  33 +import org.springframework.beans.factory.annotation.Value;
  34 +import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  35 +import org.springframework.stereotype.Service;
  36 +import org.springframework.transaction.TransactionDefinition;
  37 +import org.springframework.transaction.TransactionStatus;
  38 +import org.springframework.util.ObjectUtils;
  39 +
  40 +import java.time.LocalDateTime;
  41 +import java.util.*;
49 42
50 /** 43 /**
51 * 媒体服务器节点管理 44 * 媒体服务器节点管理
@@ -129,6 +122,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -129,6 +122,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
129 @Override 122 @Override
130 public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback, Integer port) { 123 public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback, Integer port) {
131 if (mediaServerItem == null || mediaServerItem.getId() == null) { 124 if (mediaServerItem == null || mediaServerItem.getId() == null) {
  125 + logger.info("[openRTPServer] 失败, mediaServerItem == null || mediaServerItem.getId() == null");
132 return null; 126 return null;
133 } 127 }
134 // 获取mediaServer可用的ssrc 128 // 获取mediaServer可用的ssrc
@@ -174,7 +168,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -174,7 +168,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
174 if (mediaServerItem == null) { 168 if (mediaServerItem == null) {
175 return; 169 return;
176 } 170 }
177 - zlmrtpServerFactory.closeRTPServer(mediaServerItem, streamId); 171 + zlmrtpServerFactory.closeRtpServer(mediaServerItem, streamId);
178 releaseSsrc(mediaServerItem.getId(), streamId); 172 releaseSsrc(mediaServerItem.getId(), streamId);
179 } 173 }
180 174
@@ -306,7 +300,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -306,7 +300,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
306 public void add(MediaServerItem mediaServerItem) { 300 public void add(MediaServerItem mediaServerItem) {
307 mediaServerItem.setCreateTime(DateUtil.getNow()); 301 mediaServerItem.setCreateTime(DateUtil.getNow());
308 mediaServerItem.setUpdateTime(DateUtil.getNow()); 302 mediaServerItem.setUpdateTime(DateUtil.getNow());
309 - mediaServerItem.setHookAliveInterval(120); 303 + mediaServerItem.setHookAliveInterval(30f);
310 JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); 304 JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
311 if (responseJSON != null) { 305 if (responseJSON != null) {
312 JSONArray data = responseJSON.getJSONArray("data"); 306 JSONArray data = responseJSON.getJSONArray("data");
@@ -413,7 +407,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -413,7 +407,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
413 } 407 }
414 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId(); 408 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId();
415 dynamicTask.stop(zlmKeepaliveKey); 409 dynamicTask.stop(zlmKeepaliveKey);
416 - dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (serverItem.getHookAliveInterval() + 5) * 1000); 410 + dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (Math.getExponent(serverItem.getHookAliveInterval()) + 5) * 1000);
417 publisher.zlmOnlineEventPublish(serverItem.getId()); 411 publisher.zlmOnlineEventPublish(serverItem.getId());
418 logger.info("[ZLM] 连接成功 {} - {}:{} ", 412 logger.info("[ZLM] 连接成功 {} - {}:{} ",
419 zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); 413 zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
@@ -541,6 +535,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -541,6 +535,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
541 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));
542 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));
543 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));
544 if (mediaServerItem.getRecordAssistPort() > 0) { 539 if (mediaServerItem.getRecordAssistPort() > 0) {
545 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()));
546 }else { 541 }else {
@@ -551,8 +546,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -551,8 +546,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
551 // 置0关闭此特性(推流断开会导致立即断开播放器) 546 // 置0关闭此特性(推流断开会导致立即断开播放器)
552 // 此参数不应大于播放器超时时间 547 // 此参数不应大于播放器超时时间
553 // 优化此消息以更快的收到流注销事件 548 // 优化此消息以更快的收到流注销事件
554 - param.put("general.continue_push_ms", "3000" );  
555 - param.put("general.publishToHls", "0" ); 549 + param.put("protocol.continue_push_ms", "3000" );
556 // 最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流, 550 // 最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流,
557 // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项 551 // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项
558 // param.put("general.wait_track_ready_ms", "3000" ); 552 // param.put("general.wait_track_ready_ms", "3000" );
@@ -666,7 +660,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -666,7 +660,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
666 } 660 }
667 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerItem.getId(); 661 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerItem.getId();
668 dynamicTask.stop(zlmKeepaliveKey); 662 dynamicTask.stop(zlmKeepaliveKey);
669 - dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(mediaServerItem), (mediaServerItem.getHookAliveInterval() + 5) * 1000); 663 + dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(mediaServerItem), (mediaServerItem.getHookAliveInterval().intValue() + 5) * 1000);
670 } 664 }
671 665
672 private MediaServerItem getOneFromDatabase(String mediaServerId) { 666 private MediaServerItem getOneFromDatabase(String mediaServerId) {
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -181,6 +181,16 @@ public class PlayServiceImpl implements IPlayService { @@ -181,6 +181,16 @@ public class PlayServiceImpl implements IPlayService {
181 streamId = String.format("%s_%s", device.getDeviceId(), channelId); 181 streamId = String.format("%s_%s", device.getDeviceId(), channelId);
182 } 182 }
183 SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); 183 SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false);
  184 + logger.info(JSONObject.toJSONString(ssrcInfo));
  185 + if (ssrcInfo == null) {
  186 + WVPResult wvpResult = new WVPResult();
  187 + wvpResult.setCode(ErrorCode.ERROR100.getCode());
  188 + wvpResult.setMsg("开启收流失败");
  189 + msg.setData(wvpResult);
  190 +
  191 + resultHolder.invokeAllResult(msg);
  192 + return playResult;
  193 + }
184 play(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInUse, response) -> { 194 play(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInUse, response) -> {
185 if (hookEvent != null) { 195 if (hookEvent != null) {
186 hookEvent.response(mediaServerItem, response); 196 hookEvent.response(mediaServerItem, response);
@@ -217,31 +227,25 @@ public class PlayServiceImpl implements IPlayService { @@ -217,31 +227,25 @@ public class PlayServiceImpl implements IPlayService {
217 ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, 227 ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent,
218 InviteTimeOutCallback timeoutCallback) { 228 InviteTimeOutCallback timeoutCallback) {
219 229
220 - String streamId = null;  
221 - if (mediaServerItem.isRtpEnable()) {  
222 - streamId = String.format("%s_%s", device.getDeviceId(), channelId);  
223 - }  
224 - if (ssrcInfo == null) {  
225 - ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false);  
226 - }  
227 logger.info("[点播开始] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); 230 logger.info("[点播开始] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
228 // 超时处理 231 // 超时处理
229 String timeOutTaskKey = UUID.randomUUID().toString(); 232 String timeOutTaskKey = UUID.randomUUID().toString();
230 - SSRCInfo finalSsrcInfo = ssrcInfo;  
231 dynamicTask.startDelay(timeOutTaskKey, () -> { 233 dynamicTask.startDelay(timeOutTaskKey, () -> {
232 -  
233 - logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, finalSsrcInfo.getPort(), finalSsrcInfo.getSsrc());  
234 - timeoutCallback.run(1, "收流超时");  
235 - // 点播超时回复BYE 同时释放ssrc以及此次点播的资源  
236 - try {  
237 - cmder.streamByeCmd(device, channelId, finalSsrcInfo.getStream(), null);  
238 - } catch (InvalidArgumentException | ParseException | SipException e) {  
239 - logger.error("[点播超时], 发送BYE失败 {}", e.getMessage());  
240 - } catch (SsrcTransactionNotFoundException e) {  
241 - timeoutCallback.run(0, "点播超时");  
242 - mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());  
243 - mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream());  
244 - streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); 234 + // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况
  235 + if (redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId) == null) {
  236 + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc());
  237 + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
  238 + try {
  239 + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null);
  240 + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
  241 + logger.error("[点播超时], 发送BYE失败 {}", e.getMessage());
  242 + } finally {
  243 + timeoutCallback.run(1, "收流超时");
  244 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  245 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
  246 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  247 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
  248 + }
245 } 249 }
246 }, userSetting.getPlayTimeout()); 250 }, userSetting.getPlayTimeout());
247 final String ssrc = ssrcInfo.getSsrc(); 251 final String ssrc = ssrcInfo.getSsrc();
@@ -264,8 +268,8 @@ public class PlayServiceImpl implements IPlayService { @@ -264,8 +268,8 @@ public class PlayServiceImpl implements IPlayService {
264 try { 268 try {
265 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { 269 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
266 logger.info("收到订阅消息: " + response.toJSONString()); 270 logger.info("收到订阅消息: " + response.toJSONString());
267 - System.out.println("停止超时任务: " + timeOutTaskKey);  
268 dynamicTask.stop(timeOutTaskKey); 271 dynamicTask.stop(timeOutTaskKey);
  272 +
269 // hook响应 273 // hook响应
270 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId); 274 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId);
271 hookEvent.response(mediaServerItemInuse, response); 275 hookEvent.response(mediaServerItemInuse, response);
@@ -287,18 +291,18 @@ public class PlayServiceImpl implements IPlayService { @@ -287,18 +291,18 @@ public class PlayServiceImpl implements IPlayService {
287 //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 291 //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
288 String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); 292 String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
289 // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 293 // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
290 - if (ssrc.equals(ssrcInResponse)) { 294 + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
291 return; 295 return;
292 } 296 }
293 logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); 297 logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
294 if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { 298 if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
295 - logger.info("[点播消息] SSRC修正 {}->{}", ssrc, ssrcInResponse); 299 + logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
296 300
297 if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) { 301 if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
298 // ssrc 不可用 302 // ssrc 不可用
299 // 释放ssrc 303 // 释放ssrc
300 - mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());  
301 - streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); 304 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  305 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
302 event.msg = "下级自定义了ssrc,但是此ssrc不可用"; 306 event.msg = "下级自定义了ssrc,但是此ssrc不可用";
303 event.statusCode = 400; 307 event.statusCode = 400;
304 errorEvent.response(event); 308 errorEvent.response(event);
@@ -308,7 +312,7 @@ public class PlayServiceImpl implements IPlayService { @@ -308,7 +312,7 @@ public class PlayServiceImpl implements IPlayService {
308 // 单端口模式streamId也有变化,需要重新设置监听 312 // 单端口模式streamId也有变化,需要重新设置监听
309 if (!mediaServerItem.isRtpEnable()) { 313 if (!mediaServerItem.isRtpEnable()) {
310 // 添加订阅 314 // 添加订阅
311 - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId()); 315 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
312 subscribe.removeSubscribe(hookSubscribe); 316 subscribe.removeSubscribe(hookSubscribe);
313 hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); 317 hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
314 subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> { 318 subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
@@ -320,30 +324,30 @@ public class PlayServiceImpl implements IPlayService { @@ -320,30 +324,30 @@ public class PlayServiceImpl implements IPlayService {
320 }); 324 });
321 } 325 }
322 // 关闭rtp server 326 // 关闭rtp server
323 - mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream()); 327 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
324 // 重新开启ssrc server 328 // 重新开启ssrc server
325 - mediaServerService.openRTPServer(mediaServerItem, finalSsrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, finalSsrcInfo.getPort()); 329 + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, ssrcInfo.getPort());
326 330
327 } 331 }
328 } 332 }
329 }, (event) -> { 333 }, (event) -> {
330 dynamicTask.stop(timeOutTaskKey); 334 dynamicTask.stop(timeOutTaskKey);
331 - mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream()); 335 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
332 // 释放ssrc 336 // 释放ssrc
333 - mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); 337 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
334 338
335 - streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); 339 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
336 errorEvent.response(event); 340 errorEvent.response(event);
337 }); 341 });
338 } catch (InvalidArgumentException | SipException | ParseException e) { 342 } catch (InvalidArgumentException | SipException | ParseException e) {
339 343
340 logger.error("[命令发送失败] 点播消息: {}", e.getMessage()); 344 logger.error("[命令发送失败] 点播消息: {}", e.getMessage());
341 dynamicTask.stop(timeOutTaskKey); 345 dynamicTask.stop(timeOutTaskKey);
342 - mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream()); 346 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
343 // 释放ssrc 347 // 释放ssrc
344 - mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); 348 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
345 349
346 - streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); 350 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
347 SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null)); 351 SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null));
348 eventResult.msg = "命令发送失败"; 352 eventResult.msg = "命令发送失败";
349 errorEvent.response(eventResult); 353 errorEvent.response(eventResult);
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/storager/dao/PlatformGbStreamMapper.java
@@ -23,10 +23,10 @@ public interface PlatformGbStreamMapper { @@ -23,10 +23,10 @@ public interface PlatformGbStreamMapper {
23 23
24 @Insert("<script> " + 24 @Insert("<script> " +
25 "INSERT into platform_gb_stream " + 25 "INSERT into platform_gb_stream " +
26 - "(gbStreamId, platformId, catalogId,status) " + 26 + "(gbStreamId, platformId, catalogId) " +
27 "values " + 27 "values " +
28 "<foreach collection='streamPushItems' index='index' item='item' separator=','> " + 28 "<foreach collection='streamPushItems' index='index' item='item' separator=','> " +
29 - "(${item.gbStreamId}, '${item.platformId}', '${item.catalogId}'), '${item.status}')" + 29 + "(${item.gbStreamId}, '${item.platformId}', '${item.catalogId}')" +
30 "</foreach> " + 30 "</foreach> " +
31 "</script>") 31 "</script>")
32 int batchAdd(List<StreamPushItem> streamPushItems); 32 int batchAdd(List<StreamPushItem> streamPushItems);
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 },