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 207 <version>2.1.3</version>
208 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 216 <!-- json解析库fastjson2 -->
211 217 <dependency>
212 218 <groupId>com.alibaba.fastjson2</groupId>
... ...
sql/2.6.9更新.sql
1 1 alter table wvp_device_channel
2   - change stream_id stream_id varying(255)
3 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 194 create_time character varying(50),
195 195 update_time character varying(50),
196 196 as_message_channel bool default false,
  197 + auto_push_channel bool default false,
197 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 244 create_time character varying(50),
244 245 name character varying(255),
245 246 update_time character varying(50),
  247 + stream_key character varying(255),
246 248 enable_disable_none_reader bool default false,
247 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 186 @Schema(description = "是否作为消息通道")
187 187 private boolean asMessageChannel;
188 188  
  189 + @Schema(description = "是否作为消息通道")
  190 + private boolean autoPushChannel;
  191 +
189 192 public Integer getId() {
190 193 return id;
191 194 }
... ... @@ -425,4 +428,12 @@ public class ParentPlatform {
425 428 public void setAsMessageChannel(boolean asMessageChannel) {
426 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 32  
33 33 public void putCatalogSubscribe(String platformId, SubscribeInfo subscribeInfo) {
34 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 44 public SubscribeInfo getCatalogSubscribe(String platformId) {
... ... @@ -63,11 +65,13 @@ public class SubscribeHolder {
63 65 dynamicTask.startCron(key, new MobilePositionSubscribeHandlerTask(platformId),
64 66 subscribeInfo.getGpsInterval() * 1000);
65 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 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 18  
19 19 }
20 20  
  21 + public SubscribeInfo() {
  22 + }
  23 +
21 24 private String id;
22 25  
23 26 private SIPRequest request;
... ... @@ -33,6 +36,21 @@ public class SubscribeInfo {
33 36 private String sn;
34 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 54 public String getId() {
37 55 return id;
38 56 }
... ... @@ -96,4 +114,28 @@ public class SubscribeInfo {
96 114 public void setGpsInterval(int gpsInterval) {
97 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 93 }
94 94 if (event.getGbStreams() != null && event.getGbStreams().size() > 0){
95 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 100 continue;
98 101 }
99 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 228 SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(),
229 229 parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort());
230 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 232 // to
233 233 SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain());
234 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 237 // Forwards
238 238 MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
... ... @@ -242,7 +242,7 @@ public class SIPRequestHeaderPlarformProvider {
242 242 // 设置编码, 防止中文乱码
243 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 247 request = (SIPRequest) messageFactory.createRequest(requestURI, Request.NOTIFY, callIdHeader, cSeqHeader, fromHeader,
248 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 169  
170 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 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 3 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
4 4 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
5 5 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  6 +import com.google.common.primitives.Bytes;
6 7 import gov.nist.javax.sip.message.SIPRequest;
7 8 import gov.nist.javax.sip.message.SIPResponse;
8 9 import org.apache.commons.lang3.ArrayUtils;
... ... @@ -203,15 +204,14 @@ public abstract class SIPRequestProcessorParent {
203 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 209 Document xml;
210 210 try {
211 211 xml = reader.read(new ByteArrayInputStream(bytesResult));
212 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 215 String[] xmlLineArray = new String(bytesResult).split("\\r?\\n");
216 216  
217 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 13 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
14 14 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
15 15 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
  16 +import com.genersoft.iot.vmp.service.IPlatformService;
16 17 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
17 18 import gov.nist.javax.sip.message.SIPRequest;
18 19 import gov.nist.javax.sip.message.SIPResponse;
... ... @@ -53,6 +54,10 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
53 54 @Autowired
54 55 private SIPSender sipSender;
55 56  
  57 +
  58 + @Autowired
  59 + private IPlatformService platformService;
  60 +
56 61 @Override
57 62 public void afterPropertiesSet() throws Exception {
58 63 // 添加消息处理的订阅
... ... @@ -194,5 +199,8 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
194 199 } catch (SipException | InvalidArgumentException | ParseException e) {
195 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 78 }
79 79 taskExecutor.execute(()->{
80 80 try {
81   -
82 81 String sn = getText(rootElement, "SN");
83 82 String channelId = getText(rootElement, "DeviceID");
84 83 RecordInfo recordInfo = new RecordInfo();
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -206,6 +206,13 @@ public class ZLMHttpHookListener {
206 206 }
207 207 // 推流鉴权的处理
208 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 216 if (userSetting.getPushAuthority()) {
210 217 // 推流鉴权
211 218 if (param.getParams() == null) {
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
... ... @@ -32,13 +32,20 @@ public class ZLMRESTfulUtils {
32 32 }
33 33  
34 34 private OkHttpClient getClient(){
  35 + return getClient(null);
  36 + }
  37 +
  38 + private OkHttpClient getClient(Integer readTimeOut){
35 39 if (client == null) {
  40 + if (readTimeOut == null) {
  41 + readTimeOut = 10;
  42 + }
36 43 OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
37 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 50 httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES));
44 51 if (logger.isDebugEnabled()) {
... ... @@ -55,9 +62,13 @@ public class ZLMRESTfulUtils {
55 62  
56 63 }
57 64  
58   -
59 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 73 if (mediaServerItem == null) {
63 74 return null;
... ... @@ -264,6 +275,12 @@ public class ZLMRESTfulUtils {
264 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 284 public JSONObject getMediaServerConfig(MediaServerItem mediaServerItem){
268 285 return sendPost(mediaServerItem, "getServerConfig",null, null);
269 286 }
... ... @@ -317,7 +334,7 @@ public class ZLMRESTfulUtils {
317 334 param.put("enable_mp4", enable_mp4?1:0);
318 335 param.put("enable_audio", enable_audio?1:0);
319 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 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 41 @Schema(description = "是否 无人观看时自动停用")
42 42 private boolean enableDisableNoneReader;
43 43  
  44 + @Schema(description = "拉流代理时zlm返回的key,用于停止拉流代理")
  45 + private String streamKey;
  46 +
44 47 public String getType() {
45 48 return type;
46 49 }
... ... @@ -167,5 +170,11 @@ public class StreamProxyItem extends GbStream {
167 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 69 * @param catalogId
70 70 */
71 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 80 * 语音喊话回复BYE
81 81 */
82 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 18 import org.springframework.stereotype.Service;
19 19 import org.springframework.transaction.TransactionDefinition;
20 20 import org.springframework.transaction.TransactionStatus;
  21 +import org.springframework.transaction.annotation.Transactional;
21 22 import org.springframework.util.ObjectUtils;
22 23  
23 24 import java.util.ArrayList;
... ... @@ -263,4 +264,9 @@ public class GbStreamServiceImpl implements IGbStreamService {
263 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 13 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
14 14 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
15 15 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
  16 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
16 17 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
17 18 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
18 19 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
... ... @@ -28,6 +29,7 @@ import com.genersoft.iot.vmp.storager.dao.*;
28 29 import com.genersoft.iot.vmp.utils.DateUtil;
29 30 import com.github.pagehelper.PageHelper;
30 31 import com.github.pagehelper.PageInfo;
  32 +import gov.nist.javax.sip.message.SIPRequest;
31 33 import org.slf4j.Logger;
32 34 import org.slf4j.LoggerFactory;
33 35 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -35,12 +37,19 @@ import org.springframework.stereotype.Service;
35 37  
36 38 import javax.sip.InvalidArgumentException;
37 39 import javax.sip.ResponseEvent;
  40 +import javax.sip.PeerUnavailableException;
38 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 47 import java.text.ParseException;
40 48 import java.util.HashMap;
41 49 import java.util.List;
42 50 import java.util.Map;
43 51 import java.util.UUID;
  52 +import java.util.*;
44 53  
45 54 /**
46 55 * @author lin
... ... @@ -199,6 +208,7 @@ public class PlatformServiceImpl implements IPlatformService {
199 208 }
200 209 }
201 210  
  211 +
202 212 return false;
203 213 }
204 214  
... ... @@ -243,19 +253,20 @@ public class PlatformServiceImpl implements IPlatformService {
243 253 try {
244 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 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 271 }, eventResult -> {
261 272 // 心跳成功
... ... @@ -273,6 +284,31 @@ public class PlatformServiceImpl implements IPlatformService {
273 284 },
274 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 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 10 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
11 11 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
12 12 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
  13 +import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
13 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 17 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
15 18 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
16 19 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
... ... @@ -60,6 +63,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
60 63 private ZLMRESTfulUtils zlmresTfulUtils;
61 64  
62 65 @Autowired
  66 + private ZLMServerFactory zlmServerFactory;
  67 +
  68 + @Autowired
63 69 private StreamProxyMapper streamProxyMapper;
64 70  
65 71 @Autowired
... ... @@ -137,7 +143,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
137 143 dstUrl = String.format("%s://%s:%s/%s/%s", schemaForUri, "127.0.0.1", port, param.getApp(),
138 144 param.getStream());
139 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 147 param.getStream());
142 148 }
143 149 param.setDstUrl(dstUrl);
... ... @@ -154,15 +160,14 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
154 160 callback.run(ErrorCode.ERROR100.getCode(), "保存失败", null);
155 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 169 if (param.isEnable()) {
159 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 171 String delayTalkKey = UUID.randomUUID().toString();
167 172 dynamicTask.startDelay(delayTalkKey, ()->{
168 173 StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(param.getApp(), param.getStream(), mediaInfo.getId(), false);
... ... @@ -172,9 +177,10 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
172 177 dynamicTask.stop(talkKey);
173 178 callback.run(ErrorCode.ERROR100.getCode(), "超时", null);
174 179 }
175   - }, 5000);
  180 + }, 7000);
176 181 JSONObject jsonObject = addStreamProxyToZlm(param);
177 182 if (jsonObject != null && jsonObject.getInteger("code") == 0) {
  183 + hookSubscribe.removeSubscribe(hookSubscribeForStreamChange);
178 184 dynamicTask.stop(talkKey);
179 185 StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
180 186 mediaInfo, param.getApp(), param.getStream(), null, null);
... ... @@ -304,13 +310,32 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
304 310 if (mediaServerItem == null) {
305 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 317 result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrcUrl().trim(), param.getDstUrl(),
312 318 param.getTimeoutMs() + "", param.isEnableAudio(), param.isEnableMp4(),
313 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 340 return result;
316 341 }
... ... @@ -321,7 +346,12 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
321 346 return null;
322 347 }
323 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 355 return result;
326 356 }
327 357  
... ... @@ -335,18 +365,19 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
335 365 StreamProxyItem streamProxyItem = videoManagerStorager.queryStreamProxy(app, stream);
336 366 if (streamProxyItem != null) {
337 367 gbStreamService.sendCatalogMsg(streamProxyItem, CatalogEvent.DEL);
  368 +
  369 + // 如果关联了国标那么移除关联
  370 + platformGbStreamMapper.delByAppAndStream(app, stream);
  371 + gbStreamMapper.del(app, stream);
338 372 videoManagerStorager.deleteStreamProxy(app, stream);
  373 + redisCatchStorage.removeStream(streamProxyItem.getMediaServerId(), "PULL", app, stream);
339 374 JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem);
340 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 383 @Override
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
... ... @@ -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 444 platformGbStreamMapper.batchAdd(streamPushItemListFroPlatform);
445 445 // 发送通知
446 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 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 168 " <if test='online == true' > AND dc.status=true</if> " +
169 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 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 173 " <if test='catalogId != null ' > AND pgc.platform_id = #{platformId} and pgc.catalog_id=#{catalogId} </if> " +
174 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 16 public interface ParentPlatformMapper {
17 17  
18 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 20 "status,start_offline_push,catalog_id,administrative_division,catalog_group,create_time,update_time) " +
21 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 23 " #{status}, #{startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime})")
24 24 int addParentPlatform(ParentPlatform parentPlatform);
25 25  
... ... @@ -42,6 +42,7 @@ public interface ParentPlatformMapper {
42 42 "ptz=#{ptz}, " +
43 43 "rtcp=#{rtcp}, " +
44 44 "as_message_channel=#{asMessageChannel}, " +
  45 + "auto_push_channel=#{autoPushChannel}, " +
45 46 "status=#{status}, " +
46 47 "start_offline_push=#{startOfflinePush}, " +
47 48 "catalog_group=#{catalogGroup}, " +
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java
... ... @@ -12,9 +12,9 @@ import java.util.List;
12 12 public interface StreamProxyMapper {
13 13  
14 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 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 18 "#{enableRemoveNoneReader}, #{enableDisableNoneReader}, #{createTime} )")
19 19 int add(StreamProxyItem streamProxyDto);
20 20  
... ... @@ -33,6 +33,7 @@ public interface StreamProxyMapper {
33 33 "enable_audio=#{enableAudio}, " +
34 34 "enable=#{enable}, " +
35 35 "status=#{status}, " +
  36 + "stream_key=#{streamKey}, " +
36 37 "enable_remove_none_reader=#{enableRemoveNoneReader}, " +
37 38 "enable_disable_none_reader=#{enableDisableNoneReader}, " +
38 39 "enable_mp4=#{enableMp4} " +
... ... @@ -45,7 +46,7 @@ public interface StreamProxyMapper {
45 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 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 50 List<StreamProxyItem> selectForEnable(boolean enable);
50 51  
51 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 36 import org.springframework.web.bind.annotation.*;
37 37 import org.springframework.web.context.request.async.DeferredResult;
38 38  
  39 +import javax.servlet.ServletOutputStream;
39 40 import javax.servlet.http.HttpServletResponse;
40 41 import javax.sip.InvalidArgumentException;
41 42 import javax.sip.SipException;
... ... @@ -472,7 +473,10 @@ public class DeviceQuery {
472 473 try {
473 474 final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null? ".jpg": ("_" + mark + ".jpg"))).toPath());
474 475 resp.setContentType(MediaType.IMAGE_PNG_VALUE);
  476 + ServletOutputStream outputStream = resp.getOutputStream();
475 477 IOUtils.copy(in, resp.getOutputStream());
  478 + in.close();
  479 + outputStream.close();
476 480 } catch (IOException e) {
477 481 resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
478 482 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java
1 1 package com.genersoft.iot.vmp.vmanager.gb28181.gbStream;
2 2  
  3 +import com.genersoft.iot.vmp.conf.exception.ControllerException;
3 4 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
  5 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
4 6 import com.genersoft.iot.vmp.service.IGbStreamService;
  7 +import com.genersoft.iot.vmp.service.IPlatformService;
5 8 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  9 +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
6 10 import com.genersoft.iot.vmp.vmanager.gb28181.gbStream.bean.GbStreamParam;
7 11 import com.github.pagehelper.PageInfo;
8 12 import io.swagger.v3.oas.annotations.Operation;
... ... @@ -14,6 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired;
14 18 import org.springframework.util.ObjectUtils;
15 19 import org.springframework.web.bind.annotation.*;
16 20  
  21 +import java.util.ArrayList;
17 22 import java.util.List;
18 23  
19 24 @Tag(name = "视频流关联到级联平台")
... ... @@ -28,7 +33,7 @@ public class GbStreamController {
28 33 private IGbStreamService gbStreamService;
29 34  
30 35 @Autowired
31   - private IVideoManagerStorage storager;
  36 + private IPlatformService platformService;
32 37  
33 38  
34 39 /**
... ... @@ -107,4 +112,20 @@ public class GbStreamController {
107 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 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 80 @Operation(summary = "保存代理", parameters = {
71 81 @Parameter(name = "param", description = "代理参数", required = true),
72 82 })
... ... @@ -80,9 +90,16 @@ public class StreamProxyController {
80 90 if (ObjectUtils.isEmpty(param.getType())) {
81 91 param.setType("default");
82 92 }
  93 + if (ObjectUtils.isEmpty(param.getRtpType())) {
  94 + param.setRtpType("1");
  95 + }
83 96 if (ObjectUtils.isEmpty(param.getGbId())) {
84 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 104 RequestMessage requestMessage = new RequestMessage();
88 105 String key = DeferredResultHolder.CALLBACK_CMD_PROXY + param.getApp() + param.getStream();
... ...
web_src/src/components/dialog/platformEdit.vue
... ... @@ -91,9 +91,10 @@
91 91 <el-form-item label="其他选项">
92 92 <el-checkbox label="启用" v-model="platform.enable" @change="checkExpires"></el-checkbox>
93 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 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 98 </el-form-item>
98 99 <el-form-item>
99 100 <el-button type="primary" @click="onSubmit">{{
... ... @@ -141,6 +142,7 @@ export default {
141 142 ptz: true,
142 143 rtcp: false,
143 144 asMessageChannel: false,
  145 + autoPushChannel: false,
144 146 name: null,
145 147 serverGBId: null,
146 148 serverGBDomain: null,
... ... @@ -208,6 +210,7 @@ export default {
208 210 this.platform.ptz = platform.ptz;
209 211 this.platform.rtcp = platform.rtcp;
210 212 this.platform.asMessageChannel = platform.asMessageChannel;
  213 + this.platform.autoPushChannel = platform.autoPushChannel;
211 214 this.platform.name = platform.name;
212 215 this.platform.serverGBId = platform.serverGBId;
213 216 this.platform.serverGBDomain = platform.serverGBDomain;
... ... @@ -284,6 +287,7 @@ export default {
284 287 ptz: true,
285 288 rtcp: false,
286 289 asMessageChannel: false,
  290 + autoPushChannel: false,
287 291 name: null,
288 292 serverGBId: null,
289 293 administrativeDivision: null,
... ...