| 
					
				 | 
			
			
				@@ -1,6 +1,8 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 package com.genersoft.iot.vmp.service.impl; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.alibaba.fastjson2.JSONObject; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.common.InviteInfo; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.genersoft.iot.vmp.common.InviteSessionStatus; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.common.InviteSessionType; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.baomidou.dynamic.datasource.annotation.DS; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.conf.DynamicTask; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -11,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -22,20 +25,20 @@ import com.genersoft.iot.vmp.service.IInviteStreamService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.service.IMediaServerService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.service.IPlatformService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.service.IPlayService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import com.genersoft.iot.vmp.service.bean.SSRCInfo; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.genersoft.iot.vmp.service.bean.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.storager.dao.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.genersoft.iot.vmp.utils.DateUtil; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.github.pagehelper.PageHelper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.github.pagehelper.PageInfo; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import gov.nist.javax.sip.message.SIPRequest; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import gov.nist.javax.sip.message.SIPResponse; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.slf4j.Logger; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.slf4j.LoggerFactory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.springframework.beans.factory.annotation.Autowired; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.springframework.stereotype.Service; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import javax.sdp.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import javax.sip.InvalidArgumentException; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import javax.sip.ResponseEvent; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import javax.sip.PeerUnavailableException; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -109,6 +112,9 @@ public class PlatformServiceImpl implements IPlatformService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Autowired 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private IInviteStreamService inviteStreamService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Autowired 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private ZLMRESTfulUtils zlmresTfulUtils; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Override 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public ParentPlatform queryPlatformByServerGBId(String platformGbId) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -466,21 +472,21 @@ public class PlatformServiceImpl implements IPlatformService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             logger.info("[国标级联] 语音喊话未找到可用的zlm. platform: {}", platform.getServerGBId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, platform.getServerGBId(), channelId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        InviteInfo inviteInfoForOld = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, platform.getServerGBId(), channelId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (inviteInfoForOld != null && inviteInfoForOld.getStreamInfo() != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             // 如果zlm不存在这个流,则删除数据即可 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            MediaServerItem mediaServerItemForStreamInfo = mediaServerService.getOne(inviteInfo.getStreamInfo().getMediaServerId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            MediaServerItem mediaServerItemForStreamInfo = mediaServerService.getOne(inviteInfoForOld.getStreamInfo().getMediaServerId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if (mediaServerItemForStreamInfo != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Boolean ready = zlmServerFactory.isStreamReady(mediaServerItemForStreamInfo, inviteInfo.getStreamInfo().getApp(), inviteInfo.getStreamInfo().getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Boolean ready = zlmServerFactory.isStreamReady(mediaServerItemForStreamInfo, inviteInfoForOld.getStreamInfo().getApp(), inviteInfoForOld.getStreamInfo().getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if (!ready) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     // 错误存在于redis中的数据 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    inviteStreamService.removeInviteInfo(inviteInfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    inviteStreamService.removeInviteInfo(inviteInfoForOld); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 }else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     // 流确实尚在推流,直接回调结果 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     OnStreamChangedHookParam hookParam = new OnStreamChangedHookParam(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    hookParam.setApp(inviteInfo.getStreamInfo().getApp()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    hookParam.setStream(inviteInfo.getStreamInfo().getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    hookParam.setApp(inviteInfoForOld.getStreamInfo().getApp()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    hookParam.setStream(inviteInfoForOld.getStreamInfo().getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     hookEvent.response(mediaServerItemForStreamInfo, hookParam); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     return; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -515,6 +521,11 @@ public class PlatformServiceImpl implements IPlatformService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         logger.info("[国标级联] 语音喊话,发起Invite消息 deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 platform.getServerGBId(), channelId, ssrcInfo.getPort(), userSetting.getBroadcastForPlatform(), ssrcInfo.getSsrc(), ssrcCheck); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 初始化redis中的invite消息状态 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        InviteInfo inviteInfo = InviteInfo.getInviteInfo(platform.getServerGBId(), channelId, ssrcInfo.getStream(), ssrcInfo, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                mediaServerItem.getSdpIp(), ssrcInfo.getPort(), userSetting.getBroadcastForPlatform(), InviteSessionType.BROADCAST, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                InviteSessionStatus.ready); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        inviteStreamService.updateInviteInfo(inviteInfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         String timeOutTaskKey = UUID.randomUUID().toString(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         dynamicTask.startDelay(timeOutTaskKey, () -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -545,50 +556,206 @@ public class PlatformServiceImpl implements IPlatformService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 hookEvent.response(mediaServerItem, hookParam); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }, event -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 收到200OK 检测ssrc是否有变化,防止上级自定义了ssrc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ResponseEvent responseEvent = (ResponseEvent) event.event; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            String contentString = new String(responseEvent.getResponse().getRawContent()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 获取ssrc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            int ssrcIndex = contentString.indexOf("y="); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 检查是否有y字段 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (ssrcIndex >= 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (ssrcInfo.getSsrc().equals(ssrcInResponse) || ssrcCheck) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            inviteOKHandler(event, ssrcInfo, tcpMode, ssrcCheck, mediaServerItem, platform, channelId, timeOutTaskKey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    null, inviteInfo, InviteSessionType.BROADCAST); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            // 收到200OK 检测ssrc是否有变化,防止上级自定义了ssrc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            ResponseEvent responseEvent = (ResponseEvent) event.event; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            String contentString = new String(responseEvent.getResponse().getRawContent()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            // 获取ssrc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            int ssrcIndex = contentString.indexOf("y="); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            // 检查是否有y字段 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            if (ssrcIndex >= 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                if (ssrcInfo.getSsrc().equals(ssrcInResponse) || ssrcCheck) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    tcpActiveHandler(platform, ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                if (!mediaServerItem.isRtpEnable()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    // 释放ssrc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    // 单端口模式streamId也有变化,需要重新设置监听 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    if (!mediaServerItem.isRtpEnable()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        // 添加订阅 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        subscribe.removeSubscribe(hookSubscribe); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            dynamicTask.stop(timeOutTaskKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            // hook响应 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            playService.onPublishHandlerForPlay(mediaServerItemInUse, hookParam, platform.getServerGBId(), channelId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            hookEvent.response(mediaServerItemInUse, hookParam); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    // 关闭rtp server 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    // 重新开启ssrc server 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, false, false, ssrcInfo.getPort(), true, false, tcpMode); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }, eventResult -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // 收到错误回复 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (errorEvent != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                errorEvent.response(eventResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void inviteOKHandler(SipSubscribe.EventResult eventResult, SSRCInfo ssrcInfo, int tcpMode, boolean ssrcCheck, MediaServerItem mediaServerItem, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                 ParentPlatform platform, String channelId, String timeOutTaskKey, ErrorCallback<Object> callback, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                 InviteInfo inviteInfo, InviteSessionType inviteSessionType){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        inviteInfo.setStatus(InviteSessionStatus.ok); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ResponseEvent responseEvent = (ResponseEvent) eventResult.event; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String contentString = new String(responseEvent.getResponse().getRawContent()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        System.out.println(1111); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        System.out.println(contentString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 兼容回复的消息中缺少ssrc(y字段)的情况 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (ssrcInResponse == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ssrcInResponse = ssrcInfo.getSsrc(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // ssrc 一致 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (mediaServerItem.isRtpEnable()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // 多端口 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (tcpMode == 2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    tcpActiveHandler(platform, channelId, contentString, mediaServerItem, tcpMode, ssrcCheck, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            timeOutTaskKey, ssrcInfo, callback); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // 单端口 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (tcpMode == 2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (!mediaServerItem.isRtpEnable()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logger.info("[Invite 200OK] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // ssrc 不一致 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (mediaServerItem.isRtpEnable()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // 多端口 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (ssrcCheck) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    // ssrc检验 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    // 更新ssrc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    logger.info("[Invite 200OK] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     // 释放ssrc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // 单端口模式streamId也有变化,需要重新设置监听 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (!mediaServerItem.isRtpEnable()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        // 添加订阅 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        subscribe.removeSubscribe(hookSubscribe); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            dynamicTask.stop(timeOutTaskKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            // hook响应 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            playService.onPublishHandlerForPlay(mediaServerItemInUse, hookParam, platform.getServerGBId(), channelId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            hookEvent.response(mediaServerItemInUse, hookParam); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (!result) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            logger.warn("[Invite 200OK] 更新ssrc失败,停止喊话 {}/{}", platform.getServerGBId(), channelId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            commanderForPlatform.streamByeCmd(platform, channelId, ssrcInfo.getStream(), null, null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            logger.error("[命令发送失败] 停止播放, 发送BYE: {}", e.getMessage()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        dynamicTask.stop(timeOutTaskKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        // 释放ssrc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                "下级自定义了ssrc,重新设置收流信息失败", null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        inviteStreamService.call(inviteSessionType, platform.getServerGBId(), channelId, null, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                "下级自定义了ssrc,重新设置收流信息失败", null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    }else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ssrcInfo.setSsrc(ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        inviteInfo.setSsrcInfo(ssrcInfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        inviteInfo.setStream(ssrcInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (tcpMode == 2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            if (mediaServerItem.isRtpEnable()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                tcpActiveHandler(platform, channelId, contentString, mediaServerItem, tcpMode, ssrcCheck, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        timeOutTaskKey, ssrcInfo, callback); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            }else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        inviteStreamService.updateInviteInfo(inviteInfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // 关闭rtp server 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // 重新开启ssrc server 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, false, false, ssrcInfo.getPort(), true, false, tcpMode); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                }else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ssrcInfo.setSsrc(ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    inviteInfo.setSsrcInfo(ssrcInfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    inviteInfo.setStream(ssrcInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (tcpMode == 2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (mediaServerItem.isRtpEnable()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            tcpActiveHandler(platform, channelId, contentString, mediaServerItem, tcpMode, ssrcCheck, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    timeOutTaskKey, ssrcInfo, callback); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        }else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    inviteStreamService.updateInviteInfo(inviteInfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (ssrcInResponse != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    // 单端口 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    // 重新订阅流上线 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(inviteInfo.getDeviceId(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            inviteInfo.getChannelId(), null, inviteInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    streamSession.remove(inviteInfo.getDeviceId(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            inviteInfo.getChannelId(), inviteInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    inviteStreamService.updateInviteInfoForSSRC(inviteInfo, ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    streamSession.put(platform.getServerGBId(), channelId, ssrcTransaction.getCallId(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            inviteInfo.getStream(), ssrcInResponse, mediaServerItem.getId(), (SIPResponse) responseEvent.getResponse(), inviteSessionType); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }, eventResult -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 收到错误回复 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (errorEvent != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                errorEvent.response(eventResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void tcpActiveHandler(ParentPlatform platform, String channelId, String contentString, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                  MediaServerItem mediaServerItem, int tcpMode, boolean ssrcCheck, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                  String timeOutTaskKey, SSRCInfo ssrcInfo, ErrorCallback<Object> callback){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (tcpMode != 2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String substring; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (contentString.indexOf("y=") > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            substring = contentString.substring(0, contentString.indexOf("y=")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            substring = contentString; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            int port = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Vector mediaDescriptions = sdp.getMediaDescriptions(true); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for (Object description : mediaDescriptions) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                MediaDescription mediaDescription = (MediaDescription) description; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Media media = mediaDescription.getMedia(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Vector mediaFormats = media.getMediaFormats(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (mediaFormats.contains("8") || mediaFormats.contains("0")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    port = media.getMediaPort(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logger.info("[TCP主动连接对方] serverGbId: {}, channelId: {}, 连接对方的地址:{}:{}, SSRC: {}, SSRC校验:{}", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    platform.getServerGBId(), channelId, sdp.getConnection().getAddress(), port, ssrcInfo.getSsrc(), ssrcCheck); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logger.info("[TCP主动连接对方] 结果: {}", jsonObject); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (SdpException e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logger.error("[TCP主动连接对方] serverGbId: {}, channelId: {}, 解析200OK的SDP信息失败", platform.getServerGBId(), channelId, e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            dynamicTask.stop(timeOutTaskKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // 释放ssrc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            inviteStreamService.call(InviteSessionType.PLAY, platform.getServerGBId(), channelId, null, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Override 
			 |