Commit 7de73ebd2bc07a51f0f9db031d6f616bdcfe549c

Authored by 648540858
2 parents e45b4436 205f1f1f

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

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
#	src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
Showing 28 changed files with 314 additions and 75 deletions
@@ -207,6 +207,12 @@ @@ -207,6 +207,12 @@
207 <version>2.1.3</version> 207 <version>2.1.3</version>
208 </dependency> 208 </dependency>
209 209
  210 + <dependency>
  211 + <groupId>com.google.guava</groupId>
  212 + <artifactId>guava</artifactId>
  213 + <version>20.0</version>
  214 + </dependency>
  215 +
210 <!-- json解析库fastjson2 --> 216 <!-- json解析库fastjson2 -->
211 <dependency> 217 <dependency>
212 <groupId>com.alibaba.fastjson2</groupId> 218 <groupId>com.alibaba.fastjson2</groupId>
sql/2.6.9更新.sql
1 alter table wvp_device_channel 1 alter table wvp_device_channel
2 - change stream_id stream_id varying(255)  
3 \ No newline at end of file 2 \ No newline at end of file
  3 + change stream_id stream_id varying(255)
  4 +
  5 +alter table wvp_platform
  6 + add auto_push_channel bool default false
  7 +
  8 +alter table wvp_stream_proxy
  9 + add stream_key varying(255)
sql/初始化.sql
@@ -194,6 +194,7 @@ create table wvp_platform ( @@ -194,6 +194,7 @@ create table wvp_platform (
194 create_time character varying(50), 194 create_time character varying(50),
195 update_time character varying(50), 195 update_time character varying(50),
196 as_message_channel bool default false, 196 as_message_channel bool default false,
  197 + auto_push_channel bool default false,
197 constraint uk_platform_unique_server_gb_id unique (server_gb_id) 198 constraint uk_platform_unique_server_gb_id unique (server_gb_id)
198 ); 199 );
199 200
@@ -243,6 +244,7 @@ create table wvp_stream_proxy ( @@ -243,6 +244,7 @@ create table wvp_stream_proxy (
243 create_time character varying(50), 244 create_time character varying(50),
244 name character varying(255), 245 name character varying(255),
245 update_time character varying(50), 246 update_time character varying(50),
  247 + stream_key character varying(255),
246 enable_disable_none_reader bool default false, 248 enable_disable_none_reader bool default false,
247 constraint uk_stream_proxy_app_stream unique (app, stream) 249 constraint uk_stream_proxy_app_stream unique (app, stream)
248 ); 250 );
src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
@@ -186,6 +186,9 @@ public class ParentPlatform { @@ -186,6 +186,9 @@ public class ParentPlatform {
186 @Schema(description = "是否作为消息通道") 186 @Schema(description = "是否作为消息通道")
187 private boolean asMessageChannel; 187 private boolean asMessageChannel;
188 188
  189 + @Schema(description = "是否作为消息通道")
  190 + private boolean autoPushChannel;
  191 +
189 public Integer getId() { 192 public Integer getId() {
190 return id; 193 return id;
191 } 194 }
@@ -425,4 +428,12 @@ public class ParentPlatform { @@ -425,4 +428,12 @@ public class ParentPlatform {
425 public void setAsMessageChannel(boolean asMessageChannel) { 428 public void setAsMessageChannel(boolean asMessageChannel) {
426 this.asMessageChannel = asMessageChannel; 429 this.asMessageChannel = asMessageChannel;
427 } 430 }
  431 +
  432 + public boolean isAutoPushChannel() {
  433 + return autoPushChannel;
  434 + }
  435 +
  436 + public void setAutoPushChannel(boolean autoPushChannel) {
  437 + this.autoPushChannel = autoPushChannel;
  438 + }
428 } 439 }
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java
@@ -32,11 +32,13 @@ public class SubscribeHolder { @@ -32,11 +32,13 @@ public class SubscribeHolder {
32 32
33 public void putCatalogSubscribe(String platformId, SubscribeInfo subscribeInfo) { 33 public void putCatalogSubscribe(String platformId, SubscribeInfo subscribeInfo) {
34 catalogMap.put(platformId, subscribeInfo); 34 catalogMap.put(platformId, subscribeInfo);
35 - // 添加订阅到期  
36 - String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId;  
37 - // 添加任务处理订阅过期  
38 - dynamicTask.startDelay(taskOverdueKey, () -> removeCatalogSubscribe(subscribeInfo.getId()),  
39 - subscribeInfo.getExpires() * 1000); 35 + if (subscribeInfo.getExpires() > 0) {
  36 + // 添加订阅到期
  37 + String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId;
  38 + // 添加任务处理订阅过期
  39 + dynamicTask.startDelay(taskOverdueKey, () -> removeCatalogSubscribe(subscribeInfo.getId()),
  40 + subscribeInfo.getExpires() * 1000);
  41 + }
40 } 42 }
41 43
42 public SubscribeInfo getCatalogSubscribe(String platformId) { 44 public SubscribeInfo getCatalogSubscribe(String platformId) {
@@ -63,11 +65,13 @@ public class SubscribeHolder { @@ -63,11 +65,13 @@ public class SubscribeHolder {
63 dynamicTask.startCron(key, new MobilePositionSubscribeHandlerTask(platformId), 65 dynamicTask.startCron(key, new MobilePositionSubscribeHandlerTask(platformId),
64 subscribeInfo.getGpsInterval() * 1000); 66 subscribeInfo.getGpsInterval() * 1000);
65 String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId; 67 String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId;
66 - // 添加任务处理订阅过期  
67 - dynamicTask.startDelay(taskOverdueKey, () -> {  
68 - removeMobilePositionSubscribe(subscribeInfo.getId());  
69 - },  
70 - subscribeInfo.getExpires() * 1000); 68 + if (subscribeInfo.getExpires() > 0) {
  69 + // 添加任务处理订阅过期
  70 + dynamicTask.startDelay(taskOverdueKey, () -> {
  71 + removeMobilePositionSubscribe(subscribeInfo.getId());
  72 + },
  73 + subscribeInfo.getExpires() * 1000);
  74 + }
71 } 75 }
72 76
73 public SubscribeInfo getMobilePositionSubscribe(String platformId) { 77 public SubscribeInfo getMobilePositionSubscribe(String platformId) {
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeInfo.java
@@ -18,6 +18,9 @@ public class SubscribeInfo { @@ -18,6 +18,9 @@ public class SubscribeInfo {
18 18
19 } 19 }
20 20
  21 + public SubscribeInfo() {
  22 + }
  23 +
21 private String id; 24 private String id;
22 25
23 private SIPRequest request; 26 private SIPRequest request;
@@ -33,6 +36,21 @@ public class SubscribeInfo { @@ -33,6 +36,21 @@ public class SubscribeInfo {
33 private String sn; 36 private String sn;
34 private int gpsInterval; 37 private int gpsInterval;
35 38
  39 + /**
  40 + * 模拟的FromTag
  41 + */
  42 + private String simulatedFromTag;
  43 +
  44 + /**
  45 + * 模拟的ToTag
  46 + */
  47 + private String simulatedToTag;
  48 +
  49 + /**
  50 + * 模拟的CallID
  51 + */
  52 + private String simulatedCallId;
  53 +
36 public String getId() { 54 public String getId() {
37 return id; 55 return id;
38 } 56 }
@@ -96,4 +114,28 @@ public class SubscribeInfo { @@ -96,4 +114,28 @@ public class SubscribeInfo {
96 public void setGpsInterval(int gpsInterval) { 114 public void setGpsInterval(int gpsInterval) {
97 this.gpsInterval = gpsInterval; 115 this.gpsInterval = gpsInterval;
98 } 116 }
  117 +
  118 + public String getSimulatedFromTag() {
  119 + return simulatedFromTag;
  120 + }
  121 +
  122 + public void setSimulatedFromTag(String simulatedFromTag) {
  123 + this.simulatedFromTag = simulatedFromTag;
  124 + }
  125 +
  126 + public String getSimulatedCallId() {
  127 + return simulatedCallId;
  128 + }
  129 +
  130 + public void setSimulatedCallId(String simulatedCallId) {
  131 + this.simulatedCallId = simulatedCallId;
  132 + }
  133 +
  134 + public String getSimulatedToTag() {
  135 + return simulatedToTag;
  136 + }
  137 +
  138 + public void setSimulatedToTag(String simulatedToTag) {
  139 + this.simulatedToTag = simulatedToTag;
  140 + }
99 } 141 }
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
@@ -93,7 +93,10 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; { @@ -93,7 +93,10 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; {
93 } 93 }
94 if (event.getGbStreams() != null && event.getGbStreams().size() > 0){ 94 if (event.getGbStreams() != null && event.getGbStreams().size() > 0){
95 for (GbStream gbStream : event.getGbStreams()) { 95 for (GbStream gbStream : event.getGbStreams()) {
96 - if (gbStream.getStreamType().equals("push") && !userSetting.isUsePushingAsStatus()) { 96 + if (gbStream != null
  97 + && gbStream.getStreamType() != null
  98 + && gbStream.getStreamType().equals("push")
  99 + && !userSetting.isUsePushingAsStatus()) {
97 continue; 100 continue;
98 } 101 }
99 DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform); 102 DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform);
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
@@ -228,11 +228,11 @@ public class SIPRequestHeaderPlarformProvider { @@ -228,11 +228,11 @@ public class SIPRequestHeaderPlarformProvider {
228 SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), 228 SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(),
229 parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort()); 229 parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort());
230 Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); 230 Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
231 - FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, subscribeInfo.getResponse().getToTag()); 231 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, subscribeInfo.getResponse() != null ? subscribeInfo.getResponse().getToTag(): subscribeInfo.getSimulatedToTag());
232 // to 232 // to
233 SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain()); 233 SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain());
234 Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); 234 Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
235 - ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, subscribeInfo.getRequest().getFromTag()); 235 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, subscribeInfo.getRequest() != null ?subscribeInfo.getRequest().getFromTag(): subscribeInfo.getSimulatedFromTag());
236 236
237 // Forwards 237 // Forwards
238 MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); 238 MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
@@ -242,7 +242,7 @@ public class SIPRequestHeaderPlarformProvider { @@ -242,7 +242,7 @@ public class SIPRequestHeaderPlarformProvider {
242 // 设置编码, 防止中文乱码 242 // 设置编码, 防止中文乱码
243 messageFactory.setDefaultContentEncodingCharset("gb2312"); 243 messageFactory.setDefaultContentEncodingCharset("gb2312");
244 244
245 - CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(subscribeInfo.getRequest().getCallIdHeader().getCallId()); 245 + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(subscribeInfo.getRequest() != null ? subscribeInfo.getRequest().getCallIdHeader().getCallId(): subscribeInfo.getSimulatedCallId());
246 246
247 request = (SIPRequest) messageFactory.createRequest(requestURI, Request.NOTIFY, callIdHeader, cSeqHeader, fromHeader, 247 request = (SIPRequest) messageFactory.createRequest(requestURI, Request.NOTIFY, callIdHeader, cSeqHeader, fromHeader,
248 toHeader, viaHeaders, maxForwards); 248 toHeader, viaHeaders, maxForwards);
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -169,13 +169,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -169,13 +169,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
169 169
170 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); 170 CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
171 171
172 - Request request = headerProviderPlatformProvider.createMessageRequest(  
173 - parentPlatform,  
174 - keepaliveXml.toString(),  
175 - SipUtils.getNewFromTag(),  
176 - SipUtils.getNewViaTag(),  
177 - callIdHeader);  
178 - sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, errorEvent, okEvent); 172 + Request request = headerProviderPlatformProvider.createMessageRequest(
  173 + parentPlatform,
  174 + keepaliveXml.toString(),
  175 + SipUtils.getNewFromTag(),
  176 + SipUtils.getNewViaTag(),
  177 + callIdHeader);
  178 + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, errorEvent, okEvent);
179 return callIdHeader.getCallId(); 179 return callIdHeader.getCallId();
180 } 180 }
181 181
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request; @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request;
3 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; 3 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
4 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; 4 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
5 import com.genersoft.iot.vmp.gb28181.utils.SipUtils; 5 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  6 +import com.google.common.primitives.Bytes;
6 import gov.nist.javax.sip.message.SIPRequest; 7 import gov.nist.javax.sip.message.SIPRequest;
7 import gov.nist.javax.sip.message.SIPResponse; 8 import gov.nist.javax.sip.message.SIPResponse;
8 import org.apache.commons.lang3.ArrayUtils; 9 import org.apache.commons.lang3.ArrayUtils;
@@ -203,15 +204,14 @@ public abstract class SIPRequestProcessorParent { @@ -203,15 +204,14 @@ public abstract class SIPRequestProcessorParent {
203 result.add(rawContent[i]); 204 result.add(rawContent[i]);
204 } 205 }
205 } 206 }
206 - Byte[] bytes = new Byte[0];  
207 - byte[] bytesResult = ArrayUtils.toPrimitive(result.toArray(bytes)); 207 + byte[] bytesResult = Bytes.toArray(result);
208 208
209 Document xml; 209 Document xml;
210 try { 210 try {
211 xml = reader.read(new ByteArrayInputStream(bytesResult)); 211 xml = reader.read(new ByteArrayInputStream(bytesResult));
212 }catch (DocumentException e) { 212 }catch (DocumentException e) {
213 - logger.warn("[xml解析异常]: 愿文如下: \r\n{}", new String(bytesResult));  
214 - logger.warn("[xml解析异常]: 愿文如下: 尝试兼容性处理"); 213 + logger.warn("[xml解析异常]: 原文如下: \r\n{}", new String(bytesResult));
  214 + logger.warn("[xml解析异常]: 原文如下: 尝试兼容性处理");
215 String[] xmlLineArray = new String(bytesResult).split("\\r?\\n"); 215 String[] xmlLineArray = new String(bytesResult).split("\\r?\\n");
216 216
217 // 兼容海康的address字段带有<破换xml结构导致无法解析xml的问题 217 // 兼容海康的address字段带有<破换xml结构导致无法解析xml的问题
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
@@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor @@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor
13 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; 13 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
14 import com.genersoft.iot.vmp.gb28181.utils.SipUtils; 14 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
15 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; 15 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
  16 +import com.genersoft.iot.vmp.service.IPlatformService;
16 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 17 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
17 import gov.nist.javax.sip.message.SIPRequest; 18 import gov.nist.javax.sip.message.SIPRequest;
18 import gov.nist.javax.sip.message.SIPResponse; 19 import gov.nist.javax.sip.message.SIPResponse;
@@ -53,6 +54,10 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme @@ -53,6 +54,10 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
53 @Autowired 54 @Autowired
54 private SIPSender sipSender; 55 private SIPSender sipSender;
55 56
  57 +
  58 + @Autowired
  59 + private IPlatformService platformService;
  60 +
56 @Override 61 @Override
57 public void afterPropertiesSet() throws Exception { 62 public void afterPropertiesSet() throws Exception {
58 // 添加消息处理的订阅 63 // 添加消息处理的订阅
@@ -194,5 +199,8 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme @@ -194,5 +199,8 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
194 } catch (SipException | InvalidArgumentException | ParseException e) { 199 } catch (SipException | InvalidArgumentException | ParseException e) {
195 logger.error("未处理的异常 ", e); 200 logger.error("未处理的异常 ", e);
196 } 201 }
  202 + if (subscribeHolder.getCatalogSubscribe(platformId) == null && platform.isAutoPushChannel()) {
  203 + platformService.addSimulatedSubscribeInfo(platform);
  204 + }
197 } 205 }
198 } 206 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
@@ -78,7 +78,6 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent @@ -78,7 +78,6 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
78 } 78 }
79 taskExecutor.execute(()->{ 79 taskExecutor.execute(()->{
80 try { 80 try {
81 -  
82 String sn = getText(rootElement, "SN"); 81 String sn = getText(rootElement, "SN");
83 String channelId = getText(rootElement, "DeviceID"); 82 String channelId = getText(rootElement, "DeviceID");
84 RecordInfo recordInfo = new RecordInfo(); 83 RecordInfo recordInfo = new RecordInfo();
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -206,6 +206,13 @@ public class ZLMHttpHookListener { @@ -206,6 +206,13 @@ public class ZLMHttpHookListener {
206 } 206 }
207 // 推流鉴权的处理 207 // 推流鉴权的处理
208 if (!"rtp".equals(param.getApp())) { 208 if (!"rtp".equals(param.getApp())) {
  209 + StreamProxyItem stream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
  210 + if (stream != null) {
  211 + HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
  212 + result.setEnable_audio(stream.isEnableAudio());
  213 + result.setEnable_mp4(stream.isEnableMp4());
  214 + return result;
  215 + }
209 if (userSetting.getPushAuthority()) { 216 if (userSetting.getPushAuthority()) {
210 // 推流鉴权 217 // 推流鉴权
211 if (param.getParams() == null) { 218 if (param.getParams() == null) {
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -32,13 +32,20 @@ public class ZLMRESTfulUtils { @@ -32,13 +32,20 @@ public class ZLMRESTfulUtils {
32 } 32 }
33 33
34 private OkHttpClient getClient(){ 34 private OkHttpClient getClient(){
  35 + return getClient(null);
  36 + }
  37 +
  38 + private OkHttpClient getClient(Integer readTimeOut){
35 if (client == null) { 39 if (client == null) {
  40 + if (readTimeOut == null) {
  41 + readTimeOut = 10;
  42 + }
36 OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); 43 OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
37 //todo 暂时写死超时时间 均为5s 44 //todo 暂时写死超时时间 均为5s
38 // 设置连接超时时间 45 // 设置连接超时时间
39 - httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS); 46 + httpClientBuilder.connectTimeout(8,TimeUnit.SECONDS);
40 // 设置读取超时时间 47 // 设置读取超时时间
41 - httpClientBuilder.readTimeout(10,TimeUnit.SECONDS); 48 + httpClientBuilder.readTimeout(readTimeOut,TimeUnit.SECONDS);
42 // 设置连接池 49 // 设置连接池
43 httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES)); 50 httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES));
44 if (logger.isDebugEnabled()) { 51 if (logger.isDebugEnabled()) {
@@ -55,9 +62,13 @@ public class ZLMRESTfulUtils { @@ -55,9 +62,13 @@ public class ZLMRESTfulUtils {
55 62
56 } 63 }
57 64
58 -  
59 public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) { 65 public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
60 - OkHttpClient client = getClient(); 66 + return sendPost(mediaServerItem, api, param, callback, null);
  67 + }
  68 +
  69 +
  70 + public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback, Integer readTimeOut) {
  71 + OkHttpClient client = getClient(readTimeOut);
61 72
62 if (mediaServerItem == null) { 73 if (mediaServerItem == null) {
63 return null; 74 return null;
@@ -264,6 +275,12 @@ public class ZLMRESTfulUtils { @@ -264,6 +275,12 @@ public class ZLMRESTfulUtils {
264 return sendPost(mediaServerItem, "delFFmpegSource",param, null); 275 return sendPost(mediaServerItem, "delFFmpegSource",param, null);
265 } 276 }
266 277
  278 + public JSONObject delStreamProxy(MediaServerItem mediaServerItem, String key){
  279 + Map<String, Object> param = new HashMap<>();
  280 + param.put("key", key);
  281 + return sendPost(mediaServerItem, "delStreamProxy",param, null);
  282 + }
  283 +
267 public JSONObject getMediaServerConfig(MediaServerItem mediaServerItem){ 284 public JSONObject getMediaServerConfig(MediaServerItem mediaServerItem){
268 return sendPost(mediaServerItem, "getServerConfig",null, null); 285 return sendPost(mediaServerItem, "getServerConfig",null, null);
269 } 286 }
@@ -317,7 +334,7 @@ public class ZLMRESTfulUtils { @@ -317,7 +334,7 @@ public class ZLMRESTfulUtils {
317 param.put("enable_mp4", enable_mp4?1:0); 334 param.put("enable_mp4", enable_mp4?1:0);
318 param.put("enable_audio", enable_audio?1:0); 335 param.put("enable_audio", enable_audio?1:0);
319 param.put("rtp_type", rtp_type); 336 param.put("rtp_type", rtp_type);
320 - return sendPost(mediaServerItem, "addStreamProxy",param, null); 337 + return sendPost(mediaServerItem, "addStreamProxy",param, null, 20);
321 } 338 }
322 339
323 public JSONObject closeStreams(MediaServerItem mediaServerItem, String app, String stream) { 340 public JSONObject closeStreams(MediaServerItem mediaServerItem, String app, String stream) {
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java
@@ -41,6 +41,9 @@ public class StreamProxyItem extends GbStream { @@ -41,6 +41,9 @@ public class StreamProxyItem extends GbStream {
41 @Schema(description = "是否 无人观看时自动停用") 41 @Schema(description = "是否 无人观看时自动停用")
42 private boolean enableDisableNoneReader; 42 private boolean enableDisableNoneReader;
43 43
  44 + @Schema(description = "拉流代理时zlm返回的key,用于停止拉流代理")
  45 + private String streamKey;
  46 +
44 public String getType() { 47 public String getType() {
45 return type; 48 return type;
46 } 49 }
@@ -167,5 +170,11 @@ public class StreamProxyItem extends GbStream { @@ -167,5 +170,11 @@ public class StreamProxyItem extends GbStream {
167 this.enableAudio = enable_audio; 170 this.enableAudio = enable_audio;
168 } 171 }
169 172
  173 + public String getStreamKey() {
  174 + return streamKey;
  175 + }
170 176
  177 + public void setStreamKey(String streamKey) {
  178 + this.streamKey = streamKey;
  179 + }
171 } 180 }
src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java
@@ -69,4 +69,6 @@ public interface IGbStreamService { @@ -69,4 +69,6 @@ public interface IGbStreamService {
69 * @param catalogId 69 * @param catalogId
70 */ 70 */
71 void delAllPlatformInfo(String platformId, String catalogId); 71 void delAllPlatformInfo(String platformId, String catalogId);
  72 +
  73 + List<GbStream> getGbChannelWithGbid(String gbId);
72 } 74 }
src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java
@@ -80,4 +80,6 @@ public interface IPlatformService { @@ -80,4 +80,6 @@ public interface IPlatformService {
80 * 语音喊话回复BYE 80 * 语音喊话回复BYE
81 */ 81 */
82 void stopBroadcast(ParentPlatform platform, DeviceChannel channel, String stream,boolean sendBye, MediaServerItem mediaServerItem); 82 void stopBroadcast(ParentPlatform platform, DeviceChannel channel, String stream,boolean sendBye, MediaServerItem mediaServerItem);
  83 +
  84 + void addSimulatedSubscribeInfo(ParentPlatform parentPlatform);
83 } 85 }
src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
@@ -18,6 +18,7 @@ import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -18,6 +18,7 @@ import org.springframework.jdbc.datasource.DataSourceTransactionManager;
18 import org.springframework.stereotype.Service; 18 import org.springframework.stereotype.Service;
19 import org.springframework.transaction.TransactionDefinition; 19 import org.springframework.transaction.TransactionDefinition;
20 import org.springframework.transaction.TransactionStatus; 20 import org.springframework.transaction.TransactionStatus;
  21 +import org.springframework.transaction.annotation.Transactional;
21 import org.springframework.util.ObjectUtils; 22 import org.springframework.util.ObjectUtils;
22 23
23 import java.util.ArrayList; 24 import java.util.ArrayList;
@@ -263,4 +264,9 @@ public class GbStreamServiceImpl implements IGbStreamService { @@ -263,4 +264,9 @@ public class GbStreamServiceImpl implements IGbStreamService {
263 eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); 264 eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL);
264 } 265 }
265 } 266 }
  267 +
  268 + @Override
  269 + public List<GbStream> getGbChannelWithGbid(String gbId) {
  270 + return gbStreamMapper.selectByGBId(gbId);
  271 + }
266 } 272 }
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
@@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; @@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
13 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; 13 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
14 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; 14 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
15 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; 15 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
  16 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
16 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory; 17 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
17 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 18 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
18 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; 19 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
@@ -28,6 +29,7 @@ import com.genersoft.iot.vmp.storager.dao.*; @@ -28,6 +29,7 @@ import com.genersoft.iot.vmp.storager.dao.*;
28 import com.genersoft.iot.vmp.utils.DateUtil; 29 import com.genersoft.iot.vmp.utils.DateUtil;
29 import com.github.pagehelper.PageHelper; 30 import com.github.pagehelper.PageHelper;
30 import com.github.pagehelper.PageInfo; 31 import com.github.pagehelper.PageInfo;
  32 +import gov.nist.javax.sip.message.SIPRequest;
31 import org.slf4j.Logger; 33 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory; 34 import org.slf4j.LoggerFactory;
33 import org.springframework.beans.factory.annotation.Autowired; 35 import org.springframework.beans.factory.annotation.Autowired;
@@ -35,12 +37,19 @@ import org.springframework.stereotype.Service; @@ -35,12 +37,19 @@ import org.springframework.stereotype.Service;
35 37
36 import javax.sip.InvalidArgumentException; 38 import javax.sip.InvalidArgumentException;
37 import javax.sip.ResponseEvent; 39 import javax.sip.ResponseEvent;
  40 +import javax.sip.PeerUnavailableException;
38 import javax.sip.SipException; 41 import javax.sip.SipException;
  42 +import javax.sip.SipFactory;
  43 +import javax.sip.address.Address;
  44 +import javax.sip.address.SipURI;
  45 +import javax.sip.header.*;
  46 +import javax.sip.message.Request;
39 import java.text.ParseException; 47 import java.text.ParseException;
40 import java.util.HashMap; 48 import java.util.HashMap;
41 import java.util.List; 49 import java.util.List;
42 import java.util.Map; 50 import java.util.Map;
43 import java.util.UUID; 51 import java.util.UUID;
  52 +import java.util.*;
44 53
45 /** 54 /**
46 * @author lin 55 * @author lin
@@ -199,6 +208,7 @@ public class PlatformServiceImpl implements IPlatformService { @@ -199,6 +208,7 @@ public class PlatformServiceImpl implements IPlatformService {
199 } 208 }
200 } 209 }
201 210
  211 +
202 return false; 212 return false;
203 } 213 }
204 214
@@ -243,19 +253,20 @@ public class PlatformServiceImpl implements IPlatformService { @@ -243,19 +253,20 @@ public class PlatformServiceImpl implements IPlatformService {
243 try { 253 try {
244 commanderForPlatform.keepalive(parentPlatform, eventResult -> { 254 commanderForPlatform.keepalive(parentPlatform, eventResult -> {
245 // 心跳失败 255 // 心跳失败
246 - if (eventResult.type == SipSubscribe.EventResultType.timeout) {  
247 - // 心跳超时  
248 - ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());  
249 - // 此时是第三次心跳超时, 平台离线  
250 - if (platformCatch.getKeepAliveReply() == 2) {  
251 - // 设置平台离线,并重新注册  
252 - logger.info("[国标级联] 三次心跳超时, 平台{}({})离线", parentPlatform.getName(), parentPlatform.getServerGBId());  
253 - offline(parentPlatform, false);  
254 - }  
255 -  
256 - }else { 256 + if (eventResult.type != SipSubscribe.EventResultType.timeout) {
257 logger.warn("[国标级联]发送心跳收到错误,code: {}, msg: {}", eventResult.statusCode, eventResult.msg); 257 logger.warn("[国标级联]发送心跳收到错误,code: {}, msg: {}", eventResult.statusCode, eventResult.msg);
258 } 258 }
  259 + // 心跳失败
  260 + ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
  261 + // 此时是第三次心跳超时, 平台离线
  262 + if (platformCatch.getKeepAliveReply() == 2) {
  263 + // 设置平台离线,并重新注册
  264 + logger.info("[国标级联] 三次心跳失败, 平台{}({})离线", parentPlatform.getName(), parentPlatform.getServerGBId());
  265 + offline(parentPlatform, false);
  266 + }else {
  267 + platformCatch.setKeepAliveReply(platformCatch.getKeepAliveReply() + 1);
  268 + redisCatchStorage.updatePlatformCatchInfo(platformCatch);
  269 + }
259 270
260 }, eventResult -> { 271 }, eventResult -> {
261 // 心跳成功 272 // 心跳成功
@@ -273,6 +284,31 @@ public class PlatformServiceImpl implements IPlatformService { @@ -273,6 +284,31 @@ public class PlatformServiceImpl implements IPlatformService {
273 }, 284 },
274 (parentPlatform.getKeepTimeout())*1000); 285 (parentPlatform.getKeepTimeout())*1000);
275 } 286 }
  287 + if (parentPlatform.isAutoPushChannel()) {
  288 + if (subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()) == null) {
  289 + addSimulatedSubscribeInfo(parentPlatform);
  290 + }
  291 + }else {
  292 + SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId());
  293 + if (catalogSubscribe != null && catalogSubscribe.getExpires() == -1) {
  294 + subscribeHolder.removeCatalogSubscribe(parentPlatform.getServerGBId());
  295 + }
  296 + }
  297 + }
  298 +
  299 + @Override
  300 + public void addSimulatedSubscribeInfo(ParentPlatform parentPlatform) {
  301 + // 自动添加一条模拟的订阅信息
  302 + SubscribeInfo subscribeInfo = new SubscribeInfo();
  303 + subscribeInfo.setId(parentPlatform.getServerGBId());
  304 + subscribeInfo.setExpires(-1);
  305 + subscribeInfo.setEventType("Catalog");
  306 + int random = (int) Math.floor(Math.random() * 10000);
  307 + subscribeInfo.setEventId(random + "");
  308 + subscribeInfo.setSimulatedCallId(UUID.randomUUID().toString().replace("-", "") + "@" + parentPlatform.getServerIP());
  309 + subscribeInfo.setSimulatedFromTag(UUID.randomUUID().toString().replace("-", ""));
  310 + subscribeInfo.setSimulatedToTag(UUID.randomUUID().toString().replace("-", ""));
  311 + subscribeHolder.putCatalogSubscribe(parentPlatform.getServerGBId(), subscribeInfo);
276 } 312 }
277 313
278 private void registerTask(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo){ 314 private void registerTask(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo){
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
@@ -10,7 +10,10 @@ import com.genersoft.iot.vmp.conf.exception.ControllerException; @@ -10,7 +10,10 @@ import com.genersoft.iot.vmp.conf.exception.ControllerException;
10 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 10 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
11 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; 11 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
12 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; 12 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
  13 +import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
13 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; 14 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
  15 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
  16 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
14 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 17 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
15 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; 18 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
16 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; 19 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
@@ -60,6 +63,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -60,6 +63,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
60 private ZLMRESTfulUtils zlmresTfulUtils; 63 private ZLMRESTfulUtils zlmresTfulUtils;
61 64
62 @Autowired 65 @Autowired
  66 + private ZLMServerFactory zlmServerFactory;
  67 +
  68 + @Autowired
63 private StreamProxyMapper streamProxyMapper; 69 private StreamProxyMapper streamProxyMapper;
64 70
65 @Autowired 71 @Autowired
@@ -137,7 +143,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -137,7 +143,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
137 dstUrl = String.format("%s://%s:%s/%s/%s", schemaForUri, "127.0.0.1", port, param.getApp(), 143 dstUrl = String.format("%s://%s:%s/%s/%s", schemaForUri, "127.0.0.1", port, param.getApp(),
138 param.getStream()); 144 param.getStream());
139 }else { 145 }else {
140 - dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(), 146 + dstUrl = String.format("rtsp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtspPort(), param.getApp(),
141 param.getStream()); 147 param.getStream());
142 } 148 }
143 param.setDstUrl(dstUrl); 149 param.setDstUrl(dstUrl);
@@ -154,15 +160,14 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -154,15 +160,14 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
154 callback.run(ErrorCode.ERROR100.getCode(), "保存失败", null); 160 callback.run(ErrorCode.ERROR100.getCode(), "保存失败", null);
155 return; 161 return;
156 } 162 }
157 - 163 + HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed(param.getApp(), param.getStream(), true, "rtsp", mediaInfo.getId());
  164 + hookSubscribe.addSubscribe(hookSubscribeForStreamChange, (mediaServerItem, response) -> {
  165 + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
  166 + mediaInfo, param.getApp(), param.getStream(), null, null);
  167 + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
  168 + });
158 if (param.isEnable()) { 169 if (param.isEnable()) {
159 String talkKey = UUID.randomUUID().toString(); 170 String talkKey = UUID.randomUUID().toString();
160 - dynamicTask.startCron(talkKey, ()->{  
161 - StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(param.getApp(), param.getStream(), mediaInfo.getId(), false);  
162 - if (streamInfo != null) {  
163 - callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);  
164 - }  
165 - }, 1000);  
166 String delayTalkKey = UUID.randomUUID().toString(); 171 String delayTalkKey = UUID.randomUUID().toString();
167 dynamicTask.startDelay(delayTalkKey, ()->{ 172 dynamicTask.startDelay(delayTalkKey, ()->{
168 StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(param.getApp(), param.getStream(), mediaInfo.getId(), false); 173 StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(param.getApp(), param.getStream(), mediaInfo.getId(), false);
@@ -172,9 +177,10 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -172,9 +177,10 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
172 dynamicTask.stop(talkKey); 177 dynamicTask.stop(talkKey);
173 callback.run(ErrorCode.ERROR100.getCode(), "超时", null); 178 callback.run(ErrorCode.ERROR100.getCode(), "超时", null);
174 } 179 }
175 - }, 5000); 180 + }, 7000);
176 JSONObject jsonObject = addStreamProxyToZlm(param); 181 JSONObject jsonObject = addStreamProxyToZlm(param);
177 if (jsonObject != null && jsonObject.getInteger("code") == 0) { 182 if (jsonObject != null && jsonObject.getInteger("code") == 0) {
  183 + hookSubscribe.removeSubscribe(hookSubscribeForStreamChange);
178 dynamicTask.stop(talkKey); 184 dynamicTask.stop(talkKey);
179 StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream( 185 StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
180 mediaInfo, param.getApp(), param.getStream(), null, null); 186 mediaInfo, param.getApp(), param.getStream(), null, null);
@@ -304,13 +310,32 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -304,13 +310,32 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
304 if (mediaServerItem == null) { 310 if (mediaServerItem == null) {
305 return null; 311 return null;
306 } 312 }
307 - if ("default".equals(param.getType())){  
308 - result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl().trim(),  
309 - param.isEnableAudio(), param.isEnableMp4(), param.getRtpType());  
310 - }else if ("ffmpeg".equals(param.getType())) { 313 + if (zlmServerFactory.isStreamReady(mediaServerItem, param.getApp(), param.getStream())) {
  314 + zlmresTfulUtils.closeStreams(mediaServerItem, param.getApp(), param.getStream());
  315 + }
  316 + if ("ffmpeg".equalsIgnoreCase(param.getType())){
311 result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrcUrl().trim(), param.getDstUrl(), 317 result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrcUrl().trim(), param.getDstUrl(),
312 param.getTimeoutMs() + "", param.isEnableAudio(), param.isEnableMp4(), 318 param.getTimeoutMs() + "", param.isEnableAudio(), param.isEnableMp4(),
313 param.getFfmpegCmdKey()); 319 param.getFfmpegCmdKey());
  320 + }else {
  321 + result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl().trim(),
  322 + param.isEnableAudio(), param.isEnableMp4(), param.getRtpType());
  323 + }
  324 + System.out.println("addStreamProxyToZlm====");
  325 + System.out.println(result);
  326 + if (result != null && result.getInteger("code") == 0) {
  327 + JSONObject data = result.getJSONObject("data");
  328 + if (data == null) {
  329 + logger.warn("[获取拉流代理的结果数据Data] 失败: {}", result );
  330 + return result;
  331 + }
  332 + String key = data.getString("key");
  333 + if (key == null) {
  334 + logger.warn("[获取拉流代理的结果数据Data中的KEY] 失败: {}", result );
  335 + return result;
  336 + }
  337 + param.setStreamKey(key);
  338 + streamProxyMapper.update(param);
314 } 339 }
315 return result; 340 return result;
316 } 341 }
@@ -321,7 +346,12 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -321,7 +346,12 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
321 return null; 346 return null;
322 } 347 }
323 MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId()); 348 MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
324 - JSONObject result = zlmresTfulUtils.closeStreams(mediaServerItem, param.getApp(), param.getStream()); 349 + JSONObject result = null;
  350 + if ("ffmpeg".equalsIgnoreCase(param.getType())){
  351 + result = zlmresTfulUtils.delFFmpegSource(mediaServerItem, param.getStreamKey());
  352 + }else {
  353 + result = zlmresTfulUtils.delStreamProxy(mediaServerItem, param.getStreamKey());
  354 + }
325 return result; 355 return result;
326 } 356 }
327 357
@@ -335,18 +365,19 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -335,18 +365,19 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
335 StreamProxyItem streamProxyItem = videoManagerStorager.queryStreamProxy(app, stream); 365 StreamProxyItem streamProxyItem = videoManagerStorager.queryStreamProxy(app, stream);
336 if (streamProxyItem != null) { 366 if (streamProxyItem != null) {
337 gbStreamService.sendCatalogMsg(streamProxyItem, CatalogEvent.DEL); 367 gbStreamService.sendCatalogMsg(streamProxyItem, CatalogEvent.DEL);
  368 +
  369 + // 如果关联了国标那么移除关联
  370 + platformGbStreamMapper.delByAppAndStream(app, stream);
  371 + gbStreamMapper.del(app, stream);
338 videoManagerStorager.deleteStreamProxy(app, stream); 372 videoManagerStorager.deleteStreamProxy(app, stream);
  373 + redisCatchStorage.removeStream(streamProxyItem.getMediaServerId(), "PULL", app, stream);
339 JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem); 374 JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem);
340 if (jsonObject != null && jsonObject.getInteger("code") == 0) { 375 if (jsonObject != null && jsonObject.getInteger("code") == 0) {
341 - // 如果关联了国标那么移除关联  
342 - gbStreamMapper.del(app, stream);  
343 - platformGbStreamMapper.delByAppAndStream(app, stream);  
344 - // TODO 如果关联的推流, 那么状态设置为离线 376 + logger.info("[移除代理]: 代理: {}/{}, 从zlm移除成功", app, stream);
  377 + }else {
  378 + logger.info("[移除代理]: 代理: {}/{}, 从zlm移除失败", app, stream);
345 } 379 }
346 - redisCatchStorage.removeStream(streamProxyItem.getMediaServerId(), "PULL", app, stream);  
347 } 380 }
348 -  
349 -  
350 } 381 }
351 382
352 @Override 383 @Override
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
@@ -440,7 +440,7 @@ public class StreamPushServiceImpl implements IStreamPushService { @@ -440,7 +440,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
440 440
441 } 441 }
442 } 442 }
443 - if (streamPushItemListFroPlatform.size() > 0) { 443 + if (!streamPushItemListFroPlatform.isEmpty()) {
444 platformGbStreamMapper.batchAdd(streamPushItemListFroPlatform); 444 platformGbStreamMapper.batchAdd(streamPushItemListFroPlatform);
445 // 发送通知 445 // 发送通知
446 for (String platformId : platformForEvent.keySet()) { 446 for (String platformId : platformForEvent.keySet()) {
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
@@ -167,8 +167,8 @@ public interface DeviceChannelMapper { @@ -167,8 +167,8 @@ public interface DeviceChannelMapper {
167 " <if test='query != null'> AND (dc.channel_id LIKE concat('%',#{query},'%') OR dc.name LIKE concat('%',#{query},'%') OR dc.name LIKE concat('%',#{query},'%'))</if> " + 167 " <if test='query != null'> AND (dc.channel_id LIKE concat('%',#{query},'%') OR dc.name LIKE concat('%',#{query},'%') OR dc.name LIKE concat('%',#{query},'%'))</if> " +
168 " <if test='online == true' > AND dc.status=true</if> " + 168 " <if test='online == true' > AND dc.status=true</if> " +
169 " <if test='online == false' > AND dc.status=false</if> " + 169 " <if test='online == false' > AND dc.status=false</if> " +
170 - " <if test='hasSubChannel!= null and has_sub_channel == true' > AND dc.sub_count > 0</if> " +  
171 - " <if test='hasSubChannel!= null and has_sub_channel == false' > AND dc.sub_count = 0</if> " + 170 + " <if test='hasSubChannel!= null and hasSubChannel == true' > AND dc.sub_count > 0</if> " +
  171 + " <if test='hasSubChannel!= null and hasSubChannel == false' > AND dc.sub_count = 0</if> " +
172 " <if test='catalogId == null ' > AND dc.id not in (select device_channel_id from wvp_platform_gb_channel where platform_id=#{platformId} ) </if> " + 172 " <if test='catalogId == null ' > AND dc.id not in (select device_channel_id from wvp_platform_gb_channel where platform_id=#{platformId} ) </if> " +
173 " <if test='catalogId != null ' > AND pgc.platform_id = #{platformId} and pgc.catalog_id=#{catalogId} </if> " + 173 " <if test='catalogId != null ' > AND pgc.platform_id = #{platformId} and pgc.catalog_id=#{catalogId} </if> " +
174 " ORDER BY dc.device_id, dc.channel_id ASC" + 174 " ORDER BY dc.device_id, dc.channel_id ASC" +
src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java
@@ -16,10 +16,10 @@ import java.util.List; @@ -16,10 +16,10 @@ import java.util.List;
16 public interface ParentPlatformMapper { 16 public interface ParentPlatformMapper {
17 17
18 @Insert("INSERT INTO wvp_platform (enable, name, server_gb_id, server_gb_domain, server_ip, server_port,device_gb_id,device_ip,"+ 18 @Insert("INSERT INTO wvp_platform (enable, name, server_gb_id, server_gb_domain, server_ip, server_port,device_gb_id,device_ip,"+
19 - "device_port,username,password,expires,keep_timeout,transport,character_set,ptz,rtcp,as_message_channel,"+ 19 + "device_port,username,password,expires,keep_timeout,transport,character_set,ptz,rtcp,as_message_channel,auto_push_channel,"+
20 "status,start_offline_push,catalog_id,administrative_division,catalog_group,create_time,update_time) " + 20 "status,start_offline_push,catalog_id,administrative_division,catalog_group,create_time,update_time) " +
21 " VALUES (#{enable}, #{name}, #{serverGBId}, #{serverGBDomain}, #{serverIP}, #{serverPort}, #{deviceGBId}, #{deviceIp}, " + 21 " VALUES (#{enable}, #{name}, #{serverGBId}, #{serverGBDomain}, #{serverIP}, #{serverPort}, #{deviceGBId}, #{deviceIp}, " +
22 - " #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, #{asMessageChannel}, " + 22 + " #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, #{asMessageChannel}, #{autoPushChannel}, " +
23 " #{status}, #{startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime})") 23 " #{status}, #{startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime})")
24 int addParentPlatform(ParentPlatform parentPlatform); 24 int addParentPlatform(ParentPlatform parentPlatform);
25 25
@@ -42,6 +42,7 @@ public interface ParentPlatformMapper { @@ -42,6 +42,7 @@ public interface ParentPlatformMapper {
42 "ptz=#{ptz}, " + 42 "ptz=#{ptz}, " +
43 "rtcp=#{rtcp}, " + 43 "rtcp=#{rtcp}, " +
44 "as_message_channel=#{asMessageChannel}, " + 44 "as_message_channel=#{asMessageChannel}, " +
  45 + "auto_push_channel=#{autoPushChannel}, " +
45 "status=#{status}, " + 46 "status=#{status}, " +
46 "start_offline_push=#{startOfflinePush}, " + 47 "start_offline_push=#{startOfflinePush}, " +
47 "catalog_group=#{catalogGroup}, " + 48 "catalog_group=#{catalogGroup}, " +
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java
@@ -12,9 +12,9 @@ import java.util.List; @@ -12,9 +12,9 @@ import java.util.List;
12 public interface StreamProxyMapper { 12 public interface StreamProxyMapper {
13 13
14 @Insert("INSERT INTO wvp_stream_proxy (type, name, app, stream,media_server_id, url, src_url, dst_url, " + 14 @Insert("INSERT INTO wvp_stream_proxy (type, name, app, stream,media_server_id, url, src_url, dst_url, " +
15 - "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_audio, enable_mp4, enable, status, enable_remove_none_reader, enable_disable_none_reader, create_time) VALUES" + 15 + "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_audio, enable_mp4, enable, status, stream_key, enable_remove_none_reader, enable_disable_none_reader, create_time) VALUES" +
16 "(#{type}, #{name}, #{app}, #{stream}, #{mediaServerId}, #{url}, #{srcUrl}, #{dstUrl}, " + 16 "(#{type}, #{name}, #{app}, #{stream}, #{mediaServerId}, #{url}, #{srcUrl}, #{dstUrl}, " +
17 - "#{timeoutMs}, #{ffmpegCmdKey}, #{rtpType}, #{enableAudio}, #{enableMp4}, #{enable}, #{status}, " + 17 + "#{timeoutMs}, #{ffmpegCmdKey}, #{rtpType}, #{enableAudio}, #{enableMp4}, #{enable}, #{status}, #{streamKey}, " +
18 "#{enableRemoveNoneReader}, #{enableDisableNoneReader}, #{createTime} )") 18 "#{enableRemoveNoneReader}, #{enableDisableNoneReader}, #{createTime} )")
19 int add(StreamProxyItem streamProxyDto); 19 int add(StreamProxyItem streamProxyDto);
20 20
@@ -33,6 +33,7 @@ public interface StreamProxyMapper { @@ -33,6 +33,7 @@ public interface StreamProxyMapper {
33 "enable_audio=#{enableAudio}, " + 33 "enable_audio=#{enableAudio}, " +
34 "enable=#{enable}, " + 34 "enable=#{enable}, " +
35 "status=#{status}, " + 35 "status=#{status}, " +
  36 + "stream_key=#{streamKey}, " +
36 "enable_remove_none_reader=#{enableRemoveNoneReader}, " + 37 "enable_remove_none_reader=#{enableRemoveNoneReader}, " +
37 "enable_disable_none_reader=#{enableDisableNoneReader}, " + 38 "enable_disable_none_reader=#{enableDisableNoneReader}, " +
38 "enable_mp4=#{enableMp4} " + 39 "enable_mp4=#{enableMp4} " +
@@ -45,7 +46,7 @@ public interface StreamProxyMapper { @@ -45,7 +46,7 @@ public interface StreamProxyMapper {
45 @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream order by st.create_time desc") 46 @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream order by st.create_time desc")
46 List<StreamProxyItem> selectAll(); 47 List<StreamProxyItem> selectAll();
47 48
48 - @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable=#{enable} order by st.create_time desc") 49 + @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude, 'proxy' as streamType FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable=#{enable} order by st.create_time desc")
49 List<StreamProxyItem> selectForEnable(boolean enable); 50 List<StreamProxyItem> selectForEnable(boolean enable);
50 51
51 @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.app=#{app} AND st.stream=#{stream} order by st.create_time desc") 52 @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.app=#{app} AND st.stream=#{stream} order by st.create_time desc")
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
@@ -36,6 +36,7 @@ import org.springframework.util.ObjectUtils; @@ -36,6 +36,7 @@ import org.springframework.util.ObjectUtils;
36 import org.springframework.web.bind.annotation.*; 36 import org.springframework.web.bind.annotation.*;
37 import org.springframework.web.context.request.async.DeferredResult; 37 import org.springframework.web.context.request.async.DeferredResult;
38 38
  39 +import javax.servlet.ServletOutputStream;
39 import javax.servlet.http.HttpServletResponse; 40 import javax.servlet.http.HttpServletResponse;
40 import javax.sip.InvalidArgumentException; 41 import javax.sip.InvalidArgumentException;
41 import javax.sip.SipException; 42 import javax.sip.SipException;
@@ -472,7 +473,10 @@ public class DeviceQuery { @@ -472,7 +473,10 @@ public class DeviceQuery {
472 try { 473 try {
473 final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null? ".jpg": ("_" + mark + ".jpg"))).toPath()); 474 final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null? ".jpg": ("_" + mark + ".jpg"))).toPath());
474 resp.setContentType(MediaType.IMAGE_PNG_VALUE); 475 resp.setContentType(MediaType.IMAGE_PNG_VALUE);
  476 + ServletOutputStream outputStream = resp.getOutputStream();
475 IOUtils.copy(in, resp.getOutputStream()); 477 IOUtils.copy(in, resp.getOutputStream());
  478 + in.close();
  479 + outputStream.close();
476 } catch (IOException e) { 480 } catch (IOException e) {
477 resp.setStatus(HttpServletResponse.SC_NOT_FOUND); 481 resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
478 } 482 }
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java
1 package com.genersoft.iot.vmp.vmanager.gb28181.gbStream; 1 package com.genersoft.iot.vmp.vmanager.gb28181.gbStream;
2 2
  3 +import com.genersoft.iot.vmp.conf.exception.ControllerException;
3 import com.genersoft.iot.vmp.gb28181.bean.GbStream; 4 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
  5 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
4 import com.genersoft.iot.vmp.service.IGbStreamService; 6 import com.genersoft.iot.vmp.service.IGbStreamService;
  7 +import com.genersoft.iot.vmp.service.IPlatformService;
5 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 8 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  9 +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
6 import com.genersoft.iot.vmp.vmanager.gb28181.gbStream.bean.GbStreamParam; 10 import com.genersoft.iot.vmp.vmanager.gb28181.gbStream.bean.GbStreamParam;
7 import com.github.pagehelper.PageInfo; 11 import com.github.pagehelper.PageInfo;
8 import io.swagger.v3.oas.annotations.Operation; 12 import io.swagger.v3.oas.annotations.Operation;
@@ -14,6 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -14,6 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired;
14 import org.springframework.util.ObjectUtils; 18 import org.springframework.util.ObjectUtils;
15 import org.springframework.web.bind.annotation.*; 19 import org.springframework.web.bind.annotation.*;
16 20
  21 +import java.util.ArrayList;
17 import java.util.List; 22 import java.util.List;
18 23
19 @Tag(name = "视频流关联到级联平台") 24 @Tag(name = "视频流关联到级联平台")
@@ -28,7 +33,7 @@ public class GbStreamController { @@ -28,7 +33,7 @@ public class GbStreamController {
28 private IGbStreamService gbStreamService; 33 private IGbStreamService gbStreamService;
29 34
30 @Autowired 35 @Autowired
31 - private IVideoManagerStorage storager; 36 + private IPlatformService platformService;
32 37
33 38
34 /** 39 /**
@@ -107,4 +112,20 @@ public class GbStreamController { @@ -107,4 +112,20 @@ public class GbStreamController {
107 gbStreamService.addPlatformInfo(gbStreamParam.getGbStreams(), gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId()); 112 gbStreamService.addPlatformInfo(gbStreamParam.getGbStreams(), gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId());
108 } 113 }
109 } 114 }
  115 +
  116 + /**
  117 + * 保存国标关联
  118 + * @param gbId
  119 + * @return
  120 + */
  121 + @Operation(summary = "保存国标关联")
  122 + @GetMapping(value = "/addWithGbid")
  123 + @ResponseBody
  124 + public void add(String gbId, String platformGbId, @RequestParam(required = false) String catalogGbId){
  125 + List<GbStream> gbStreams = gbStreamService.getGbChannelWithGbid(gbId);
  126 + if (gbStreams.isEmpty()) {
  127 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "gbId的信息未找到");
  128 + }
  129 + gbStreamService.addPlatformInfo(gbStreams, platformGbId, catalogGbId);
  130 + }
110 } 131 }
src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java
@@ -67,6 +67,16 @@ public class StreamProxyController { @@ -67,6 +67,16 @@ public class StreamProxyController {
67 return streamProxyService.getAll(page, count); 67 return streamProxyService.getAll(page, count);
68 } 68 }
69 69
  70 + @Operation(summary = "查询流代理")
  71 + @Parameter(name = "app", description = "应用名")
  72 + @Parameter(name = "stream", description = "流Id")
  73 + @GetMapping(value = "/one")
  74 + @ResponseBody
  75 + public StreamProxyItem one(String app, String stream){
  76 +
  77 + return streamProxyService.getStreamProxyByAppAndStream(app, stream);
  78 + }
  79 +
70 @Operation(summary = "保存代理", parameters = { 80 @Operation(summary = "保存代理", parameters = {
71 @Parameter(name = "param", description = "代理参数", required = true), 81 @Parameter(name = "param", description = "代理参数", required = true),
72 }) 82 })
@@ -80,9 +90,16 @@ public class StreamProxyController { @@ -80,9 +90,16 @@ public class StreamProxyController {
80 if (ObjectUtils.isEmpty(param.getType())) { 90 if (ObjectUtils.isEmpty(param.getType())) {
81 param.setType("default"); 91 param.setType("default");
82 } 92 }
  93 + if (ObjectUtils.isEmpty(param.getRtpType())) {
  94 + param.setRtpType("1");
  95 + }
83 if (ObjectUtils.isEmpty(param.getGbId())) { 96 if (ObjectUtils.isEmpty(param.getGbId())) {
84 param.setGbId(null); 97 param.setGbId(null);
85 } 98 }
  99 + StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
  100 + if (streamProxyItem != null) {
  101 + streamProxyService.del(param.getApp(), param.getStream());
  102 + }
86 103
87 RequestMessage requestMessage = new RequestMessage(); 104 RequestMessage requestMessage = new RequestMessage();
88 String key = DeferredResultHolder.CALLBACK_CMD_PROXY + param.getApp() + param.getStream(); 105 String key = DeferredResultHolder.CALLBACK_CMD_PROXY + param.getApp() + param.getStream();
web_src/src/components/dialog/platformEdit.vue
@@ -91,9 +91,10 @@ @@ -91,9 +91,10 @@
91 <el-form-item label="其他选项"> 91 <el-form-item label="其他选项">
92 <el-checkbox label="启用" v-model="platform.enable" @change="checkExpires"></el-checkbox> 92 <el-checkbox label="启用" v-model="platform.enable" @change="checkExpires"></el-checkbox>
93 <!-- <el-checkbox label="云台控制" v-model="platform.ptz"></el-checkbox>--> 93 <!-- <el-checkbox label="云台控制" v-model="platform.ptz"></el-checkbox>-->
94 - <el-checkbox label="拉起离线推流" v-model="platform.startOfflinePush"></el-checkbox> 94 + <el-checkbox label="拉起推流" v-model="platform.startOfflinePush"></el-checkbox>
95 <el-checkbox label="RTCP保活" v-model="platform.rtcp" @change="rtcpCheckBoxChange"></el-checkbox> 95 <el-checkbox label="RTCP保活" v-model="platform.rtcp" @change="rtcpCheckBoxChange"></el-checkbox>
96 - <el-checkbox label="作为消息通道" v-model="platform.asMessageChannel" ></el-checkbox> 96 + <el-checkbox label="消息通道" v-model="platform.asMessageChannel" ></el-checkbox>
  97 + <el-checkbox label="推送通道" v-model="platform.autoPushChannel" ></el-checkbox>
97 </el-form-item> 98 </el-form-item>
98 <el-form-item> 99 <el-form-item>
99 <el-button type="primary" @click="onSubmit">{{ 100 <el-button type="primary" @click="onSubmit">{{
@@ -141,6 +142,7 @@ export default { @@ -141,6 +142,7 @@ export default {
141 ptz: true, 142 ptz: true,
142 rtcp: false, 143 rtcp: false,
143 asMessageChannel: false, 144 asMessageChannel: false,
  145 + autoPushChannel: false,
144 name: null, 146 name: null,
145 serverGBId: null, 147 serverGBId: null,
146 serverGBDomain: null, 148 serverGBDomain: null,
@@ -208,6 +210,7 @@ export default { @@ -208,6 +210,7 @@ export default {
208 this.platform.ptz = platform.ptz; 210 this.platform.ptz = platform.ptz;
209 this.platform.rtcp = platform.rtcp; 211 this.platform.rtcp = platform.rtcp;
210 this.platform.asMessageChannel = platform.asMessageChannel; 212 this.platform.asMessageChannel = platform.asMessageChannel;
  213 + this.platform.autoPushChannel = platform.autoPushChannel;
211 this.platform.name = platform.name; 214 this.platform.name = platform.name;
212 this.platform.serverGBId = platform.serverGBId; 215 this.platform.serverGBId = platform.serverGBId;
213 this.platform.serverGBDomain = platform.serverGBDomain; 216 this.platform.serverGBDomain = platform.serverGBDomain;
@@ -284,6 +287,7 @@ export default { @@ -284,6 +287,7 @@ export default {
284 ptz: true, 287 ptz: true,
285 rtcp: false, 288 rtcp: false,
286 asMessageChannel: false, 289 asMessageChannel: false,
  290 + autoPushChannel: false,
287 name: null, 291 name: null,
288 serverGBId: null, 292 serverGBId: null,
289 administrativeDivision: null, 293 administrativeDivision: null,