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 11  
12 12 <groupId>com.genersoft</groupId>
13 13 <artifactId>wvp-pro</artifactId>
14   - <version>2.3.2</version>
  14 + <version>2.6.6</version>
15 15 <name>web video platform</name>
16 16 <description>国标28181视频平台</description>
17 17  
... ...
sql/mysql.sql
... ... @@ -281,7 +281,6 @@ CREATE TABLE `media_server` (
281 281 `secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
282 282 `rtpEnable` int NOT NULL,
283 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 284 `recordAssistPort` int NOT NULL,
286 285 `defaultServer` int NOT NULL,
287 286 `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
... ...
sql/update.sql
1 1 alter table media_server
2 2 drop column streamNoneReaderDelayMS;
3 3  
  4 +alter table media_server
  5 + drop column sendRtpPortRange;
  6 +
4 7 alter table stream_proxy
5 8 add enable_disable_none_reader bit(1) default null;
6 9  
... ...
src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java
1 1 package com.genersoft.iot.vmp.conf;
2 2  
3   -import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
4 3 import org.slf4j.Logger;
5 4 import org.slf4j.LoggerFactory;
6   -import org.springframework.beans.factory.annotation.Autowired;
7   -import org.springframework.context.annotation.Bean;
8 5 import org.springframework.scheduling.annotation.Scheduled;
9 6 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
10 7 import org.springframework.stereotype.Component;
... ... @@ -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 105 futureMap.remove(key);
108 106 runnableMap.remove(key);
109 107 }
  108 + return result;
110 109 }
111 110  
112 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 2  
3 3 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
4 4 import com.genersoft.iot.vmp.utils.DateUtil;
5   -import com.genersoft.iot.vmp.vmanager.gb28181.device.DeviceQuery;
6 5 import org.slf4j.Logger;
7 6 import org.slf4j.LoggerFactory;
8 7 import org.springframework.beans.factory.annotation.Value;
9 8 import org.springframework.context.annotation.Configuration;
10 9 import org.springframework.util.ObjectUtils;
11   -import org.springframework.util.StringUtils;
12 10  
13 11 import java.net.InetAddress;
14 12 import java.net.UnknownHostException;
... ... @@ -75,10 +73,6 @@ public class MediaConfig{
75 73 @Value("${media.rtp.port-range}")
76 74 private String rtpPortRange;
77 75  
78   -
79   - @Value("${media.rtp.send-port-range}")
80   - private String sendRtpPortRange;
81   -
82 76 @Value("${media.record-assist-port:0}")
83 77 private Integer recordAssistPort = 0;
84 78  
... ... @@ -191,10 +185,6 @@ public class MediaConfig{
191 185 return sipDomain;
192 186 }
193 187  
194   - public String getSendRtpPortRange() {
195   - return sendRtpPortRange;
196   - }
197   -
198 188 public MediaServerItem getMediaSerItem(){
199 189 MediaServerItem mediaServerItem = new MediaServerItem();
200 190 mediaServerItem.setId(id);
... ... @@ -214,9 +204,8 @@ public class MediaConfig{
214 204 mediaServerItem.setSecret(secret);
215 205 mediaServerItem.setRtpEnable(rtpEnable);
216 206 mediaServerItem.setRtpPortRange(rtpPortRange);
217   - mediaServerItem.setSendRtpPortRange(sendRtpPortRange);
218 207 mediaServerItem.setRecordAssistPort(recordAssistPort);
219   - mediaServerItem.setHookAliveInterval(120);
  208 + mediaServerItem.setHookAliveInterval(30.00f);
220 209  
221 210 mediaServerItem.setCreateTime(DateUtil.getNow());
222 211 mediaServerItem.setUpdateTime(DateUtil.getNow());
... ...
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
... ... @@ -39,6 +39,8 @@ public class UserSetting {
39 39  
40 40 private Boolean pushAuthority = Boolean.TRUE;
41 41  
  42 + private Boolean gbSendStreamStrict = Boolean.FALSE;
  43 +
42 44 private String serverId = "000000";
43 45  
44 46 private String thirdPartyGBIdReg = "[\\s\\S]*";
... ... @@ -176,4 +178,12 @@ public class UserSetting {
176 178 public void setPushAuthority(Boolean pushAuthority) {
177 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 7 import org.springframework.scheduling.annotation.Scheduled;
8 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 14 import javax.sip.header.CallIdHeader;
12 15 import javax.sip.message.Response;
13   -import java.text.ParseException;
14 16 import java.time.Instant;
15 17 import java.util.Map;
16 18 import java.util.concurrent.ConcurrentHashMap;
... ... @@ -29,6 +31,7 @@ public class SipSubscribe {
29 31 private Map<String, SipSubscribe.Event> okSubscribes = new ConcurrentHashMap<>();
30 32  
31 33 private Map<String, Instant> okTimeSubscribes = new ConcurrentHashMap<>();
  34 +
32 35 private Map<String, Instant> errorTimeSubscribes = new ConcurrentHashMap<>();
33 36  
34 37 // @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java
1 1 package com.genersoft.iot.vmp.gb28181.event.record;
2 2  
3 3 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
4   -import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
5 4 import org.slf4j.Logger;
6 5 import org.slf4j.LoggerFactory;
7 6 import org.springframework.context.ApplicationListener;
8 7 import org.springframework.stereotype.Component;
9 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 16 * @author: pan
17 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 2  
3 3 import com.alibaba.fastjson2.JSONObject;
4 4 import com.genersoft.iot.vmp.common.StreamInfo;
5   -import com.genersoft.iot.vmp.conf.DynamicTask;
6 5 import com.genersoft.iot.vmp.conf.SipConfig;
7 6 import com.genersoft.iot.vmp.conf.UserSetting;
8 7 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
... ... @@ -13,15 +12,15 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
13 12 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
14 13 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
15 14 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
  15 +import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
16 16 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  17 +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
17 18 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
18 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 20 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
23 21 import com.genersoft.iot.vmp.service.IMediaServerService;
24 22 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
  23 +import com.genersoft.iot.vmp.utils.DateUtil;
25 24 import gov.nist.javax.sip.message.SIPRequest;
26 25 import gov.nist.javax.sip.message.SIPResponse;
27 26 import org.slf4j.Logger;
... ... @@ -31,16 +30,13 @@ import org.springframework.context.annotation.DependsOn;
31 30 import org.springframework.stereotype.Component;
32 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 38 import javax.sip.message.Request;
40   -import javax.sip.message.Response;
41   -import java.lang.reflect.Field;
42 39 import java.text.ParseException;
43   -import java.util.HashSet;
44 40  
45 41 /**
46 42 * @description:设备能力接口,用于定义设备的控制、查询能力
... ... @@ -173,7 +169,7 @@ public class SIPCommander implements ISIPCommander {
173 169 public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,
174 170 int zoomSpeed) throws InvalidArgumentException, SipException, ParseException {
175 171 String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed);
176   - StringBuffer ptzXml = new StringBuffer(200);
  172 + StringBuilder ptzXml = new StringBuilder(200);
177 173 String charset = device.getCharset();
178 174 ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
179 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 16 import com.genersoft.iot.vmp.service.IMediaServerService;
17 17 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
18 18 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
19   -import gov.nist.javax.sip.SipProviderImpl;
20 19 import gov.nist.javax.sip.message.MessageFactoryImpl;
21 20 import gov.nist.javax.sip.message.SIPRequest;
22 21 import org.slf4j.Logger;
23 22 import org.slf4j.LoggerFactory;
24 23 import org.springframework.beans.factory.annotation.Autowired;
25   -import org.springframework.beans.factory.annotation.Qualifier;
26 24 import org.springframework.context.annotation.DependsOn;
27   -import org.springframework.context.annotation.Lazy;
28 25 import org.springframework.lang.Nullable;
29 26 import org.springframework.stereotype.Component;
30 27 import org.springframework.util.ObjectUtils;
... ... @@ -639,7 +636,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
639 636 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
640 637 if (mediaServerItem != null) {
641 638 mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
642   - zlmrtpServerFactory.closeRTPServer(mediaServerItem, sendRtpItem.getStreamId());
  639 + zlmrtpServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStreamId());
643 640 }
644 641 SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(parentPlatform, sendRtpItem);
645 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 10 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
11 11 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
12 12 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
13   -import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
14 13 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
  14 +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
15 15 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
16 16 import com.genersoft.iot.vmp.service.IMediaServerService;
17 17 import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
... ... @@ -33,7 +33,8 @@ import javax.sip.header.FromHeader;
33 33 import javax.sip.header.HeaderAddress;
34 34 import javax.sip.header.ToHeader;
35 35 import java.text.ParseException;
36   -import java.util.*;
  36 +import java.util.HashMap;
  37 +import java.util.Map;
37 38  
38 39 /**
39 40 * SIP命令类型: ACK请求
... ... @@ -63,6 +64,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
63 64 private ZLMRTPServerFactory zlmrtpServerFactory;
64 65  
65 66 @Autowired
  67 + private ZlmHttpHookSubscribe hookSubscribe;
  68 +
  69 + @Autowired
66 70 private IMediaServerService mediaServerService;
67 71  
68 72 @Autowired
... ... @@ -130,8 +134,18 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
130 134 startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader);
131 135 });
132 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 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 45 import javax.sip.message.Response;
46 46 import java.text.ParseException;
47 47 import java.time.Instant;
  48 +import java.util.Random;
48 49 import java.util.Vector;
49 50  
50 51 /**
... ... @@ -157,11 +158,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
157 158 StreamProxyItem proxyByAppAndStream =null;
158 159 // 不是通道可能是直播流
159 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 161 // 通道存在,发100,TRYING
166 162 try {
167 163 responseAck(request, Response.TRYING);
... ... @@ -385,7 +381,12 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
385 381 } else {
386 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 390 content.append("a=sendonly\r\n");
390 391 content.append("a=rtpmap:96 PS/90000\r\n");
391 392 content.append("y=" + sendRtpItem.getSsrc() + "\r\n");
... ... @@ -476,6 +477,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
476 477  
477 478 // 写入redis, 超时时回复
478 479 redisCatchStorage.updateSendRTPSever(sendRtpItem);
  480 + MediaServerItem finalMediaServerItem = mediaServerItem;
479 481 playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> {
480 482 logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId);
481 483 redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
... ... @@ -656,6 +658,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
656 658 if (!platform.isStartOfflinePush()) {
657 659 // 平台设置中关闭了拉起离线的推流则直接回复
658 660 try {
  661 + logger.info("[上级点播] 失败,推流设备未推流,channel: {}, app: {}, stream: {}", gbStream.getGbId(), gbStream.getApp(), gbStream.getStream());
659 662 responseAck(request, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing");
660 663 } catch (SipException | InvalidArgumentException | ParseException e) {
661 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 85 String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword();
86 86 AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
87 87 if (authHead == null && !ObjectUtils.isEmpty(password)) {
88   - logger.info("[注册请求] 未携带授权头 回复401: {}", requestAddress);
  88 + logger.info("[注册请求] 回复401: {}", requestAddress);
89 89 response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
90 90 new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain());
91 91 sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
1 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 3 import com.alibaba.fastjson2.JSON;
  4 +import com.alibaba.fastjson2.JSONObject;
9 5 import com.genersoft.iot.vmp.common.StreamInfo;
10 6 import com.genersoft.iot.vmp.conf.UserSetting;
11 7 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
12 8 import com.genersoft.iot.vmp.gb28181.bean.*;
13 9 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
14 10 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  11 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
15 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 17 import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
18 18 import com.genersoft.iot.vmp.service.*;
19 19 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
... ... @@ -24,18 +24,15 @@ import org.springframework.beans.factory.annotation.Autowired;
24 24 import org.springframework.beans.factory.annotation.Qualifier;
25 25 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
26 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 29 import javax.servlet.http.HttpServletRequest;
37 30 import javax.sip.InvalidArgumentException;
38 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 38 * @description:针对 ZLMediaServer的hook事件监听
... ... @@ -186,7 +183,7 @@ public class ZLMHttpHookListener {
186 183  
187 184 if (!"rtp".equals(param.getApp())) {
188 185 if (userSetting.getPushAuthority()) {
189   -// 推流鉴权
  186 + // 推流鉴权
190 187 if (param.getParams() == null) {
191 188 logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
192 189 ret.put("code", 401);
... ... @@ -571,6 +568,8 @@ public class ZLMHttpHookListener {
571 568 public JSONObject onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject){
572 569  
573 570 jsonObject.put("ip", request.getRemoteAddr());
  571 + System.out.println(jsonObject.toJSONString()
  572 + );
574 573 ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
575 574 zlmServerConfig.setIp(request.getRemoteAddr());
576 575 logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
... ... @@ -627,6 +626,32 @@ public class ZLMHttpHookListener {
627 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 655 private Map<String, String> urlParamToMap(String params) {
631 656 HashMap<String, String> map = new HashMap<>();
632 657 if (ObjectUtils.isEmpty(params)) {
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
1 1 package com.genersoft.iot.vmp.media.zlm;
2 2  
  3 +import com.alibaba.fastjson2.JSON;
3 4 import com.alibaba.fastjson2.JSONArray;
4 5 import com.alibaba.fastjson2.JSONObject;
5 6 import com.genersoft.iot.vmp.conf.UserSetting;
6 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 9 import org.slf4j.Logger;
9 10 import org.slf4j.LoggerFactory;
10 11 import org.springframework.beans.factory.annotation.Autowired;
11 12 import org.springframework.stereotype.Component;
12   -import org.springframework.util.ObjectUtils;
13   -import org.springframework.util.StringUtils;
14 13  
15 14 import java.util.*;
16 15  
... ... @@ -25,6 +24,9 @@ public class ZLMRTPServerFactory {
25 24 @Autowired
26 25 private UserSetting userSetting;
27 26  
  27 + @Autowired
  28 + private ZlmHttpHookSubscribe hookSubscribe;
  29 +
28 30 private int[] portRangeArray = new int[2];
29 31  
30 32 public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List<Integer> usedFreelist) {
... ... @@ -142,7 +144,7 @@ public class ZLMRTPServerFactory {
142 144 return result;
143 145 }
144 146  
145   - public boolean closeRTPServer(MediaServerItem serverItem, String streamId) {
  147 + public boolean closeRtpServer(MediaServerItem serverItem, String streamId) {
146 148 boolean result = false;
147 149 if (serverItem !=null){
148 150 Map<String, Object> param = new HashMap<>();
... ... @@ -162,32 +164,6 @@ public class ZLMRTPServerFactory {
162 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 177 */
202 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 190 SendRtpItem sendRtpItem = new SendRtpItem();
221 191 sendRtpItem.setIp(ip);
... ... @@ -243,21 +213,13 @@ public class ZLMRTPServerFactory {
243 213 * @return SendRtpItem
244 214 */
245 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 224 SendRtpItem sendRtpItem = new SendRtpItem();
263 225 sendRtpItem.setIp(ip);
... ... @@ -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 276 * 调用zlm RESTFUL API —— startSendRtp
279 277 */
280 278 public JSONObject startSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
... ... @@ -294,7 +292,8 @@ public class ZLMRTPServerFactory {
294 292 */
295 293 public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) {
296 294 JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId);
297   - return (mediaInfo.getInteger("code") == 0
  295 + return mediaInfo != null && (mediaInfo.getInteger("code") == 0
  296 +
298 297 && mediaInfo.getJSONArray("data") != null
299 298 && mediaInfo.getJSONArray("data").size() > 0);
300 299 }
... ... @@ -333,7 +332,7 @@ public class ZLMRTPServerFactory {
333 332 result= true;
334 333 logger.info("[停止RTP推流] 成功");
335 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 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 18 import org.springframework.scheduling.annotation.Async;
19 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 27 @Component
24 28 @Order(value=2)
... ... @@ -73,8 +77,6 @@ public class ZLMRunner implements CommandLineRunner {
73 77 }
74 78 });
75 79  
76   -
77   -
78 80 // 获取zlm信息
79 81 logger.info("[zlm] 等待默认zlm中...");
80 82  
... ... @@ -87,7 +89,7 @@ public class ZLMRunner implements CommandLineRunner {
87 89 }
88 90 for (MediaServerItem mediaServerItem : all) {
89 91 if (startGetMedia == null) {
90   - startGetMedia = new HashMap<>();
  92 + startGetMedia = new ConcurrentHashMap<>();
91 93 }
92 94 startGetMedia.put(mediaServerItem.getId(), true);
93 95 connectZlmServer(mediaServerItem);
... ... @@ -95,7 +97,7 @@ public class ZLMRunner implements CommandLineRunner {
95 97 }
96 98 String taskKey = "zlm-connect-timeout";
97 99 dynamicTask.startDelay(taskKey, ()->{
98   - if (startGetMedia != null) {
  100 + if (startGetMedia != null && startGetMedia.size() > 0) {
99 101 Set<String> allZlmId = startGetMedia.keySet();
100 102 for (String id : allZlmId) {
101 103 logger.error("[ {} ]]主动连接失败,不再尝试连接", id);
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java
... ... @@ -66,7 +66,7 @@ public class ZLMServerConfig {
66 66 private String hookAdminParams;
67 67  
68 68 @JSONField(name = "hook.alive_interval")
69   - private int hookAliveInterval;
  69 + private Float hookAliveInterval;
70 70  
71 71 @JSONField(name = "hook.enable")
72 72 private String hookEnable;
... ... @@ -798,11 +798,11 @@ public class ZLMServerConfig {
798 798 this.shellPhell = shellPhell;
799 799 }
800 800  
801   - public int getHookAliveInterval() {
  801 + public Float getHookAliveInterval() {
802 802 return hookAliveInterval;
803 803 }
804 804  
805   - public void setHookAliveInterval(int hookAliveInterval) {
  805 + public void setHookAliveInterval(Float hookAliveInterval) {
806 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 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 38 public static HookSubscribeForServerStarted on_server_started() {
28 39 HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted();
29 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 15  
16 16 private JSONObject content;
17 17  
  18 + @JSONField(format="yyyy-MM-dd HH:mm:ss")
18 19 private Instant expires;
19 20  
20 21 @Override
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java
... ... @@ -19,5 +19,7 @@ public enum HookType {
19 19 on_stream_none_reader,
20 20 on_stream_not_found,
21 21 on_server_started,
  22 +
  23 + on_rtp_server_timeout,
22 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 5 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
6 6 import io.swagger.v3.oas.annotations.media.Schema;
7 7 import org.springframework.util.ObjectUtils;
8   -import org.springframework.util.StringUtils;
9 8  
10 9 import java.util.HashMap;
11 10  
... ... @@ -55,7 +54,7 @@ public class MediaServerItem{
55 54 private String secret;
56 55  
57 56 @Schema(description = "keepalive hook触发间隔,单位秒")
58   - private int hookAliveInterval;
  57 + private Float hookAliveInterval;
59 58  
60 59 @Schema(description = "是否使用多端口模式")
61 60 private boolean rtpEnable;
... ... @@ -66,9 +65,6 @@ public class MediaServerItem{
66 65 @Schema(description = "多端口RTP收流端口范围")
67 66 private String rtpPortRange;
68 67  
69   - @Schema(description = "RTP发流端口范围")
70   - private String sendRtpPortRange;
71   -
72 68 @Schema(description = "assist服务端口")
73 69 private int recordAssistPort;
74 70  
... ... @@ -119,7 +115,6 @@ public class MediaServerItem{
119 115 hookAliveInterval = zlmServerConfig.getHookAliveInterval();
120 116 rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口
121 117 rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号
122   - sendRtpPortRange = "30000,30500"; // 默认使用30000,30500作为级联时发送流的端口号
123 118 recordAssistPort = 0; // 默认关闭
124 119  
125 120 }
... ... @@ -324,19 +319,11 @@ public class MediaServerItem{
324 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 323 return hookAliveInterval;
337 324 }
338 325  
339   - public void setHookAliveInterval(int hookAliveInterval) {
  326 + public void setHookAliveInterval(Float hookAliveInterval) {
340 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 11 import java.util.List;
12 12 import java.util.Map;
13 13  
  14 +/**
  15 + * @author lin
  16 + */
14 17 public interface IStreamPushService {
15 18  
16 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 4 import com.genersoft.iot.vmp.gb28181.bean.*;
5 5 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
6 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 9 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
8 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 11 import com.genersoft.iot.vmp.service.IDeviceChannelService;
11 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 13 import com.genersoft.iot.vmp.service.IMediaServerService;
15 14 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
16 15 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
... ... @@ -24,12 +23,10 @@ import org.slf4j.Logger;
24 23 import org.slf4j.LoggerFactory;
25 24 import org.springframework.beans.factory.annotation.Autowired;
26 25 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
27   -import org.springframework.jdbc.support.incrementer.AbstractIdentityColumnMaxValueIncrementer;
28 26 import org.springframework.stereotype.Service;
29 27 import org.springframework.transaction.TransactionDefinition;
30 28 import org.springframework.transaction.TransactionStatus;
31 29 import org.springframework.util.ObjectUtils;
32   -import org.springframework.util.StringUtils;
33 30  
34 31 import javax.sip.InvalidArgumentException;
35 32 import javax.sip.SipException;
... ... @@ -171,7 +168,7 @@ public class DeviceServiceImpl implements IDeviceService {
171 168 redisCatchStorage.updateDevice(device);
172 169 deviceMapper.update(device);
173 170 //进行通道离线
174   - deviceChannelMapper.offlineByDeviceId(deviceId);
  171 +// deviceChannelMapper.offlineByDeviceId(deviceId);
175 172 // 离线释放所有ssrc
176 173 List<SsrcTransaction> ssrcTransactions = streamSession.getSsrcTransactionForAll(deviceId, null, null, null);
177 174 if (ssrcTransactions != null && ssrcTransactions.size() > 0) {
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
1 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 3 import com.alibaba.fastjson2.JSON;
28 4 import com.alibaba.fastjson2.JSONArray;
29 5 import com.alibaba.fastjson2.JSONObject;
30 6 import com.genersoft.iot.vmp.common.VideoManagerConstants;
  7 +import com.genersoft.iot.vmp.conf.DynamicTask;
31 8 import com.genersoft.iot.vmp.conf.SipConfig;
32 9 import com.genersoft.iot.vmp.conf.UserSetting;
  10 +import com.genersoft.iot.vmp.conf.exception.ControllerException;
33 11 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
34 12 import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
35 13 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
... ... @@ -37,15 +15,30 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
37 15 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
38 16 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
39 17 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  18 +import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData;
40 19 import com.genersoft.iot.vmp.service.IMediaServerService;
  20 +import com.genersoft.iot.vmp.service.bean.MediaServerLoad;
41 21 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
  22 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
42 23 import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
43 24 import com.genersoft.iot.vmp.utils.DateUtil;
44 25 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
45   -
  26 +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
46 27 import okhttp3.OkHttpClient;
47 28 import okhttp3.Request;
48 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 122 @Override
130 123 public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback, Integer port) {
131 124 if (mediaServerItem == null || mediaServerItem.getId() == null) {
  125 + logger.info("[openRTPServer] 失败, mediaServerItem == null || mediaServerItem.getId() == null");
132 126 return null;
133 127 }
134 128 // 获取mediaServer可用的ssrc
... ... @@ -174,7 +168,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
174 168 if (mediaServerItem == null) {
175 169 return;
176 170 }
177   - zlmrtpServerFactory.closeRTPServer(mediaServerItem, streamId);
  171 + zlmrtpServerFactory.closeRtpServer(mediaServerItem, streamId);
178 172 releaseSsrc(mediaServerItem.getId(), streamId);
179 173 }
180 174  
... ... @@ -306,7 +300,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
306 300 public void add(MediaServerItem mediaServerItem) {
307 301 mediaServerItem.setCreateTime(DateUtil.getNow());
308 302 mediaServerItem.setUpdateTime(DateUtil.getNow());
309   - mediaServerItem.setHookAliveInterval(120);
  303 + mediaServerItem.setHookAliveInterval(30f);
310 304 JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
311 305 if (responseJSON != null) {
312 306 JSONArray data = responseJSON.getJSONArray("data");
... ... @@ -413,7 +407,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
413 407 }
414 408 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId();
415 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 411 publisher.zlmOnlineEventPublish(serverItem.getId());
418 412 logger.info("[ZLM] 连接成功 {} - {}:{} ",
419 413 zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
... ... @@ -541,6 +535,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
541 535 param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex));
542 536 param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrex));
543 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 539 if (mediaServerItem.getRecordAssistPort() > 0) {
545 540 param.put("hook.on_record_mp4",String.format("http://127.0.0.1:%s/api/record/on_record_mp4", mediaServerItem.getRecordAssistPort()));
546 541 }else {
... ... @@ -551,8 +546,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
551 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 550 // 最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流,
557 551 // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项
558 552 // param.put("general.wait_track_ready_ms", "3000" );
... ... @@ -666,7 +660,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
666 660 }
667 661 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerItem.getId();
668 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 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 181 streamId = String.format("%s_%s", device.getDeviceId(), channelId);
182 182 }
183 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 194 play(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInUse, response) -> {
185 195 if (hookEvent != null) {
186 196 hookEvent.response(mediaServerItem, response);
... ... @@ -217,31 +227,25 @@ public class PlayServiceImpl implements IPlayService {
217 227 ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent,
218 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 230 logger.info("[点播开始] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
228 231 // 超时处理
229 232 String timeOutTaskKey = UUID.randomUUID().toString();
230   - SSRCInfo finalSsrcInfo = ssrcInfo;
231 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 250 }, userSetting.getPlayTimeout());
247 251 final String ssrc = ssrcInfo.getSsrc();
... ... @@ -264,8 +268,8 @@ public class PlayServiceImpl implements IPlayService {
264 268 try {
265 269 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
266 270 logger.info("收到订阅消息: " + response.toJSONString());
267   - System.out.println("停止超时任务: " + timeOutTaskKey);
268 271 dynamicTask.stop(timeOutTaskKey);
  272 +
269 273 // hook响应
270 274 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId);
271 275 hookEvent.response(mediaServerItemInuse, response);
... ... @@ -287,18 +291,18 @@ public class PlayServiceImpl implements IPlayService {
287 291 //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
288 292 String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
289 293 // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
290   - if (ssrc.equals(ssrcInResponse)) {
  294 + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
291 295 return;
292 296 }
293 297 logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
294 298 if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
295   - logger.info("[点播消息] SSRC修正 {}->{}", ssrc, ssrcInResponse);
  299 + logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
296 300  
297 301 if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
298 302 // ssrc 不可用
299 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 306 event.msg = "下级自定义了ssrc,但是此ssrc不可用";
303 307 event.statusCode = 400;
304 308 errorEvent.response(event);
... ... @@ -308,7 +312,7 @@ public class PlayServiceImpl implements IPlayService {
308 312 // 单端口模式streamId也有变化,需要重新设置监听
309 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 316 subscribe.removeSubscribe(hookSubscribe);
313 317 hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
314 318 subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
... ... @@ -320,30 +324,30 @@ public class PlayServiceImpl implements IPlayService {
320 324 });
321 325 }
322 326 // 关闭rtp server
323   - mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream());
  327 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
324 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 333 }, (event) -> {
330 334 dynamicTask.stop(timeOutTaskKey);
331   - mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream());
  335 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
332 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 340 errorEvent.response(event);
337 341 });
338 342 } catch (InvalidArgumentException | SipException | ParseException e) {
339 343  
340 344 logger.error("[命令发送失败] 点播消息: {}", e.getMessage());
341 345 dynamicTask.stop(timeOutTaskKey);
342   - mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream());
  346 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
343 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 351 SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null));
348 352 eventResult.msg = "命令发送失败";
349 353 errorEvent.response(eventResult);
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
... ... @@ -28,7 +28,6 @@ public interface MediaServerMapper {
28 28 "secret, " +
29 29 "rtpEnable, " +
30 30 "rtpPortRange, " +
31   - "sendRtpPortRange, " +
32 31 "recordAssistPort, " +
33 32 "defaultServer, " +
34 33 "createTime, " +
... ... @@ -52,7 +51,6 @@ public interface MediaServerMapper {
52 51 "'${secret}', " +
53 52 "${rtpEnable}, " +
54 53 "'${rtpPortRange}', " +
55   - "'${sendRtpPortRange}', " +
56 54 "${recordAssistPort}, " +
57 55 "${defaultServer}, " +
58 56 "'${createTime}', " +
... ... @@ -77,7 +75,6 @@ public interface MediaServerMapper {
77 75 "<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" +
78 76 "<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" +
79 77 "<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" +
80   - "<if test=\"sendRtpPortRange != null\">, sendRtpPortRange='${sendRtpPortRange}'</if>" +
81 78 "<if test=\"secret != null\">, secret='${secret}'</if>" +
82 79 "<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" +
83 80 "<if test=\"hookAliveInterval != null\">, hookAliveInterval=${hookAliveInterval}</if>" +
... ... @@ -101,7 +98,6 @@ public interface MediaServerMapper {
101 98 "<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" +
102 99 "<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" +
103 100 "<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" +
104   - "<if test=\"sendRtpPortRange != null\">, sendRtpPortRange='${sendRtpPortRange}'</if>" +
105 101 "<if test=\"secret != null\">, secret='${secret}'</if>" +
106 102 "<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" +
107 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 23  
24 24 @Insert("<script> " +
25 25 "INSERT into platform_gb_stream " +
26   - "(gbStreamId, platformId, catalogId,status) " +
  26 + "(gbStreamId, platformId, catalogId) " +
27 27 "values " +
28 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 30 "</foreach> " +
31 31 "</script>")
32 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 62 if (callId != null) {
63 63 // 权限校验
64 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 68 authority = true;
67 69 }else {
68 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 135 MediaServerItem mediaServerItemInDatabase = mediaServerService.getOne(mediaServerItem.getId());
136 136  
137 137 if (mediaServerItemInDatabase != null) {
138   - if (ObjectUtils.isEmpty(mediaServerItemInDatabase.getSendRtpPortRange()) && ObjectUtils.isEmpty(mediaServerItem.getSendRtpPortRange())) {
139   - mediaServerItem.setSendRtpPortRange("30000,30500");
140   - }
141 138 mediaServerService.update(mediaServerItem);
142 139 } else {
143   - if (ObjectUtils.isEmpty(mediaServerItem.getSendRtpPortRange())) {
144   - mediaServerItem.setSendRtpPortRange("30000,30500");
145   - }
146 140 mediaServerService.add(mediaServerItem);
147 141 }
148 142 }
... ...
src/main/resources/all-application.yml
... ... @@ -192,6 +192,9 @@ user-settings:
192 192 stream-on-demand: true
193 193 # 推流鉴权, 默认开启
194 194 push-authority: true
  195 + # 国标级联发流严格模式,严格模式会使用与sdp信息中一致的端口发流,端口共享media.rtp.port-range,这会损失一些性能,
  196 + # 非严格模式使用随机端口发流,性能更好, 默认关闭
  197 + gb-send-stream-strict: false
195 198  
196 199 # 关闭在线文档(生产环境建议关闭)
197 200 springdoc:
... ...
web_src/src/components/dialog/MediaServerEdit.vue
... ... @@ -89,11 +89,6 @@
89 89 -
90 90 <el-input v-model="rtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input>
91 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 92 <el-form-item label="录像管理服务端口" prop="recordAssistPort">
98 93 <el-input v-model.number="mediaServerForm.recordAssistPort" :disabled="mediaServerForm.defaultServer">
99 94 <!-- <el-button v-if="mediaServerForm.recordAssistPort > 0" slot="append" type="primary" @click="checkRecordServer">测试</el-button>-->
... ... @@ -177,15 +172,12 @@ export default {
177 172 rtmpSSlPort: "",
178 173 rtpEnable: false,
179 174 rtpPortRange: "",
180   - sendRtpPortRange: "",
181 175 rtpProxyPort: "",
182 176 rtspPort: "",
183 177 rtspSSLPort: "",
184 178 },
185 179 rtpPortRange1:30000,
186 180 rtpPortRange2:30500,
187   - sendRtpPortRange1:30000,
188   - sendRtpPortRange2:30500,
189 181  
190 182 rules: {
191 183 ip: [{ required: true, validator: isValidIp, message: '请输入有效的IP地址', trigger: 'blur' }],
... ... @@ -196,8 +188,6 @@ export default {
196 188 rtmpSSlPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
197 189 rtpPortRange1: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
198 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 191 rtpProxyPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
202 192 rtspPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
203 193 rtspSSLPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
... ... @@ -229,9 +219,6 @@ export default {
229 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 224 checkServer: function() {
... ... @@ -251,8 +238,6 @@ export default {
251 238 that.mediaServerForm = data.data;
252 239 that.mediaServerForm.httpPort = httpPort;
253 240 that.mediaServerForm.autoConfig = true;
254   - that.sendRtpPortRange1 = 30000
255   - that.sendRtpPortRange2 = 30500
256 241 that.rtpPortRange1 = 30000
257 242 that.rtpPortRange2 = 30500
258 243 that.serverCheck = 1;
... ... @@ -336,13 +321,10 @@ export default {
336 321 rtmpSSlPort: "",
337 322 rtpEnable: false,
338 323 rtpPortRange: "",
339   - sendRtpPortRange: "",
340 324 rtpProxyPort: "",
341 325 rtspPort: "",
342 326 rtspSSLPort: "",
343 327 };
344   - this.sendRtpPortRange1 = 30000;
345   - this.sendRtpPortRange2 = 30500;
346 328 this.rtpPortRange1 = 30500;
347 329 this.rtpPortRange2 = 30500;
348 330 this.listChangeCallback = null
... ... @@ -367,9 +349,7 @@ export default {
367 349 }
368 350 },
369 351 portRangeChange: function() {
370   - this.mediaServerForm.sendRtpPortRange = this.sendRtpPortRange1 + "," + this.sendRtpPortRange2
371 352 this.mediaServerForm.rtpPortRange = this.rtpPortRange1 + "," + this.rtpPortRange2
372   - console.log(this.mediaServerForm.sendRtpPortRange)
373 353 console.log(this.mediaServerForm.rtpPortRange)
374 354 }
375 355 },
... ...