Commit e898c344aaa515b4fe16ae7ce3d979160d1e962b

Authored by 648540858
2 parents 7079c73b 0aada74b

Merge branch 'wvp-28181-2.0' into main-dev

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
Showing 27 changed files with 369 additions and 464 deletions
doc/_content/introduction/config.md
@@ -160,4 +160,4 @@ user-settings: @@ -160,4 +160,4 @@ user-settings:
160 160
161 161
162 如果配置信息无误,你可以启动zlm,再启动wvp来测试了,启动成功的话,你可以在wvp的日志下看到zlm已连接的提示。 162 如果配置信息无误,你可以启动zlm,再启动wvp来测试了,启动成功的话,你可以在wvp的日志下看到zlm已连接的提示。
163 -接下来[部署到服务器](./_content/introduction/deployment.md), 如何你只是本地运行直接再本地运行即可。 163 +接下来[部署到服务器](./_content/introduction/deployment.md), 如果你只是本地运行直接在本地运行即可。
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 org.apache.commons.lang3.ObjectUtils;
3 import org.slf4j.Logger; 4 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory; 5 import org.slf4j.LoggerFactory;
5 import org.springframework.scheduling.annotation.Scheduled; 6 import org.springframework.scheduling.annotation.Scheduled;
@@ -45,6 +46,9 @@ public class DynamicTask { @@ -45,6 +46,9 @@ public class DynamicTask {
45 * @return 46 * @return
46 */ 47 */
47 public void startCron(String key, Runnable task, int cycleForCatalog) { 48 public void startCron(String key, Runnable task, int cycleForCatalog) {
  49 + if(ObjectUtils.isEmpty(key)) {
  50 + return;
  51 + }
48 ScheduledFuture<?> future = futureMap.get(key); 52 ScheduledFuture<?> future = futureMap.get(key);
49 if (future != null) { 53 if (future != null) {
50 if (future.isCancelled()) { 54 if (future.isCancelled()) {
@@ -73,6 +77,9 @@ public class DynamicTask { @@ -73,6 +77,9 @@ public class DynamicTask {
73 * @return 77 * @return
74 */ 78 */
75 public void startDelay(String key, Runnable task, int delay) { 79 public void startDelay(String key, Runnable task, int delay) {
  80 + if(ObjectUtils.isEmpty(key)) {
  81 + return;
  82 + }
76 stop(key); 83 stop(key);
77 84
78 // 获取执行的时刻 85 // 获取执行的时刻
@@ -99,9 +106,12 @@ public class DynamicTask { @@ -99,9 +106,12 @@ public class DynamicTask {
99 } 106 }
100 107
101 public boolean stop(String key) { 108 public boolean stop(String key) {
  109 + if(ObjectUtils.isEmpty(key)) {
  110 + return false;
  111 + }
102 boolean result = false; 112 boolean result = false;
103 - if (futureMap.get(key) != null && !futureMap.get(key).isCancelled() && !futureMap.get(key).isDone()) {  
104 - result = futureMap.get(key).cancel(false); 113 + if (!ObjectUtils.isEmpty(futureMap.get(key)) && !futureMap.get(key).isCancelled() && !futureMap.get(key).isDone()) {
  114 + result = futureMap.get(key).cancel(true);
105 futureMap.remove(key); 115 futureMap.remove(key);
106 runnableMap.remove(key); 116 runnableMap.remove(key);
107 } 117 }
@@ -109,6 +119,9 @@ public class DynamicTask { @@ -109,6 +119,9 @@ public class DynamicTask {
109 } 119 }
110 120
111 public boolean contains(String key) { 121 public boolean contains(String key) {
  122 + if(ObjectUtils.isEmpty(key)) {
  123 + return false;
  124 + }
112 return futureMap.get(key) != null; 125 return futureMap.get(key) != null;
113 } 126 }
114 127
@@ -117,6 +130,9 @@ public class DynamicTask { @@ -117,6 +130,9 @@ public class DynamicTask {
117 } 130 }
118 131
119 public Runnable get(String key) { 132 public Runnable get(String key) {
  133 + if(ObjectUtils.isEmpty(key)) {
  134 + return null;
  135 + }
120 return runnableMap.get(key); 136 return runnableMap.get(key);
121 } 137 }
122 138
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
@@ -43,8 +43,6 @@ public class UserSetting { @@ -43,8 +43,6 @@ public class UserSetting {
43 43
44 private Boolean pushAuthority = Boolean.TRUE; 44 private Boolean pushAuthority = Boolean.TRUE;
45 45
46 - private Boolean gbSendStreamStrict = Boolean.FALSE;  
47 -  
48 private Boolean syncChannelOnDeviceOnline = Boolean.FALSE; 46 private Boolean syncChannelOnDeviceOnline = Boolean.FALSE;
49 47
50 private Boolean sipLog = Boolean.FALSE; 48 private Boolean sipLog = Boolean.FALSE;
@@ -208,14 +206,6 @@ public class UserSetting { @@ -208,14 +206,6 @@ public class UserSetting {
208 this.pushAuthority = pushAuthority; 206 this.pushAuthority = pushAuthority;
209 } 207 }
210 208
211 - public Boolean getGbSendStreamStrict() {  
212 - return gbSendStreamStrict;  
213 - }  
214 -  
215 - public void setGbSendStreamStrict(Boolean gbSendStreamStrict) {  
216 - this.gbSendStreamStrict = gbSendStreamStrict;  
217 - }  
218 -  
219 public Boolean getSyncChannelOnDeviceOnline() { 209 public Boolean getSyncChannelOnDeviceOnline() {
220 return syncChannelOnDeviceOnline; 210 return syncChannelOnDeviceOnline;
221 } 211 }
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
@@ -165,7 +165,7 @@ public class Device { @@ -165,7 +165,7 @@ public class Device {
165 * 是否开启ssrc校验,默认关闭,开启可以防止串流 165 * 是否开启ssrc校验,默认关闭,开启可以防止串流
166 */ 166 */
167 @Schema(description = "是否开启ssrc校验,默认关闭,开启可以防止串流") 167 @Schema(description = "是否开启ssrc校验,默认关闭,开启可以防止串流")
168 - private boolean ssrcCheck = true; 168 + private boolean ssrcCheck = false;
169 169
170 /** 170 /**
171 * 地理坐标系, 目前支持 WGS84,GCJ02 171 * 地理坐标系, 目前支持 WGS84,GCJ02
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -333,7 +333,7 @@ public interface ISIPCommander { @@ -333,7 +333,7 @@ public interface ISIPCommander {
333 * @param endTime 报警发生终止时间(可选) 333 * @param endTime 报警发生终止时间(可选)
334 * @return true = 命令发送成功 334 * @return true = 命令发送成功
335 */ 335 */
336 - void alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) throws InvalidArgumentException, SipException, ParseException; 336 + void alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String startTime, String endTime) throws InvalidArgumentException, SipException, ParseException;
337 337
338 /** 338 /**
339 * 订阅、取消订阅目录信息 339 * 订阅、取消订阅目录信息
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
@@ -167,6 +167,7 @@ public class SIPRequestHeaderPlarformProvider { @@ -167,6 +167,7 @@ public class SIPRequestHeaderPlarformProvider {
167 167
168 public Request createMessageRequest(ParentPlatform parentPlatform, String content, SendRtpItem sendRtpItem) throws PeerUnavailableException, ParseException, InvalidArgumentException { 168 public Request createMessageRequest(ParentPlatform parentPlatform, String content, SendRtpItem sendRtpItem) throws PeerUnavailableException, ParseException, InvalidArgumentException {
169 CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId()); 169 CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId());
  170 + callIdHeader.setCallId(sendRtpItem.getCallId());
170 return createMessageRequest(parentPlatform, content, sendRtpItem.getToTag(), SipUtils.getNewViaTag(), sendRtpItem.getFromTag(), callIdHeader); 171 return createMessageRequest(parentPlatform, content, sendRtpItem.getToTag(), SipUtils.getNewViaTag(), sendRtpItem.getFromTag(), callIdHeader);
171 } 172 }
172 173
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -1275,7 +1275,7 @@ public class SIPCommander implements ISIPCommander { @@ -1275,7 +1275,7 @@ public class SIPCommander implements ISIPCommander {
1275 * @return true = 命令发送成功 1275 * @return true = 命令发送成功
1276 */ 1276 */
1277 @Override 1277 @Override
1278 - public void alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) throws InvalidArgumentException, SipException, ParseException { 1278 + public void alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String startTime, String endTime) throws InvalidArgumentException, SipException, ParseException {
1279 1279
1280 StringBuffer cmdXml = new StringBuffer(200); 1280 StringBuffer cmdXml = new StringBuffer(200);
1281 String charset = device.getCharset(); 1281 String charset = device.getCharset();
@@ -1293,9 +1293,6 @@ public class SIPCommander implements ISIPCommander { @@ -1293,9 +1293,6 @@ public class SIPCommander implements ISIPCommander {
1293 if (!ObjectUtils.isEmpty(alarmMethod)) { 1293 if (!ObjectUtils.isEmpty(alarmMethod)) {
1294 cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n"); 1294 cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
1295 } 1295 }
1296 - if (!ObjectUtils.isEmpty(alarmType)) {  
1297 - cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");  
1298 - }  
1299 if (!ObjectUtils.isEmpty(startTime)) { 1296 if (!ObjectUtils.isEmpty(startTime)) {
1300 cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n"); 1297 cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
1301 } 1298 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
@@ -98,15 +98,20 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In @@ -98,15 +98,20 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
98 logger.warn("[收到ACK]:未找到来自{},目标为({})的推流信息",fromUserId, toUserId); 98 logger.warn("[收到ACK]:未找到来自{},目标为({})的推流信息",fromUserId, toUserId);
99 return; 99 return;
100 } 100 }
101 - // tcp主动时,此时是级联下级平台,在回复200ok时,本地已经请求zlm开启监听,跳过下面步骤  
102 - if (sendRtpItem.isTcpActive()) {  
103 - return;  
104 - }  
105 - logger.info("[收到ACK]:rtp/{}开始级推流, 目标={}:{},SSRC={}, RTCP={}", sendRtpItem.getStream(),  
106 - sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp());  
107 - // 取消设置的超时任务  
108 - dynamicTask.stop(callIdHeader.getCallId()); 101 + // tcp主动时,此时是级联下级平台,在回复200ok时,本地已经请求zlm开启监听,跳过下面步骤
  102 + if (sendRtpItem.isTcpActive()) {
  103 + logger.info("收到ACK,rtp/{} TCP主动方式后续处理", sendRtpItem.getStreamId());
  104 + return;
  105 + }
  106 + String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
109 MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); 107 MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
  108 + logger.info("收到ACK,rtp/{}开始向上级推流, 目标={}:{},SSRC={}, 协议:{}",
  109 + sendRtpItem.getStream(),
  110 + sendRtpItem.getIp(),
  111 + sendRtpItem.getPort(),
  112 + sendRtpItem.getSsrc(),
  113 + sendRtpItem.isTcp()?(sendRtpItem.isTcpActive()?"TCP主动":"TCP被动"):"UDP"
  114 + );
110 ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(fromUserId); 115 ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(fromUserId);
111 116
112 if (parentPlatform != null) { 117 if (parentPlatform != null) {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
@@ -148,7 +148,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @@ -148,7 +148,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
148 logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId); 148 logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId);
149 } 149 }
150 try { 150 try {
151 - logger.warn("[停止点播] {}/{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); 151 + logger.info("[停止点播] {}/{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
152 cmder.streamByeCmd(device, sendRtpItem.getChannelId(), streamId, null); 152 cmder.streamByeCmd(device, sendRtpItem.getChannelId(), streamId, null);
153 } catch (InvalidArgumentException | ParseException | SipException | 153 } catch (InvalidArgumentException | ParseException | SipException |
154 SsrcTransactionNotFoundException e) { 154 SsrcTransactionNotFoundException e) {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -469,8 +469,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -469,8 +469,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
469 sendRtpItem.setApp("rtp"); 469 sendRtpItem.setApp("rtp");
470 if ("Playback".equalsIgnoreCase(sessionName)) { 470 if ("Playback".equalsIgnoreCase(sessionName)) {
471 sendRtpItem.setPlayType(InviteStreamType.PLAYBACK); 471 sendRtpItem.setPlayType(InviteStreamType.PLAYBACK);
472 - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, false, device.getStreamModeForParam());  
473 - sendRtpItem.setStream(ssrcInfo.getStream()); 472 + String startTimeStr = DateUtil.urlFormatter.format(start);
  473 + String endTimeStr = DateUtil.urlFormatter.format(end);
  474 + String stream = device.getDeviceId() + "_" + channelId + "_" + startTimeStr + "_" + endTimeStr;
  475 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, stream, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam());
474 // 写入redis, 超时时回复 476 // 写入redis, 超时时回复
475 redisCatchStorage.updateSendRTPSever(sendRtpItem); 477 redisCatchStorage.updateSendRTPSever(sendRtpItem);
476 playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start), 478 playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start),
@@ -530,12 +532,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -530,12 +532,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
530 } 532 }
531 })); 533 }));
532 sendRtpItem.setPlayType(InviteStreamType.PLAY); 534 sendRtpItem.setPlayType(InviteStreamType.PLAY);
533 - String streamId = null;  
534 - if (mediaServerItem.isRtpEnable()) {  
535 - streamId = String.format("%s_%s", device.getDeviceId(), channelId);  
536 - }else {  
537 - streamId = String.format("%08x", Integer.parseInt(ssrcInfo.getSsrc())).toUpperCase();  
538 - } 535 + String streamId = String.format("%s_%s", device.getDeviceId(), channelId);
539 sendRtpItem.setStream(streamId); 536 sendRtpItem.setStream(streamId);
540 sendRtpItem.setSsrc(ssrcInfo.getSsrc()); 537 sendRtpItem.setSsrc(ssrcInfo.getSsrc());
541 redisCatchStorage.updateSendRTPSever(sendRtpItem); 538 redisCatchStorage.updateSendRTPSever(sendRtpItem);
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
@@ -33,7 +33,7 @@ import java.text.ParseException; @@ -33,7 +33,7 @@ import java.text.ParseException;
33 public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { 33 public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
34 34
35 35
36 - private Logger logger = LoggerFactory.getLogger(KeepaliveNotifyMessageHandler.class); 36 + private final Logger logger = LoggerFactory.getLogger(KeepaliveNotifyMessageHandler.class);
37 private final static String cmdType = "Keepalive"; 37 private final static String cmdType = "Keepalive";
38 38
39 @Autowired 39 @Autowired
@@ -59,14 +59,19 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp @@ -59,14 +59,19 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
59 // 未注册的设备不做处理 59 // 未注册的设备不做处理
60 return; 60 return;
61 } 61 }
62 - logger.info("[收到心跳], device: {}", device.getDeviceId());  
63 SIPRequest request = (SIPRequest) evt.getRequest(); 62 SIPRequest request = (SIPRequest) evt.getRequest();
  63 + logger.info("[收到心跳], device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId());
  64 +
64 // 回复200 OK 65 // 回复200 OK
65 try { 66 try {
66 responseAck(request, Response.OK); 67 responseAck(request, Response.OK);
67 } catch (SipException | InvalidArgumentException | ParseException e) { 68 } catch (SipException | InvalidArgumentException | ParseException e) {
68 logger.error("[命令发送失败] 心跳回复: {}", e.getMessage()); 69 logger.error("[命令发送失败] 心跳回复: {}", e.getMessage());
69 } 70 }
  71 + if (DateUtil.getDifferenceForNow(device.getKeepaliveTime()) <= 3000L){
  72 + logger.info("[收到心跳] 心跳发送过于频繁,已忽略 device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId());
  73 + return;
  74 + }
70 75
71 RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress()); 76 RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress());
72 if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) { 77 if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) {
@@ -80,7 +85,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp @@ -80,7 +85,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
80 }else { 85 }else {
81 long lastTime = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(device.getKeepaliveTime()); 86 long lastTime = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(device.getKeepaliveTime());
82 if (System.currentTimeMillis()/1000-lastTime > 10) { 87 if (System.currentTimeMillis()/1000-lastTime > 10) {
83 - device.setKeepaliveIntervalTime(new Long(System.currentTimeMillis()/1000-lastTime).intValue()); 88 + device.setKeepaliveIntervalTime(Long.valueOf(System.currentTimeMillis()/1000-lastTime).intValue());
84 } 89 }
85 } 90 }
86 91
src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java
@@ -281,6 +281,6 @@ public class SipUtils { @@ -281,6 +281,6 @@ public class SipUtils {
281 return null; 281 return null;
282 } 282 }
283 } 283 }
284 - return localDateTime.format(DateUtil.formatterISO8601); 284 + return localDateTime.format(DateUtil.formatter);
285 } 285 }
286 } 286 }
287 \ No newline at end of file 287 \ No newline at end of file
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -200,7 +200,10 @@ public class ZLMHttpHookListener { @@ -200,7 +200,10 @@ public class ZLMHttpHookListener {
200 200
201 String mediaServerId = json.getString("mediaServerId"); 201 String mediaServerId = json.getString("mediaServerId");
202 MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); 202 MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
203 - 203 + if (mediaInfo == null) {
  204 + return new HookResultForOnPublish(200, "success");
  205 + }
  206 + // 推流鉴权的处理
204 if (!"rtp".equals(param.getApp())) { 207 if (!"rtp".equals(param.getApp())) {
205 if (userSetting.getPushAuthority()) { 208 if (userSetting.getPushAuthority()) {
206 // 推流鉴权 209 // 推流鉴权
@@ -252,11 +255,21 @@ public class ZLMHttpHookListener { @@ -252,11 +255,21 @@ public class ZLMHttpHookListener {
252 } 255 }
253 }); 256 });
254 257
  258 + // 是否录像
255 if ("rtp".equals(param.getApp())) { 259 if ("rtp".equals(param.getApp())) {
256 result.setEnable_mp4(userSetting.getRecordSip()); 260 result.setEnable_mp4(userSetting.getRecordSip());
257 } else { 261 } else {
258 result.setEnable_mp4(userSetting.isRecordPushLive()); 262 result.setEnable_mp4(userSetting.isRecordPushLive());
259 } 263 }
  264 + // 替换流地址
  265 + if ("rtp".equals(param.getApp()) && !mediaInfo.isRtpEnable()) {
  266 + String ssrc = String.format("%010d", Long.parseLong(param.getStream(), 16));;
  267 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc);
  268 + if (inviteInfo != null) {
  269 + result.setStream_replace(inviteInfo.getStream());
  270 + logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream());
  271 + }
  272 + }
260 List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream()); 273 List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
261 if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) { 274 if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
262 String deviceId = ssrcTransactionForAll.get(0).getDeviceId(); 275 String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
@@ -569,6 +582,7 @@ public class ZLMHttpHookListener { @@ -569,6 +582,7 @@ public class ZLMHttpHookListener {
569 Device device = deviceService.getDevice(inviteInfo.getDeviceId()); 582 Device device = deviceService.getDevice(inviteInfo.getDeviceId());
570 if (device != null) { 583 if (device != null) {
571 try { 584 try {
  585 + // 多查询一次防止已经被处理了
572 InviteInfo info = inviteStreamService.getInviteInfo(inviteInfo.getType(), 586 InviteInfo info = inviteStreamService.getInviteInfo(inviteInfo.getType(),
573 inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); 587 inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
574 if (info != null) { 588 if (info != null) {
@@ -643,7 +657,7 @@ public class ZLMHttpHookListener { @@ -643,7 +657,7 @@ public class ZLMHttpHookListener {
643 657
644 if ("rtp".equals(param.getApp())) { 658 if ("rtp".equals(param.getApp())) {
645 String[] s = param.getStream().split("_"); 659 String[] s = param.getStream().split("_");
646 - if (!mediaInfo.isRtpEnable() || (s.length != 2 && s.length != 4)) { 660 + if ((s.length != 2 && s.length != 4)) {
647 defaultResult.setResult(HookResult.SUCCESS()); 661 defaultResult.setResult(HookResult.SUCCESS());
648 return defaultResult; 662 return defaultResult;
649 } 663 }
@@ -672,7 +686,6 @@ public class ZLMHttpHookListener { @@ -672,7 +686,6 @@ public class ZLMHttpHookListener {
672 686
673 result.onTimeout(() -> { 687 result.onTimeout(() -> {
674 logger.info("[ZLM HOOK] 预览流自动点播, 等待超时"); 688 logger.info("[ZLM HOOK] 预览流自动点播, 等待超时");
675 - // 释放rtpserver  
676 msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时")); 689 msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
677 resultHolder.invokeResult(msg); 690 resultHolder.invokeResult(msg);
678 }); 691 });
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java
@@ -168,13 +168,9 @@ public class ZLMServerFactory { @@ -168,13 +168,9 @@ public class ZLMServerFactory {
168 public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, 168 public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId,
169 String deviceId, String channelId, boolean tcp, boolean rtcp){ 169 String deviceId, String channelId, boolean tcp, boolean rtcp){
170 170
171 - // 默认为随机端口  
172 - int localPort = 0;  
173 - if (userSetting.getGbSendStreamStrict()) {  
174 - localPort = sendRtpPortManager.getNextPort(serverItem);  
175 - if (localPort == 0) {  
176 - return null;  
177 - } 171 + int localPort = sendRtpPortManager.getNextPort(serverItem);
  172 + if (localPort == 0) {
  173 + return null;
178 } 174 }
179 SendRtpItem sendRtpItem = new SendRtpItem(); 175 SendRtpItem sendRtpItem = new SendRtpItem();
180 sendRtpItem.setIp(ip); 176 sendRtpItem.setIp(ip);
@@ -204,13 +200,10 @@ public class ZLMServerFactory { @@ -204,13 +200,10 @@ public class ZLMServerFactory {
204 */ 200 */
205 public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, 201 public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId,
206 String app, String stream, String channelId, boolean tcp, boolean rtcp){ 202 String app, String stream, String channelId, boolean tcp, boolean rtcp){
207 - // 默认为随机端口  
208 - int localPort = 0;  
209 - if (userSetting.getGbSendStreamStrict()) {  
210 - localPort = sendRtpPortManager.getNextPort(serverItem);  
211 - if (localPort == 0) {  
212 - return null;  
213 - } 203 +
  204 + int localPort = sendRtpPortManager.getNextPort(serverItem);
  205 + if (localPort == 0) {
  206 + return null;
214 } 207 }
215 SendRtpItem sendRtpItem = new SendRtpItem(); 208 SendRtpItem sendRtpItem = new SendRtpItem();
216 sendRtpItem.setIp(ip); 209 sendRtpItem.setIp(ip);
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
@@ -6,6 +6,7 @@ public class HookResultForOnPublish extends HookResult{ @@ -6,6 +6,7 @@ public class HookResultForOnPublish extends HookResult{
6 private boolean enable_mp4; 6 private boolean enable_mp4;
7 private int mp4_max_second; 7 private int mp4_max_second;
8 private String mp4_save_path; 8 private String mp4_save_path;
  9 + private String stream_replace;
9 10
10 public HookResultForOnPublish() { 11 public HookResultForOnPublish() {
11 } 12 }
@@ -51,12 +52,21 @@ public class HookResultForOnPublish extends HookResult{ @@ -51,12 +52,21 @@ public class HookResultForOnPublish extends HookResult{
51 this.mp4_save_path = mp4_save_path; 52 this.mp4_save_path = mp4_save_path;
52 } 53 }
53 54
  55 + public String getStream_replace() {
  56 + return stream_replace;
  57 + }
  58 +
  59 + public void setStream_replace(String stream_replace) {
  60 + this.stream_replace = stream_replace;
  61 + }
  62 +
54 @Override 63 @Override
55 public String toString() { 64 public String toString() {
56 return "HookResultForOnPublish{" + 65 return "HookResultForOnPublish{" +
57 "enable_audio=" + enable_audio + 66 "enable_audio=" + enable_audio +
58 ", enable_mp4=" + enable_mp4 + 67 ", enable_mp4=" + enable_mp4 +
59 ", mp4_max_second=" + mp4_max_second + 68 ", mp4_max_second=" + mp4_max_second +
  69 + ", stream_replace=" + stream_replace +
60 ", mp4_save_path='" + mp4_save_path + '\'' + 70 ", mp4_save_path='" + mp4_save_path + '\'' +
61 '}'; 71 '}';
62 } 72 }
src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java
@@ -74,5 +74,13 @@ public interface IInviteStreamService { @@ -74,5 +74,13 @@ public interface IInviteStreamService {
74 int getStreamInfoCount(String mediaServerId); 74 int getStreamInfoCount(String mediaServerId);
75 75
76 76
  77 + /**
  78 + * 获取MediaServer下的流信息
  79 + */
  80 + InviteInfo getInviteInfoBySSRC(String ssrc);
77 81
  82 + /**
  83 + * 更新ssrc
  84 + */
  85 + InviteInfo updateInviteInfoForSSRC(InviteInfo inviteInfo, String ssrcInResponse);
78 } 86 }
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
@@ -190,6 +190,17 @@ public class DeviceServiceImpl implements IDeviceService { @@ -190,6 +190,17 @@ public class DeviceServiceImpl implements IDeviceService {
190 redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, true); 190 redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, true);
191 } 191 }
192 192
  193 +//
  194 +// try {
  195 +// cmder.alarmSubscribe(device, 600, "0", "4", "0", "2023-7-27T00:00:00", "2023-7-28T00:00:00");
  196 +// } catch (InvalidArgumentException e) {
  197 +// throw new RuntimeException(e);
  198 +// } catch (SipException e) {
  199 +// throw new RuntimeException(e);
  200 +// } catch (ParseException e) {
  201 +// throw new RuntimeException(e);
  202 +// }
  203 +
193 } 204 }
194 205
195 @Override 206 @Override
src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java
@@ -77,10 +77,11 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @@ -77,10 +77,11 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
77 77
78 } 78 }
79 String key = VideoManagerConstants.INVITE_PREFIX + 79 String key = VideoManagerConstants.INVITE_PREFIX +
80 - "_" + inviteInfoForUpdate.getType() +  
81 - "_" + inviteInfoForUpdate.getDeviceId() +  
82 - "_" + inviteInfoForUpdate.getChannelId() +  
83 - "_" + inviteInfoForUpdate.getStream(); 80 + ":" + inviteInfoForUpdate.getType() +
  81 + ":" + inviteInfoForUpdate.getDeviceId() +
  82 + ":" + inviteInfoForUpdate.getChannelId() +
  83 + ":" + inviteInfoForUpdate.getStream()+
  84 + ":" + inviteInfoForUpdate.getSsrcInfo().getSsrc();
84 redisTemplate.opsForValue().set(key, inviteInfoForUpdate); 85 redisTemplate.opsForValue().set(key, inviteInfoForUpdate);
85 } 86 }
86 87
@@ -93,11 +94,15 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @@ -93,11 +94,15 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
93 } 94 }
94 removeInviteInfo(inviteInfoInDb); 95 removeInviteInfo(inviteInfoInDb);
95 String key = VideoManagerConstants.INVITE_PREFIX + 96 String key = VideoManagerConstants.INVITE_PREFIX +
96 - "_" + inviteInfo.getType() +  
97 - "_" + inviteInfo.getDeviceId() +  
98 - "_" + inviteInfo.getChannelId() +  
99 - "_" + stream; 97 + ":" + inviteInfo.getType() +
  98 + ":" + inviteInfo.getDeviceId() +
  99 + ":" + inviteInfo.getChannelId() +
  100 + ":" + stream +
  101 + ":" + inviteInfo.getSsrcInfo().getSsrc();
100 inviteInfoInDb.setStream(stream); 102 inviteInfoInDb.setStream(stream);
  103 + if (inviteInfoInDb.getSsrcInfo() != null) {
  104 + inviteInfoInDb.getSsrcInfo().setStream(stream);
  105 + }
101 redisTemplate.opsForValue().set(key, inviteInfoInDb); 106 redisTemplate.opsForValue().set(key, inviteInfoInDb);
102 return inviteInfoInDb; 107 return inviteInfoInDb;
103 } 108 }
@@ -105,10 +110,11 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @@ -105,10 +110,11 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
105 @Override 110 @Override
106 public InviteInfo getInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) { 111 public InviteInfo getInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) {
107 String key = VideoManagerConstants.INVITE_PREFIX + 112 String key = VideoManagerConstants.INVITE_PREFIX +
108 - "_" + (type != null ? type : "*") +  
109 - "_" + (deviceId != null ? deviceId : "*") +  
110 - "_" + (channelId != null ? channelId : "*") +  
111 - "_" + (stream != null ? stream : "*"); 113 + ":" + (type != null ? type : "*") +
  114 + ":" + (deviceId != null ? deviceId : "*") +
  115 + ":" + (channelId != null ? channelId : "*") +
  116 + ":" + (stream != null ? stream : "*")
  117 + + ":*";
112 List<Object> scanResult = RedisUtil.scan(redisTemplate, key); 118 List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
113 if (scanResult.size() != 1) { 119 if (scanResult.size() != 1) {
114 return null; 120 return null;
@@ -130,10 +136,11 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @@ -130,10 +136,11 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
130 @Override 136 @Override
131 public void removeInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) { 137 public void removeInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) {
132 String scanKey = VideoManagerConstants.INVITE_PREFIX + 138 String scanKey = VideoManagerConstants.INVITE_PREFIX +
133 - "_" + (type != null ? type : "*") +  
134 - "_" + (deviceId != null ? deviceId : "*") +  
135 - "_" + (channelId != null ? channelId : "*") +  
136 - "_" + (stream != null ? stream : "*"); 139 + ":" + (type != null ? type : "*") +
  140 + ":" + (deviceId != null ? deviceId : "*") +
  141 + ":" + (channelId != null ? channelId : "*") +
  142 + ":" + (stream != null ? stream : "*") +
  143 + ":*";
137 List<Object> scanResult = RedisUtil.scan(redisTemplate, scanKey); 144 List<Object> scanResult = RedisUtil.scan(redisTemplate, scanKey);
138 if (scanResult.size() > 0) { 145 if (scanResult.size() > 0) {
139 for (Object keyObj : scanResult) { 146 for (Object keyObj : scanResult) {
@@ -171,10 +178,10 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @@ -171,10 +178,10 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
171 } 178 }
172 179
173 private String buildKey(InviteSessionType type, String deviceId, String channelId, String stream) { 180 private String buildKey(InviteSessionType type, String deviceId, String channelId, String stream) {
174 - String key = type + "_" + deviceId + "_" + channelId; 181 + String key = type + ":" + deviceId + ":" + channelId;
175 // 如果ssrc未null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite 182 // 如果ssrc未null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite
176 if (stream != null) { 183 if (stream != null) {
177 - key += ("_" + stream); 184 + key += (":" + stream);
178 } 185 }
179 return key; 186 return key;
180 } 187 }
@@ -188,7 +195,7 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @@ -188,7 +195,7 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
188 @Override 195 @Override
189 public int getStreamInfoCount(String mediaServerId) { 196 public int getStreamInfoCount(String mediaServerId) {
190 int count = 0; 197 int count = 0;
191 - String key = VideoManagerConstants.INVITE_PREFIX + "_*_*_*_*"; 198 + String key = VideoManagerConstants.INVITE_PREFIX + ":*:*:*:*:*";
192 List<Object> scanResult = RedisUtil.scan(redisTemplate, key); 199 List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
193 if (scanResult.size() == 0) { 200 if (scanResult.size() == 0) {
194 return 0; 201 return 0;
@@ -219,11 +226,42 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @@ -219,11 +226,42 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
219 226
220 227
221 private String buildSubStreamKey(InviteSessionType type, String deviceId, String channelId, String stream) { 228 private String buildSubStreamKey(InviteSessionType type, String deviceId, String channelId, String stream) {
222 - String key = type + "_" + "_" + deviceId + "_" + channelId; 229 + String key = type + ":" + ":" + deviceId + ":" + channelId;
223 // 如果ssrc为null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite 230 // 如果ssrc为null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite
224 if (stream != null) { 231 if (stream != null) {
225 - key += ("_" + stream); 232 + key += (":" + stream);
226 } 233 }
227 return key; 234 return key;
228 } 235 }
  236 +
  237 + @Override
  238 + public InviteInfo getInviteInfoBySSRC(String ssrc) {
  239 + String key = VideoManagerConstants.INVITE_PREFIX + ":*:*:*:*:" + ssrc;
  240 + List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
  241 + if (scanResult.size() != 1) {
  242 + return null;
  243 + }
  244 +
  245 + return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0));
  246 + }
  247 +
  248 + @Override
  249 + public InviteInfo updateInviteInfoForSSRC(InviteInfo inviteInfo, String ssrc) {
  250 + InviteInfo inviteInfoInDb = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
  251 + if (inviteInfoInDb == null) {
  252 + return null;
  253 + }
  254 + removeInviteInfo(inviteInfoInDb);
  255 + String key = VideoManagerConstants.INVITE_PREFIX +
  256 + ":" + inviteInfo.getType() +
  257 + ":" + inviteInfo.getDeviceId() +
  258 + ":" + inviteInfo.getChannelId() +
  259 + ":" + inviteInfo.getStream() +
  260 + ":" + inviteInfo.getSsrcInfo().getSsrc();
  261 + if (inviteInfoInDb.getSsrcInfo() != null) {
  262 + inviteInfoInDb.getSsrcInfo().setSsrc(ssrc);
  263 + }
  264 + redisTemplate.opsForValue().set(key, inviteInfoInDb);
  265 + return inviteInfoInDb;
  266 + }
229 } 267 }
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -153,9 +153,14 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -153,9 +153,14 @@ public class MediaServerServiceImpl implements IMediaServerService {
153 if (streamId == null) { 153 if (streamId == null) {
154 streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); 154 streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
155 } 155 }
  156 + int ssrcCheckParam = 0;
  157 + if (ssrcCheck && tcpMode > 1) {
  158 + // 目前zlm不支持 tcp模式更新ssrc,暂时关闭ssrc校验
  159 + logger.warn("[openRTPServer] TCP被动/TCP主动收流时,默认关闭ssrc检验");
  160 + }
156 int rtpServerPort; 161 int rtpServerPort;
157 if (mediaServerItem.isRtpEnable()) { 162 if (mediaServerItem.isRtpEnable()) {
158 - rtpServerPort = zlmServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port,onlyAuto, reUsePort, tcpMode); 163 + rtpServerPort = zlmServerFactory.createRTPServer(mediaServerItem, streamId, (ssrcCheck && tcpMode == 0)?Integer.parseInt(ssrc):0, port, onlyAuto, reUsePort, tcpMode);
159 } else { 164 } else {
160 rtpServerPort = mediaServerItem.getRtpProxyPort(); 165 rtpServerPort = mediaServerItem.getRtpProxyPort();
161 } 166 }
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -18,6 +18,8 @@ import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; @@ -18,6 +18,8 @@ import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
18 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 18 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
19 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; 19 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
20 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; 20 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  21 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
  22 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
21 import com.genersoft.iot.vmp.gb28181.utils.SipUtils; 23 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
22 import com.genersoft.iot.vmp.media.zlm.*; 24 import com.genersoft.iot.vmp.media.zlm.*;
23 import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils; 25 import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
@@ -43,6 +45,7 @@ import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; @@ -43,6 +45,7 @@ import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
43 import com.genersoft.iot.vmp.vmanager.bean.StreamContent; 45 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
44 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; 46 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
45 import gov.nist.javax.sip.message.SIPResponse; 47 import gov.nist.javax.sip.message.SIPResponse;
  48 +import gov.nist.javax.sip.message.SIPResponse;
46 import org.slf4j.Logger; 49 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory; 50 import org.slf4j.LoggerFactory;
48 import org.springframework.beans.factory.annotation.Autowired; 51 import org.springframework.beans.factory.annotation.Autowired;
@@ -147,15 +150,21 @@ public class PlayServiceImpl implements IPlayService { @@ -147,15 +150,21 @@ public class PlayServiceImpl implements IPlayService {
147 @Override 150 @Override
148 public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback<Object> callback) { 151 public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback<Object> callback) {
149 if (mediaServerItem == null) { 152 if (mediaServerItem == null) {
  153 + logger.warn("[点播] 未找到可用的zlm deviceId: {},channelId:{}", deviceId, channelId);
150 throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm"); 154 throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm");
151 } 155 }
152 156
153 Device device = redisCatchStorage.getDevice(deviceId); 157 Device device = redisCatchStorage.getDevice(deviceId);
  158 + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE") && !mediaServerItem.isRtpEnable()) {
  159 + logger.warn("[点播] 单端口收流时不支持TCP主动方式收流 deviceId: {},channelId:{}", deviceId, channelId);
  160 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "单端口收流时不支持TCP主动方式收流");
  161 + }
154 InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); 162 InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
155 if (inviteInfo != null ) { 163 if (inviteInfo != null ) {
156 if (inviteInfo.getStreamInfo() == null) { 164 if (inviteInfo.getStreamInfo() == null) {
157 // 点播发起了但是尚未成功, 仅注册回调等待结果即可 165 // 点播发起了但是尚未成功, 仅注册回调等待结果即可
158 inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); 166 inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback);
  167 + logger.info("[点播开始] 已经请求中,等待结果, deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
159 return inviteInfo.getSsrcInfo(); 168 return inviteInfo.getSsrcInfo();
160 }else { 169 }else {
161 StreamInfo streamInfo = inviteInfo.getStreamInfo(); 170 StreamInfo streamInfo = inviteInfo.getStreamInfo();
@@ -178,6 +187,7 @@ public class PlayServiceImpl implements IPlayService { @@ -178,6 +187,7 @@ public class PlayServiceImpl implements IPlayService {
178 InviteErrorCode.SUCCESS.getCode(), 187 InviteErrorCode.SUCCESS.getCode(),
179 InviteErrorCode.SUCCESS.getMsg(), 188 InviteErrorCode.SUCCESS.getMsg(),
180 streamInfo); 189 streamInfo);
  190 + logger.info("[点播已存在] 直接返回, deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
181 return inviteInfo.getSsrcInfo(); 191 return inviteInfo.getSsrcInfo();
182 }else { 192 }else {
183 // 点播发起了但是尚未成功, 仅注册回调等待结果即可 193 // 点播发起了但是尚未成功, 仅注册回调等待结果即可
@@ -187,11 +197,8 @@ public class PlayServiceImpl implements IPlayService { @@ -187,11 +197,8 @@ public class PlayServiceImpl implements IPlayService {
187 } 197 }
188 } 198 }
189 } 199 }
190 - String streamId = null;  
191 - if (mediaServerItem.isRtpEnable()) {  
192 - streamId = String.format("%s_%s", device.getDeviceId(), channelId);  
193 - }  
194 - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, false,device.getStreamModeForParam()); 200 + String streamId = String.format("%s_%s", device.getDeviceId(), channelId);;
  201 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, ssrc, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam());
195 if (ssrcInfo == null) { 202 if (ssrcInfo == null) {
196 callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), null); 203 callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), null);
197 inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, 204 inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
@@ -200,7 +207,6 @@ public class PlayServiceImpl implements IPlayService { @@ -200,7 +207,6 @@ public class PlayServiceImpl implements IPlayService {
200 null); 207 null);
201 return null; 208 return null;
202 } 209 }
203 - // TODO 记录点播的状态  
204 play(mediaServerItem, ssrcInfo, device, channelId, callback); 210 play(mediaServerItem, ssrcInfo, device, channelId, callback);
205 return ssrcInfo; 211 return ssrcInfo;
206 } 212 }
@@ -357,8 +363,8 @@ public class PlayServiceImpl implements IPlayService { @@ -357,8 +363,8 @@ public class PlayServiceImpl implements IPlayService {
357 null); 363 null);
358 return; 364 return;
359 } 365 }
360 - logger.info("[点播开始] deviceId: {}, channelId: {},码流类型:{}, 收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}",  
361 - device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流", ssrcInfo.getPort(), 366 + logger.info("[点播开始] deviceId: {}, channelId: {},码流类型:{}, 收流端口: {}, STREAM:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}",
  367 + device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流", ssrcInfo.getPort(), ssrcInfo.getStream(),
362 device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); 368 device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
363 //端口获取失败的ssrcInfo 没有必要发送点播指令 369 //端口获取失败的ssrcInfo 没有必要发送点播指令
364 if (ssrcInfo.getPort() <= 0) { 370 if (ssrcInfo.getPort() <= 0) {
@@ -389,16 +395,6 @@ public class PlayServiceImpl implements IPlayService { @@ -389,16 +395,6 @@ public class PlayServiceImpl implements IPlayService {
389 device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流", 395 device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流",
390 ssrcInfo.getPort(), ssrcInfo.getSsrc()); 396 ssrcInfo.getPort(), ssrcInfo.getSsrc());
391 397
392 - // 点播超时回复BYE 同时释放ssrc以及此次点播的资源  
393 -// InviteInfo inviteInfoForTimeout = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.play, device.getDeviceId(), channelId);  
394 -// if (inviteInfoForTimeout == null) {  
395 -// return;  
396 -// }  
397 -// if (InviteSessionStatus.ok == inviteInfoForTimeout.getStatus() ) {  
398 -// // TODO 发送bye  
399 -// }else {  
400 -// // TODO 发送cancel  
401 -// }  
402 callback.run(InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); 398 callback.run(InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null);
403 inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, 399 inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
404 InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); 400 InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null);
@@ -443,128 +439,10 @@ public class PlayServiceImpl implements IPlayService { @@ -443,128 +439,10 @@ public class PlayServiceImpl implements IPlayService {
443 logger.info("[点播成功] deviceId: {}, channelId:{}, 码流类型:{}", device.getDeviceId(), channelId, 439 logger.info("[点播成功] deviceId: {}, channelId:{}, 码流类型:{}", device.getDeviceId(), channelId,
444 device.isSwitchPrimarySubStream() ? "辅码流" : "主码流"); 440 device.isSwitchPrimarySubStream() ? "辅码流" : "主码流");
445 snapOnPlay(mediaServerItemInuse, device.getDeviceId(), channelId, ssrcInfo.getStream()); 441 snapOnPlay(mediaServerItemInuse, device.getDeviceId(), channelId, ssrcInfo.getStream());
446 - }, (event) -> {  
447 - inviteInfo.setStatus(InviteSessionStatus.ok);  
448 -  
449 - ResponseEvent responseEvent = (ResponseEvent) event.event;  
450 - String contentString = new String(responseEvent.getResponse().getRawContent());  
451 - // 获取ssrc  
452 - int ssrcIndex = contentString.indexOf("y=");  
453 - // 检查是否有y字段  
454 - if (ssrcIndex >= 0) {  
455 - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容  
456 - String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12).trim();  
457 - // 查询到ssrc不一致且开启了ssrc校验则需要针对处理  
458 - if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {  
459 - if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {  
460 - String substring = contentString.substring(0, contentString.indexOf("y="));  
461 - try {  
462 - SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);  
463 - int port = -1;  
464 - Vector mediaDescriptions = sdp.getMediaDescriptions(true);  
465 - for (Object description : mediaDescriptions) {  
466 - MediaDescription mediaDescription = (MediaDescription) description;  
467 - Media media = mediaDescription.getMedia();  
468 -  
469 - Vector mediaFormats = media.getMediaFormats(false);  
470 - if (mediaFormats.contains("96")) {  
471 - port = media.getMediaPort();  
472 - break;  
473 - }  
474 - }  
475 - logger.info("[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());  
476 - JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream());  
477 - logger.info("[点播-TCP主动连接对方] 结果: {}", jsonObject);  
478 - } catch (SdpException e) {  
479 - logger.error("[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e);  
480 - dynamicTask.stop(timeOutTaskKey);  
481 - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());  
482 - // 释放ssrc  
483 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());  
484 -  
485 - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());  
486 -  
487 - callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),  
488 - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);  
489 - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,  
490 - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),  
491 - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);  
492 - }  
493 - }  
494 - return;  
495 - }  
496 - logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);  
497 - if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {  
498 - logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);  
499 - // 释放ssrc  
500 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());  
501 - // 单端口模式streamId也有变化,重新设置监听即可  
502 - if (!mediaServerItem.isRtpEnable()) {  
503 - // 添加订阅  
504 - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());  
505 - subscribe.removeSubscribe(hookSubscribe);  
506 - String stream = String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase();  
507 - hookSubscribe.getContent().put("stream", stream);  
508 - inviteStreamService.updateInviteInfoForStream(inviteInfo, stream);  
509 - subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> {  
510 - logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam);  
511 - dynamicTask.stop(timeOutTaskKey);  
512 - // hook响应  
513 - StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInUse, hookParam, device.getDeviceId(), channelId);  
514 - if (streamInfo == null){  
515 - callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),  
516 - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);  
517 - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,  
518 - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),  
519 - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);  
520 - return;  
521 - }  
522 - callback.run(InviteErrorCode.SUCCESS.getCode(),  
523 - InviteErrorCode.SUCCESS.getMsg(), streamInfo);  
524 - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,  
525 - InviteErrorCode.SUCCESS.getCode(),  
526 - InviteErrorCode.SUCCESS.getMsg(),  
527 - streamInfo);  
528 - snapOnPlay(mediaServerItemInUse, device.getDeviceId(), channelId, stream);  
529 - });  
530 - return;  
531 - }  
532 -  
533 - // 更新ssrc  
534 - Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse);  
535 - if (!result) {  
536 - try {  
537 - logger.warn("[点播] 更新ssrc失败,停止点播 {}/{}", device.getDeviceId(), channelId);  
538 - cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);  
539 - } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {  
540 - logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());  
541 - }  
542 -  
543 - dynamicTask.stop(timeOutTaskKey);  
544 - // 释放ssrc  
545 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());  
546 -  
547 - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());  
548 -  
549 - callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),  
550 - "下级自定义了ssrc,重新设置收流信息失败", null);  
551 - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,  
552 - InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),  
553 - "下级自定义了ssrc,重新设置收流信息失败", null);  
554 -  
555 - }else {  
556 - if (ssrcInfo.getStream()!= null && !ssrcInfo.getStream().equals(inviteInfo.getStream())) {  
557 - inviteStreamService.removeInviteInfo(inviteInfo);  
558 - }  
559 - ssrcInfo.setSsrc(ssrcInResponse);  
560 - inviteInfo.setSsrcInfo(ssrcInfo);  
561 - inviteInfo.setStream(ssrcInfo.getStream());  
562 - }  
563 - }else {  
564 - logger.info("[点播消息] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正");  
565 - }  
566 - }  
567 - inviteStreamService.updateInviteInfo(inviteInfo); 442 + }, (eventResult) -> {
  443 + // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题
  444 + InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId,
  445 + timeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAY);
568 }, (event) -> { 446 }, (event) -> {
569 dynamicTask.stop(timeOutTaskKey); 447 dynamicTask.stop(timeOutTaskKey);
570 mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); 448 mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
@@ -601,6 +479,47 @@ public class PlayServiceImpl implements IPlayService { @@ -601,6 +479,47 @@ public class PlayServiceImpl implements IPlayService {
601 } 479 }
602 } 480 }
603 481
  482 + private void tcpActiveHandler(Device device, String channelId, String contentString,
  483 + MediaServerItem mediaServerItem,
  484 + String timeOutTaskKey, SSRCInfo ssrcInfo, ErrorCallback<Object> callback){
  485 + if (!device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {
  486 + return;
  487 + }
  488 + String substring = contentString.substring(0, contentString.indexOf("y="));
  489 + try {
  490 + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
  491 + int port = -1;
  492 + Vector mediaDescriptions = sdp.getMediaDescriptions(true);
  493 + for (Object description : mediaDescriptions) {
  494 + MediaDescription mediaDescription = (MediaDescription) description;
  495 + Media media = mediaDescription.getMedia();
  496 +
  497 + Vector mediaFormats = media.getMediaFormats(false);
  498 + if (mediaFormats.contains("96")) {
  499 + port = media.getMediaPort();
  500 + break;
  501 + }
  502 + }
  503 + logger.info("[TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
  504 + JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream());
  505 + logger.info("[TCP主动连接对方] 结果: {}", jsonObject);
  506 + } catch (SdpException e) {
  507 + logger.error("[TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e);
  508 + dynamicTask.stop(timeOutTaskKey);
  509 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
  510 + // 释放ssrc
  511 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  512 +
  513 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  514 +
  515 + callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),
  516 + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);
  517 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  518 + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),
  519 + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);
  520 + }
  521 + }
  522 +
604 /** 523 /**
605 * 点播成功时调用截图. 524 * 点播成功时调用截图.
606 * 525 *
@@ -707,22 +626,23 @@ public class PlayServiceImpl implements IPlayService { @@ -707,22 +626,23 @@ public class PlayServiceImpl implements IPlayService {
707 String endTime, ErrorCallback<Object> callback) { 626 String endTime, ErrorCallback<Object> callback) {
708 Device device = storager.queryVideoDevice(deviceId); 627 Device device = storager.queryVideoDevice(deviceId);
709 if (device == null) { 628 if (device == null) {
710 - return; 629 + logger.warn("[录像回放] 未找到设备 deviceId: {},channelId:{}", deviceId, channelId);
  630 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到设备:" + deviceId);
711 } 631 }
  632 +
712 MediaServerItem newMediaServerItem = getNewMediaServerItem(device); 633 MediaServerItem newMediaServerItem = getNewMediaServerItem(device);
713 - String stream = null;  
714 - if (newMediaServerItem.isRtpEnable()) {  
715 - String startTimeStr = startTime.replace("-", "")  
716 - .replace(":", "")  
717 - .replace(" ", "");  
718 - System.out.println(startTimeStr);  
719 - String endTimeTimeStr = endTime.replace("-", "")  
720 - .replace(":", "")  
721 - .replace(" ", "");  
722 - System.out.println(endTimeTimeStr);  
723 - stream = deviceId + "_" + channelId + "_" + startTimeStr + "_" + endTimeTimeStr;  
724 - }  
725 - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, stream, null, device.isSsrcCheck(), true, 0, false,false, device.getStreamModeForParam()); 634 + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE") && ! newMediaServerItem.isRtpEnable()) {
  635 + logger.warn("[录像回放] 单端口收流时不支持TCP主动方式收流 deviceId: {},channelId:{}", deviceId, channelId);
  636 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "单端口收流时不支持TCP主动方式收流");
  637 + }
  638 + String startTimeStr = startTime.replace("-", "")
  639 + .replace(":", "")
  640 + .replace(" ", "");
  641 + String endTimeTimeStr = endTime.replace("-", "")
  642 + .replace(":", "")
  643 + .replace(" ", "");
  644 + String stream = deviceId + "_" + channelId + "_" + startTimeStr + "_" + endTimeTimeStr;
  645 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, stream, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam());
726 playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, callback); 646 playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, callback);
727 } 647 }
728 648
@@ -795,113 +715,13 @@ public class PlayServiceImpl implements IPlayService { @@ -795,113 +715,13 @@ public class PlayServiceImpl implements IPlayService {
795 try { 715 try {
796 cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, 716 cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime,
797 hookEvent, eventResult -> { 717 hookEvent, eventResult -> {
798 - inviteInfo.setStatus(InviteSessionStatus.ok);  
799 - ResponseEvent responseEvent = (ResponseEvent) eventResult.event;  
800 - String contentString = new String(responseEvent.getResponse().getRawContent());  
801 - // 获取ssrc  
802 - int ssrcIndex = contentString.indexOf("y=");  
803 - // 检查是否有y字段  
804 - if (ssrcIndex >= 0) {  
805 - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容  
806 - String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);  
807 - // 查询到ssrc不一致且开启了ssrc校验则需要针对处理  
808 - if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {  
809 - if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {  
810 - String substring = contentString.substring(0, contentString.indexOf("y="));  
811 - try {  
812 - SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);  
813 - int port = -1;  
814 - Vector mediaDescriptions = sdp.getMediaDescriptions(true);  
815 - for (Object description : mediaDescriptions) {  
816 - MediaDescription mediaDescription = (MediaDescription) description;  
817 - Media media = mediaDescription.getMedia();  
818 -  
819 - Vector mediaFormats = media.getMediaFormats(false);  
820 - if (mediaFormats.contains("96")) {  
821 - port = media.getMediaPort();  
822 - break;  
823 - }  
824 - }  
825 - logger.info("[录像回放-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());  
826 - JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream());  
827 - logger.info("[录像回放-TCP主动连接对方] 结果: {}", jsonObject);  
828 - } catch (SdpException e) {  
829 - logger.error("[录像回放-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e);  
830 - dynamicTask.stop(playBackTimeOutTaskKey);  
831 - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());  
832 - // 释放ssrc  
833 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());  
834 -  
835 - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());  
836 -  
837 - callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),  
838 - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);  
839 - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,  
840 - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),  
841 - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);  
842 - }  
843 - }  
844 - return;  
845 - }  
846 - logger.info("[录像回放] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);  
847 - if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {  
848 - logger.info("[录像回放] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);  
849 -  
850 - // 释放ssrc  
851 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());  
852 -  
853 - // 单端口模式streamId也有变化,需要重新设置监听  
854 - if (!mediaServerItem.isRtpEnable()) {  
855 - // 添加订阅  
856 - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());  
857 - subscribe.removeSubscribe(hookSubscribe);  
858 - String stream = String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase();  
859 - hookSubscribe.getContent().put("stream", stream);  
860 - inviteStreamService.updateInviteInfoForStream(inviteInfo, stream);  
861 - subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> {  
862 - logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam);  
863 - dynamicTask.stop(playBackTimeOutTaskKey);  
864 - // hook响应  
865 - hookEvent.response(mediaServerItemInUse, hookParam);  
866 - });  
867 - }  
868 - // 更新ssrc  
869 - Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse);  
870 - if (!result) {  
871 - try {  
872 - logger.warn("[录像回放] 更新ssrc失败,停止录像回放 {}/{}", device.getDeviceId(), channelId);  
873 - cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);  
874 - } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {  
875 - logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());  
876 -  
877 - }  
878 -  
879 - dynamicTask.stop(playBackTimeOutTaskKey);  
880 - // 释放ssrc  
881 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());  
882 -  
883 - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());  
884 -  
885 - callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),  
886 - "下级自定义了ssrc,重新设置收流信息失败", null);  
887 -  
888 - }else {  
889 - if (ssrcInfo.getStream()!= null && !ssrcInfo.getStream().equals(inviteInfo.getStream())) {  
890 - inviteStreamService.removeInviteInfo(inviteInfo);  
891 - }  
892 -  
893 - ssrcInfo.setSsrc(ssrcInResponse);  
894 - inviteInfo.setSsrcInfo(ssrcInfo);  
895 - inviteInfo.setStream(ssrcInfo.getStream());  
896 - }  
897 - }else {  
898 - logger.info("[点播消息] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正");  
899 - }  
900 - }  
901 - inviteStreamService.updateInviteInfo(inviteInfo); 718 + // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题
  719 + InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId,
  720 + playBackTimeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAYBACK);
  721 +
902 }, errorEvent); 722 }, errorEvent);
903 } catch (InvalidArgumentException | SipException | ParseException e) { 723 } catch (InvalidArgumentException | SipException | ParseException e) {
904 - logger.error("[命令发送失败] 回放: {}", e.getMessage()); 724 + logger.error("[命令发送失败] 录像回放: {}", e.getMessage());
905 725
906 SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); 726 SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult();
907 eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; 727 eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent;
@@ -912,6 +732,90 @@ public class PlayServiceImpl implements IPlayService { @@ -912,6 +732,90 @@ public class PlayServiceImpl implements IPlayService {
912 } 732 }
913 733
914 734
  735 + private void InviteOKHandler(SipSubscribe.EventResult eventResult, SSRCInfo ssrcInfo, MediaServerItem mediaServerItem,
  736 + Device device, String channelId, String timeOutTaskKey, ErrorCallback<Object> callback,
  737 + InviteInfo inviteInfo, InviteSessionType inviteSessionType){
  738 + inviteInfo.setStatus(InviteSessionStatus.ok);
  739 + ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
  740 + String contentString = new String(responseEvent.getResponse().getRawContent());
  741 + String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString);
  742 + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
  743 + // ssrc 一致
  744 + if (mediaServerItem.isRtpEnable()) {
  745 + // 多端口
  746 + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {
  747 + tcpActiveHandler(device, channelId, contentString, mediaServerItem, timeOutTaskKey, ssrcInfo, callback);
  748 + }
  749 + }else {
  750 + // 单端口
  751 + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {
  752 + logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流");
  753 + }
  754 +
  755 + }
  756 + }else {
  757 + logger.info("[Invite 200OK] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
  758 + // ssrc 不一致
  759 + if (mediaServerItem.isRtpEnable()) {
  760 + // 多端口
  761 + if (device.isSsrcCheck()) {
  762 + // ssrc检验
  763 + // 更新ssrc
  764 + logger.info("[Invite 200OK] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
  765 + // 释放ssrc
  766 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  767 + Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse);
  768 + if (!result) {
  769 + try {
  770 + logger.warn("[Invite 200OK] 更新ssrc失败,停止点播 {}/{}", device.getDeviceId(), channelId);
  771 + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);
  772 + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
  773 + logger.error("[命令发送失败] 停止播放, 发送BYE: {}", e.getMessage());
  774 + }
  775 +
  776 + dynamicTask.stop(timeOutTaskKey);
  777 + // 释放ssrc
  778 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  779 +
  780 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  781 +
  782 + callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
  783 + "下级自定义了ssrc,重新设置收流信息失败", null);
  784 + inviteStreamService.call(inviteSessionType, device.getDeviceId(), channelId, null,
  785 + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
  786 + "下级自定义了ssrc,重新设置收流信息失败", null);
  787 +
  788 + }else {
  789 + ssrcInfo.setSsrc(ssrcInResponse);
  790 + inviteInfo.setSsrcInfo(ssrcInfo);
  791 + inviteInfo.setStream(ssrcInfo.getStream());
  792 + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {
  793 + if (mediaServerItem.isRtpEnable()) {
  794 + tcpActiveHandler(device, channelId, contentString, mediaServerItem, timeOutTaskKey, ssrcInfo, callback);
  795 + }else {
  796 + logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流");
  797 + }
  798 + }
  799 + inviteStreamService.updateInviteInfo(inviteInfo);
  800 + }
  801 + }
  802 + }else {
  803 + if (ssrcInResponse != null) {
  804 + // 单端口
  805 + // 重新订阅流上线
  806 + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(inviteInfo.getDeviceId(),
  807 + inviteInfo.getChannelId(), null, inviteInfo.getStream());
  808 + streamSession.remove(inviteInfo.getDeviceId(),
  809 + inviteInfo.getChannelId(), inviteInfo.getStream());
  810 + inviteStreamService.updateInviteInfoForSSRC(inviteInfo, ssrcInResponse);
  811 + streamSession.put(device.getDeviceId(), channelId, ssrcTransaction.getCallId(),
  812 + inviteInfo.getStream(), ssrcInResponse, mediaServerItem.getId(), (SIPResponse) responseEvent.getResponse(), inviteSessionType);
  813 + }
  814 + }
  815 + }
  816 + }
  817 +
  818 +
915 @Override 819 @Override
916 public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback) { 820 public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback) {
917 Device device = storager.queryVideoDevice(deviceId); 821 Device device = storager.queryVideoDevice(deviceId);
@@ -925,6 +829,7 @@ public class PlayServiceImpl implements IPlayService { @@ -925,6 +829,7 @@ public class PlayServiceImpl implements IPlayService {
925 null); 829 null);
926 return; 830 return;
927 } 831 }
  832 + // 录像下载不使用固定流地址,固定流地址会导致如果开始时间与结束时间一致时文件错误的叠加在一起
928 SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,false, device.getStreamModeForParam()); 833 SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,false, device.getStreamModeForParam());
929 download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed, callback); 834 download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed, callback);
930 } 835 }
@@ -993,108 +898,9 @@ public class PlayServiceImpl implements IPlayService { @@ -993,108 +898,9 @@ public class PlayServiceImpl implements IPlayService {
993 try { 898 try {
994 cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, 899 cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed,
995 hookEvent, errorEvent, eventResult ->{ 900 hookEvent, errorEvent, eventResult ->{
996 - inviteInfo.setStatus(InviteSessionStatus.ok);  
997 - ResponseEvent responseEvent = (ResponseEvent) eventResult.event;  
998 - String contentString = new String(responseEvent.getResponse().getRawContent());  
999 - // 获取ssrc  
1000 - int ssrcIndex = contentString.indexOf("y=");  
1001 - // 检查是否有y字段  
1002 - if (ssrcIndex >= 0) {  
1003 - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容  
1004 - String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);  
1005 - // 查询到ssrc不一致且开启了ssrc校验则需要针对处理  
1006 - if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {  
1007 - if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {  
1008 - String substring = contentString.substring(0, contentString.indexOf("y="));  
1009 - try {  
1010 - SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);  
1011 - int port = -1;  
1012 - Vector mediaDescriptions = sdp.getMediaDescriptions(true);  
1013 - for (Object description : mediaDescriptions) {  
1014 - MediaDescription mediaDescription = (MediaDescription) description;  
1015 - Media media = mediaDescription.getMedia();  
1016 -  
1017 - Vector mediaFormats = media.getMediaFormats(false);  
1018 - if (mediaFormats.contains("96")) {  
1019 - port = media.getMediaPort();  
1020 - break;  
1021 - }  
1022 - }  
1023 - logger.info("[录像下载-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());  
1024 - JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream());  
1025 - logger.info("[录像下载-TCP主动连接对方] 结果: {}", jsonObject);  
1026 - } catch (SdpException e) {  
1027 - logger.error("[录像下载-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e);  
1028 - dynamicTask.stop(downLoadTimeOutTaskKey);  
1029 - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());  
1030 - // 释放ssrc  
1031 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());  
1032 -  
1033 - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());  
1034 -  
1035 - callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),  
1036 - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);  
1037 - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,  
1038 - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),  
1039 - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);  
1040 - }  
1041 - }  
1042 - return;  
1043 - }  
1044 - logger.info("[录像下载] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);  
1045 - if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {  
1046 - logger.info("[录像下载] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);  
1047 -  
1048 - // 释放ssrc  
1049 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());  
1050 -  
1051 - // 单端口模式streamId也有变化,需要重新设置监听  
1052 - if (!mediaServerItem.isRtpEnable()) {  
1053 - // 添加订阅  
1054 - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());  
1055 - subscribe.removeSubscribe(hookSubscribe);  
1056 - String stream = String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase();  
1057 - hookSubscribe.getContent().put("stream", stream);  
1058 - inviteStreamService.updateInviteInfoForStream(inviteInfo, stream);  
1059 - subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> {  
1060 - logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam);  
1061 - dynamicTask.stop(downLoadTimeOutTaskKey);  
1062 - hookEvent.response(mediaServerItemInUse, hookParam);  
1063 - });  
1064 - }  
1065 -  
1066 - // 更新ssrc  
1067 - Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse);  
1068 - if (!result) {  
1069 - try {  
1070 - logger.warn("[录像下载] 更新ssrc失败,停止录像回放 {}/{}", device.getDeviceId(), channelId);  
1071 - cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);  
1072 - } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {  
1073 - logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());  
1074 - }  
1075 -  
1076 - dynamicTask.stop(downLoadTimeOutTaskKey);  
1077 - // 释放ssrc  
1078 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());  
1079 -  
1080 - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());  
1081 -  
1082 - callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),  
1083 - "下级自定义了ssrc,重新设置收流信息失败", null);  
1084 -  
1085 - }else {  
1086 - if (ssrcInfo.getStream()!= null && !ssrcInfo.getStream().equals(inviteInfo.getStream())) {  
1087 - inviteStreamService.removeInviteInfo(inviteInfo);  
1088 - }  
1089 - ssrcInfo.setSsrc(ssrcInResponse);  
1090 - inviteInfo.setSsrcInfo(ssrcInfo);  
1091 - inviteInfo.setStream(ssrcInfo.getStream());  
1092 - }  
1093 - }else {  
1094 - logger.info("[录像下载] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正");  
1095 - }  
1096 - }  
1097 - inviteStreamService.updateInviteInfo(inviteInfo); 901 + // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题
  902 + InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId,
  903 + downLoadTimeOutTaskKey, callback, inviteInfo, InviteSessionType.DOWNLOAD);
1098 }); 904 });
1099 } catch (InvalidArgumentException | SipException | ParseException e) { 905 } catch (InvalidArgumentException | SipException | ParseException e) {
1100 logger.error("[命令发送失败] 录像下载: {}", e.getMessage()); 906 logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java
@@ -7,6 +7,7 @@ import java.time.LocalDateTime; @@ -7,6 +7,7 @@ import java.time.LocalDateTime;
7 import java.time.ZoneId; 7 import java.time.ZoneId;
8 import java.time.format.DateTimeFormatter; 8 import java.time.format.DateTimeFormatter;
9 import java.time.format.DateTimeParseException; 9 import java.time.format.DateTimeParseException;
  10 +import java.time.temporal.ChronoUnit;
10 import java.time.temporal.TemporalAccessor; 11 import java.time.temporal.TemporalAccessor;
11 12
12 import java.util.Locale; 13 import java.util.Locale;
@@ -106,4 +107,9 @@ public class DateUtil { @@ -106,4 +107,9 @@ public class DateUtil {
106 LocalDateTime nowDateTime = LocalDateTime.now(); 107 LocalDateTime nowDateTime = LocalDateTime.now();
107 return formatterISO8601.format(nowDateTime); 108 return formatterISO8601.format(nowDateTime);
108 } 109 }
  110 +
  111 + public static long getDifferenceForNow(String keepaliveTime) {
  112 + Instant beforeInstant = Instant.from(formatter.parse(keepaliveTime));
  113 + return ChronoUnit.MILLIS.between(beforeInstant, Instant.now());
  114 + }
109 } 115 }
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java
@@ -67,7 +67,7 @@ public class MediaController { @@ -67,7 +67,7 @@ public class MediaController {
67 && streamAuthorityInfo.getCallId().equals(callId)) { 67 && streamAuthorityInfo.getCallId().equals(callId)) {
68 authority = true; 68 authority = true;
69 }else { 69 }else {
70 - throw new ControllerException(ErrorCode.ERROR400); 70 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "获取播放地址鉴权失败");
71 } 71 }
72 }else { 72 }else {
73 // 是否登陆用户, 登陆用户返回完整信息 73 // 是否登陆用户, 登陆用户返回完整信息
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
@@ -97,6 +97,7 @@ public class PlayController { @@ -97,6 +97,7 @@ public class PlayController {
97 public DeferredResult<WVPResult<StreamContent>> play(HttpServletRequest request, @PathVariable String deviceId, 97 public DeferredResult<WVPResult<StreamContent>> play(HttpServletRequest request, @PathVariable String deviceId,
98 @PathVariable String channelId) { 98 @PathVariable String channelId) {
99 99
  100 + logger.info("[开始点播] deviceId:{}, channelId:{}, ", deviceId, channelId);
100 // 获取可用的zlm 101 // 获取可用的zlm
101 Device device = storager.queryVideoDevice(deviceId); 102 Device device = storager.queryVideoDevice(deviceId);
102 MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); 103 MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
@@ -109,13 +110,15 @@ public class PlayController { @@ -109,13 +110,15 @@ public class PlayController {
109 DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); 110 DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
110 111
111 result.onTimeout(()->{ 112 result.onTimeout(()->{
112 - logger.info("点播接口等待超时"); 113 + logger.info("[点播等待超时] deviceId:{}, channelId:{}, ", deviceId, channelId);
113 // 释放rtpserver 114 // 释放rtpserver
114 WVPResult<StreamInfo> wvpResult = new WVPResult<>(); 115 WVPResult<StreamInfo> wvpResult = new WVPResult<>();
115 wvpResult.setCode(ErrorCode.ERROR100.getCode()); 116 wvpResult.setCode(ErrorCode.ERROR100.getCode());
116 wvpResult.setMsg("点播超时"); 117 wvpResult.setMsg("点播超时");
117 requestMessage.setData(wvpResult); 118 requestMessage.setData(wvpResult);
118 - resultHolder.invokeResult(requestMessage); 119 + resultHolder.invokeAllResult(requestMessage);
  120 + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
  121 + storager.stopPlay(deviceId, channelId);
119 }); 122 });
120 123
121 // 录像查询以channelId作为deviceId查询 124 // 录像查询以channelId作为deviceId查询
@@ -168,7 +171,7 @@ public class PlayController { @@ -168,7 +171,7 @@ public class PlayController {
168 } 171 }
169 if (InviteSessionStatus.ok == inviteInfo.getStatus()) { 172 if (InviteSessionStatus.ok == inviteInfo.getStatus()) {
170 try { 173 try {
171 - logger.warn("[停止点播] {}/{}", device.getDeviceId(), channelId); 174 + logger.info("[停止点播] {}/{}", device.getDeviceId(), channelId);
172 cmder.streamByeCmd(device, channelId, inviteInfo.getStream(), null, null); 175 cmder.streamByeCmd(device, channelId, inviteInfo.getStream(), null, null);
173 } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { 176 } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
174 logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); 177 logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());
@@ -338,7 +341,7 @@ public class PlayController { @@ -338,7 +341,7 @@ public class PlayController {
338 message.setKey(key); 341 message.setKey(key);
339 message.setId(uuid); 342 message.setId(uuid);
340 343
341 - String fileName = deviceId + "_" + channelId + "_" + DateUtil.getNowForUrl() + "jpg"; 344 + String fileName = deviceId + "_" + channelId + "_" + DateUtil.getNowForUrl() + ".jpg";
342 playService.getSnap(deviceId, channelId, fileName, (code, msg, data) -> { 345 playService.getSnap(deviceId, channelId, fileName, (code, msg, data) -> {
343 if (code == InviteErrorCode.SUCCESS.getCode()) { 346 if (code == InviteErrorCode.SUCCESS.getCode()) {
344 message.setData(data); 347 message.setData(data);
src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java
@@ -42,7 +42,7 @@ public class LogController { @@ -42,7 +42,7 @@ public class LogController {
42 * @return 42 * @return
43 */ 43 */
44 @GetMapping("/all") 44 @GetMapping("/all")
45 - @Operation(summary = "分页查询报警") 45 + @Operation(summary = "分页查询日志")
46 @Parameter(name = "query", description = "查询内容", required = true) 46 @Parameter(name = "query", description = "查询内容", required = true)
47 @Parameter(name = "page", description = "当前页", required = true) 47 @Parameter(name = "page", description = "当前页", required = true)
48 @Parameter(name = "count", description = "每页查询数量", required = true) 48 @Parameter(name = "count", description = "每页查询数量", required = true)
@@ -84,7 +84,7 @@ public class LogController { @@ -84,7 +84,7 @@ public class LogController {
84 * 清空日志 84 * 清空日志
85 * 85 *
86 */ 86 */
87 - @Operation(summary = "停止视频回放") 87 + @Operation(summary = "清空日志")
88 @DeleteMapping("/clear") 88 @DeleteMapping("/clear")
89 public void clear() { 89 public void clear() {
90 logService.clear(); 90 logService.clear();
src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
@@ -266,12 +266,4 @@ public class ServerController { @@ -266,12 +266,4 @@ public class ServerController {
266 266
267 return result; 267 return result;
268 } 268 }
269 -  
270 - @PostMapping(value = "/test/getPort")  
271 - @ResponseBody  
272 - public int getPort() {  
273 - int result = sendRtpPortManager.getNextPort(mediaServerService.getDefaultMediaServer());  
274 - System.out.println(result);  
275 - return result;  
276 - }  
277 } 269 }
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java
@@ -133,17 +133,29 @@ public class ApiStreamController { @@ -133,17 +133,29 @@ public class ApiStreamController {
133 result.put("ChannelName", deviceChannel.getName()); 133 result.put("ChannelName", deviceChannel.getName());
134 result.put("ChannelCustomName", ""); 134 result.put("ChannelCustomName", "");
135 result.put("FLV", inviteInfo.getStreamInfo().getFlv().getUrl()); 135 result.put("FLV", inviteInfo.getStreamInfo().getFlv().getUrl());
136 - result.put("HTTPS_FLV", inviteInfo.getStreamInfo().getHttps_flv().getUrl()); 136 + if(inviteInfo.getStreamInfo().getHttps_flv() != null) {
  137 + result.put("HTTPS_FLV", inviteInfo.getStreamInfo().getHttps_flv().getUrl());
  138 + }
137 result.put("WS_FLV", inviteInfo.getStreamInfo().getWs_flv().getUrl()); 139 result.put("WS_FLV", inviteInfo.getStreamInfo().getWs_flv().getUrl());
138 - result.put("WSS_FLV", inviteInfo.getStreamInfo().getWss_flv().getUrl()); 140 + if(inviteInfo.getStreamInfo().getWss_flv() != null) {
  141 + result.put("WSS_FLV", inviteInfo.getStreamInfo().getWss_flv().getUrl());
  142 + }
139 result.put("RTMP", inviteInfo.getStreamInfo().getRtmp().getUrl()); 143 result.put("RTMP", inviteInfo.getStreamInfo().getRtmp().getUrl());
140 - result.put("RTMPS", inviteInfo.getStreamInfo().getRtmps().getUrl()); 144 + if (inviteInfo.getStreamInfo().getRtmps() != null) {
  145 + result.put("RTMPS", inviteInfo.getStreamInfo().getRtmps().getUrl());
  146 + }
141 result.put("HLS", inviteInfo.getStreamInfo().getHls().getUrl()); 147 result.put("HLS", inviteInfo.getStreamInfo().getHls().getUrl());
142 - result.put("HTTPS_HLS", inviteInfo.getStreamInfo().getHttps_hls().getUrl()); 148 + if (inviteInfo.getStreamInfo().getHttps_hls() != null) {
  149 + result.put("HTTPS_HLS", inviteInfo.getStreamInfo().getHttps_hls().getUrl());
  150 + }
143 result.put("RTSP", inviteInfo.getStreamInfo().getRtsp().getUrl()); 151 result.put("RTSP", inviteInfo.getStreamInfo().getRtsp().getUrl());
144 - result.put("RTSPS", inviteInfo.getStreamInfo().getRtsps().getUrl()); 152 + if (inviteInfo.getStreamInfo().getRtsps() != null) {
  153 + result.put("RTSPS", inviteInfo.getStreamInfo().getRtsps().getUrl());
  154 + }
145 result.put("WEBRTC", inviteInfo.getStreamInfo().getRtc().getUrl()); 155 result.put("WEBRTC", inviteInfo.getStreamInfo().getRtc().getUrl());
146 - result.put("HTTPS_WEBRTC", inviteInfo.getStreamInfo().getRtcs().getUrl()); 156 + if (inviteInfo.getStreamInfo().getRtcs() != null) {
  157 + result.put("HTTPS_WEBRTC", inviteInfo.getStreamInfo().getRtcs().getUrl());
  158 + }
147 result.put("CDN", ""); 159 result.put("CDN", "");
148 result.put("SnapURL", ""); 160 result.put("SnapURL", "");
149 result.put("Transport", device.getTransport()); 161 result.put("Transport", device.getTransport());
src/main/resources/all-application.yml
@@ -187,9 +187,6 @@ user-settings: @@ -187,9 +187,6 @@ user-settings:
187 stream-on-demand: true 187 stream-on-demand: true
188 # 推流鉴权, 默认开启 188 # 推流鉴权, 默认开启
189 push-authority: true 189 push-authority: true
190 - # 国标级联发流严格模式,严格模式会使用与sdp信息中一致的端口发流,端口共享media.rtp.port-range,这会损失一些性能,  
191 - # 非严格模式使用随机端口发流,性能更好, 默认关闭  
192 - gb-send-stream-strict: false  
193 # 设备上线时是否自动同步通道 190 # 设备上线时是否自动同步通道
194 sync-channel-on-device-online: false 191 sync-channel-on-device-online: false
195 # 国标级联语音喊话发流模式 * UDP:udp传输 TCP-ACTIVE:tcp主动模式 TCP-PASSIVE:tcp被动模式 192 # 国标级联语音喊话发流模式 * UDP:udp传输 TCP-ACTIVE:tcp主动模式 TCP-PASSIVE:tcp被动模式