Commit ab81136765f1b641223b982b2baef13e06307fe4

Authored by 648540858
1 parent 9b1af8ef

优化适配zlm的hook保活

Showing 20 changed files with 440 additions and 78 deletions
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
1 1 package com.genersoft.iot.vmp.gb28181;
2 2  
3 3 import com.genersoft.iot.vmp.conf.SipConfig;
4   -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
5 4 import com.genersoft.iot.vmp.gb28181.transmit.ISIPProcessorObserver;
6   -import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
7 5 import gov.nist.javax.sip.SipProviderImpl;
8 6 import gov.nist.javax.sip.SipStackImpl;
9 7 import org.slf4j.Logger;
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
... ... @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.gb28181.event.offline.OfflineEvent;
5 5 import com.genersoft.iot.vmp.gb28181.event.platformKeepaliveExpire.PlatformKeepaliveExpireEvent;
6 6 import com.genersoft.iot.vmp.gb28181.event.platformNotRegister.PlatformNotRegisterEvent;
7 7 import com.genersoft.iot.vmp.media.zlm.event.ZLMOfflineEvent;
  8 +import com.genersoft.iot.vmp.media.zlm.event.ZLMOnlineEvent;
8 9 import org.springframework.beans.factory.annotation.Autowired;
9 10 import org.springframework.context.ApplicationEventPublisher;
10 11 import org.springframework.stereotype.Component;
... ... @@ -73,5 +74,10 @@ public class EventPublisher {
73 74 outEvent.setMediaServerId(mediaServerId);
74 75 applicationEventPublisher.publishEvent(outEvent);
75 76 }
76   -
  77 +
  78 + public void zlmOnlineEventPublish(String mediaServerId) {
  79 + ZLMOnlineEvent outEvent = new ZLMOnlineEvent(this);
  80 + outEvent.setMediaServerId(mediaServerId);
  81 + applicationEventPublisher.publishEvent(outEvent);
  82 + }
77 83 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -179,29 +179,33 @@ public class ZLMHttpHookListener {
179 179 public ResponseEntity<String> onPublish(@RequestBody JSONObject json) {
180 180  
181 181 logger.debug("[ ZLM HOOK ]on_publish API调用,参数:" + json.toString());
182   -
  182 + JSONObject ret = new JSONObject();
  183 + ret.put("code", 0);
  184 + ret.put("msg", "success");
  185 + ret.put("enableHls", true);
  186 + ret.put("enableMP4", userSetup.isRecordPushLive());
183 187 String mediaServerId = json.getString("mediaServerId");
184 188 ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
185 189 if (subscribe != null) {
186 190 MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
187 191 if (mediaInfo != null) {
188 192 subscribe.response(mediaInfo, json);
  193 + }else {
  194 + ret.put("code", 1);
  195 + ret.put("msg", "zlm not register");
189 196 }
190 197 }
191 198 String app = json.getString("app");
192 199 String stream = json.getString("stream");
193 200 StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(stream);
194   - JSONObject ret = new JSONObject();
  201 +
195 202 // 录像回放时不进行录像下载
196 203 if (streamInfo != null) {
197 204 ret.put("enableMP4", false);
198 205 }else {
199 206 ret.put("enableMP4", userSetup.isRecordPushLive());
200 207 }
201   - ret.put("code", 0);
202   - ret.put("msg", "success");
203   - ret.put("enableHls", true);
204   - ret.put("enableMP4", userSetup.isRecordPushLive());
  208 +
205 209 return new ResponseEntity<String>(ret.toString(), HttpStatus.OK);
206 210 }
207 211  
... ... @@ -340,37 +344,38 @@ public class ZLMHttpHookListener {
340 344 if (!"rtp".equals(app)){
341 345 String type = OriginType.values()[item.getOriginType()].getType();
342 346 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
343   - if (regist) {
344   - StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem, app, streamId, tracks);
345   - redisCatchStorage.addStream(mediaServerItem, type, app, streamId, streamInfo);
346   - if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal()
347   - || item.getOriginType() == OriginType.RTMP_PUSH.ordinal()
348   - || item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) {
349   - zlmMediaListManager.addPush(item);
350   - }
351   - }else {
352   - // 兼容流注销时类型错误的问题,等zlm更新后删除
353   - StreamPushItem streamPushItem = streamPushService.getPush(app, streamId);
354   - if (streamPushItem != null) {
355   - type = "PUSH";
  347 + if (mediaServerItem != null){
  348 + if (regist) {
  349 + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem, app, streamId, tracks);
  350 + redisCatchStorage.addStream(mediaServerItem, type, app, streamId, streamInfo);
  351 + if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal()
  352 + || item.getOriginType() == OriginType.RTMP_PUSH.ordinal()
  353 + || item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) {
  354 + zlmMediaListManager.addPush(item);
  355 + }
356 356 }else {
357   - StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(app, streamId);
358   - if (streamProxyByAppAndStream != null) {
359   - type = "PULL";
  357 + // 兼容流注销时类型错误的问题,等zlm更新后删除
  358 + StreamPushItem streamPushItem = streamPushService.getPush(app, streamId);
  359 + if (streamPushItem != null) {
  360 + type = "PUSH";
  361 + }else {
  362 + StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(app, streamId);
  363 + if (streamProxyByAppAndStream != null) {
  364 + type = "PULL";
  365 + }
360 366 }
  367 + zlmMediaListManager.removeMedia(app, streamId);
  368 + redisCatchStorage.removeStream(mediaServerItem.getId(), type, app, streamId);
361 369 }
362   - zlmMediaListManager.removeMedia(app, streamId);
363   - redisCatchStorage.removeStream(mediaServerItem, type, app, streamId);
  370 + // 发送流变化redis消息
  371 + JSONObject jsonObject = new JSONObject();
  372 + jsonObject.put("serverId", userSetup.getServerId());
  373 + jsonObject.put("app", app);
  374 + jsonObject.put("stream", streamId);
  375 + jsonObject.put("register", regist);
  376 + jsonObject.put("mediaServerId", mediaServerId);
  377 + redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
364 378 }
365   -
366   - // 发送流变化redis消息
367   - JSONObject jsonObject = new JSONObject();
368   - jsonObject.put("serverId", userSetup.getServerId());
369   - jsonObject.put("app", app);
370   - jsonObject.put("stream", streamId);
371   - jsonObject.put("register", regist);
372   - jsonObject.put("mediaServerId", mediaServerId);
373   - redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
374 379 }
375 380 }
376 381 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
... ... @@ -141,7 +141,6 @@ public class ZLMMediaListManager {
141 141 }else {
142 142 gbStreamMapper.add(transform);
143 143 }
144   -
145 144 }
146 145 }
147 146  
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
... ... @@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
4 4 import com.alibaba.fastjson.JSONArray;
5 5 import com.alibaba.fastjson.JSONObject;
6 6 import com.genersoft.iot.vmp.conf.MediaConfig;
  7 +import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
7 8 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
8 9 import com.genersoft.iot.vmp.service.IMediaServerService;
9 10 import com.genersoft.iot.vmp.service.IStreamProxyService;
... ... @@ -17,6 +18,7 @@ import org.springframework.core.annotation.Order;
17 18 import org.springframework.scheduling.annotation.Async;
18 19 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
19 20 import org.springframework.stereotype.Component;
  21 +import org.springframework.util.StringUtils;
20 22  
21 23 import java.util.*;
22 24  
... ... @@ -38,6 +40,9 @@ public class ZLMRunner implements CommandLineRunner {
38 40 private IStreamProxyService streamProxyService;
39 41  
40 42 @Autowired
  43 + private EventPublisher publisher;
  44 +
  45 + @Autowired
41 46 private IMediaServerService mediaServerService;
42 47  
43 48 @Autowired
... ... @@ -117,7 +122,7 @@ public class ZLMRunner implements CommandLineRunner {
117 122  
118 123 @Async
119 124 public void connectZlmServer(MediaServerItem mediaServerItem){
120   - ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem);
  125 + ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem, 1);
121 126 if (zlmServerConfig != null) {
122 127 zlmServerConfig.setIp(mediaServerItem.getIp());
123 128 zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort());
... ... @@ -126,7 +131,7 @@ public class ZLMRunner implements CommandLineRunner {
126 131 }
127 132 }
128 133  
129   - public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem) {
  134 + public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem, int index) {
130 135 if (startGetMedia == null) { return null;}
131 136 if (!mediaServerItem.isDefaultServer() && mediaServerService.getOne(mediaServerItem.getId()) == null) {
132 137 return null;
... ... @@ -143,14 +148,19 @@ public class ZLMRunner implements CommandLineRunner {
143 148 ZLMServerConfig.setIp(mediaServerItem.getIp());
144 149 }
145 150 } else {
146   - logger.error("[ {} ]-[ {}:{} ]主动连接失败失败, 2s后重试",
147   - mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
  151 + logger.error("[ {} ]-[ {}:{} ]第{}次主动连接失败, 2s后重试",
  152 + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort(), index);
  153 + if (index == 1 && !StringUtils.isEmpty(mediaServerItem.getId())) {
  154 + logger.info("[ {} ]-[ {}:{} ]第{}次主动连接失败, 开始清理相关资源",
  155 + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort(), index);
  156 + publisher.zlmOfflineEventPublish(mediaServerItem.getId());
  157 + }
148 158 try {
149 159 Thread.sleep(2000);
150 160 } catch (InterruptedException e) {
151 161 e.printStackTrace();
152 162 }
153   - ZLMServerConfig = getMediaServerConfig(mediaServerItem);
  163 + ZLMServerConfig = getMediaServerConfig(mediaServerItem, index += 1);
154 164 }
155 165 return ZLMServerConfig;
156 166  
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMKeepliveTimeoutListener.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.event;
  2 +
  3 +import com.genersoft.iot.vmp.common.VideoManagerConstants;
  4 +import com.genersoft.iot.vmp.conf.UserSetup;
  5 +import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
  6 +import org.slf4j.Logger;
  7 +import org.slf4j.LoggerFactory;
  8 +import org.springframework.beans.factory.annotation.Autowired;
  9 +import org.springframework.data.redis.connection.Message;
  10 +import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
  11 +import org.springframework.data.redis.listener.RedisMessageListenerContainer;
  12 +import org.springframework.stereotype.Component;
  13 +
  14 +/**
  15 + * @description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
  16 + * @author: swwheihei
  17 + * @date: 2020年5月6日 上午11:35:46
  18 + */
  19 +@Component
  20 +public class ZLMKeepliveTimeoutListener extends KeyExpirationEventMessageListener {
  21 +
  22 + private Logger logger = LoggerFactory.getLogger(ZLMKeepliveTimeoutListener.class);
  23 +
  24 + @Autowired
  25 + private EventPublisher publisher;
  26 +
  27 + @Autowired
  28 + private UserSetup userSetup;
  29 +
  30 + public ZLMKeepliveTimeoutListener(RedisMessageListenerContainer listenerContainer) {
  31 + super(listenerContainer);
  32 + }
  33 +
  34 + /**
  35 + * 监听失效的key,key格式为keeplive_deviceId
  36 + * @param message
  37 + * @param pattern
  38 + */
  39 + @Override
  40 + public void onMessage(Message message, byte[] pattern) {
  41 + // 获取失效的key
  42 + String expiredKey = message.toString();
  43 + String KEEPLIVEKEY_PREFIX = VideoManagerConstants.MEDIA_SERVER_KEEPALIVE_PREFIX + userSetup.getServerId() + "_";
  44 + if(!expiredKey.startsWith(KEEPLIVEKEY_PREFIX)){
  45 + return;
  46 + }
  47 +
  48 + String mediaServerId = expiredKey.substring(KEEPLIVEKEY_PREFIX.length(),expiredKey.length());
  49 +
  50 + publisher.zlmOfflineEventPublish(mediaServerId);
  51 + }
  52 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOfflineEvent.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.event;
  2 +
  3 +/**
  4 + * zlm离线事件类
  5 + */
  6 +public class ZLMOfflineEvent extends ZLMEventAbstract {
  7 +
  8 + public ZLMOfflineEvent(Object source) {
  9 + super(source);
  10 + }
  11 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOfflineEventListener.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.event;
  2 +
  3 +import com.genersoft.iot.vmp.conf.UserSetup;
  4 +import com.genersoft.iot.vmp.service.IMediaServerService;
  5 +import com.genersoft.iot.vmp.service.IStreamProxyService;
  6 +import com.genersoft.iot.vmp.service.IStreamPushService;
  7 +import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  8 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
  9 +import org.slf4j.Logger;
  10 +import org.slf4j.LoggerFactory;
  11 +import org.springframework.beans.factory.annotation.Autowired;
  12 +import org.springframework.context.ApplicationListener;
  13 +import org.springframework.stereotype.Component;
  14 +
  15 +/**
  16 + *
  17 + */
  18 +@Component
  19 +public class ZLMOfflineEventListener implements ApplicationListener<ZLMOfflineEvent> {
  20 +
  21 + private final static Logger logger = LoggerFactory.getLogger(ZLMOfflineEventListener.class);
  22 +
  23 + @Autowired
  24 + private IMediaServerService mediaServerService;
  25 +
  26 + @Autowired
  27 + private IStreamPushService streamPushService;
  28 +
  29 + @Autowired
  30 + private IStreamProxyService streamProxyService;
  31 +
  32 + @Override
  33 + public void onApplicationEvent(ZLMOfflineEvent event) {
  34 +
  35 + if (logger.isDebugEnabled()) {
  36 + logger.debug("ZLM离线事件触发,ID:" + event.getMediaServerId());
  37 + }
  38 + // 处理ZLM离线
  39 + mediaServerService.zlmServerOffline(event.getMediaServerId());
  40 + streamProxyService.zlmServerOffline(event.getMediaServerId());
  41 + streamPushService.zlmServerOffline(event.getMediaServerId());
  42 + // TODO 处理对国标的影响
  43 + }
  44 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOnlineEvent.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.event;
  2 +
  3 +/**
  4 + * zlm在线事件
  5 + */
  6 +public class ZLMOnlineEvent extends ZLMEventAbstract {
  7 +
  8 + public ZLMOnlineEvent(Object source) {
  9 + super(source);
  10 + }
  11 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOnlineEventListener.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.event;
  2 +
  3 +import com.genersoft.iot.vmp.conf.SipConfig;
  4 +import com.genersoft.iot.vmp.conf.UserSetup;
  5 +import com.genersoft.iot.vmp.service.IMediaServerService;
  6 +import com.genersoft.iot.vmp.service.IStreamProxyService;
  7 +import com.genersoft.iot.vmp.service.IStreamPushService;
  8 +import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  9 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
  10 +import org.slf4j.Logger;
  11 +import org.slf4j.LoggerFactory;
  12 +import org.springframework.beans.factory.annotation.Autowired;
  13 +import org.springframework.context.ApplicationListener;
  14 +import org.springframework.stereotype.Component;
  15 +
  16 +import java.text.SimpleDateFormat;
  17 +
  18 +/**
  19 + * @description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源:
  20 + * 1、设备主动注销,发送注销指令
  21 + * 2、设备未知原因离线,心跳超时
  22 + * @author: swwheihei
  23 + * @date: 2020年5月6日 下午1:51:23
  24 + */
  25 +@Component
  26 +public class ZLMOnlineEventListener implements ApplicationListener<ZLMOnlineEvent> {
  27 +
  28 + private final static Logger logger = LoggerFactory.getLogger(ZLMOnlineEventListener.class);
  29 +
  30 + @Autowired
  31 + private IVideoManagerStorager storager;
  32 +
  33 + @Autowired
  34 + private RedisUtil redis;
  35 +
  36 + @Autowired
  37 + private SipConfig sipConfig;
  38 +
  39 + @Autowired
  40 + private UserSetup userSetup;
  41 +
  42 + @Autowired
  43 + private IMediaServerService mediaServerService;
  44 +
  45 + @Autowired
  46 + private IStreamPushService streamPushService;
  47 +
  48 + @Autowired
  49 + private IStreamProxyService streamProxyService;
  50 +
  51 + private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  52 +
  53 + @Override
  54 + public void onApplicationEvent(ZLMOnlineEvent event) {
  55 +
  56 + if (logger.isDebugEnabled()) {
  57 + logger.debug("ZLM上线事件触发,ID:" + event.getMediaServerId());
  58 + }
  59 + streamPushService.zlmServerOnline(event.getMediaServerId());
  60 + streamProxyService.zlmServerOnline(event.getMediaServerId());
  61 +
  62 +
  63 +
  64 + }
  65 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java
... ... @@ -78,10 +78,10 @@ public interface IStreamProxyService {
78 78  
79 79 /**
80 80 * 新的节点加入
81   - * @param zlmServerConfig
  81 + * @param mediaServerId
82 82 * @return
83 83 */
84   - void zlmServerOnline(ZLMServerConfig zlmServerConfig);
  84 + void zlmServerOnline(String mediaServerId);
85 85  
86 86 /**
87 87 * 节点离线
... ... @@ -89,4 +89,6 @@ public interface IStreamProxyService {
89 89 * @return
90 90 */
91 91 void zlmServerOffline(String mediaServerId);
  92 +
  93 + void clean();
92 94 }
... ...
src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java
... ... @@ -34,6 +34,7 @@ public interface IStreamPushService {
34 34 * @return
35 35 */
36 36 PageInfo<StreamPushItem> getPushList(Integer page, Integer count);
  37 + List<StreamPushItem> getPushList(String mediaSererId);
37 38  
38 39 StreamPushItem transform(MediaItem item);
39 40  
... ... @@ -49,10 +50,10 @@ public interface IStreamPushService {
49 50  
50 51 /**
51 52 * 新的节点加入
52   - * @param zlmServerConfig
  53 + * @param mediaServerId
53 54 * @return
54 55 */
55   - void zlmServerOnline(ZLMServerConfig zlmServerConfig);
  56 + void zlmServerOnline(String mediaServerId);
56 57  
57 58 /**
58 59 * 节点离线
... ... @@ -61,4 +62,5 @@ public interface IStreamPushService {
61 62 */
62 63 void zlmServerOffline(String mediaServerId);
63 64  
  65 + void clean();
64 66 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
... ... @@ -4,10 +4,10 @@ import com.alibaba.fastjson.JSON;
4 4 import com.alibaba.fastjson.JSONArray;
5 5 import com.alibaba.fastjson.JSONObject;
6 6 import com.genersoft.iot.vmp.common.VideoManagerConstants;
7   -import com.genersoft.iot.vmp.conf.MediaConfig;
8 7 import com.genersoft.iot.vmp.conf.SipConfig;
9 8 import com.genersoft.iot.vmp.conf.UserSetup;
10 9 import com.genersoft.iot.vmp.gb28181.bean.Device;
  10 +import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
11 11 import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
12 12 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
13 13 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
... ... @@ -71,6 +71,9 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR
71 71 private RedisUtil redisUtil;
72 72  
73 73 @Autowired
  74 + private EventPublisher publisher;
  75 +
  76 + @Autowired
74 77 JedisUtil jedisUtil;
75 78  
76 79 private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
... ... @@ -312,8 +315,6 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR
312 315 return mediaServerMapper.update(mediaSerItem);
313 316 }
314 317  
315   -
316   -
317 318 /**
318 319 * 处理zlm上线
319 320 * @param zlmServerConfig zlm上线携带的参数
... ... @@ -353,28 +354,31 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR
353 354 if (serverItem.getRtpProxyPort() == 0) {
354 355 serverItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
355 356 }
356   - if (StringUtils.isEmpty(serverItem.getId())) {
357   - serverItem.setId(zlmServerConfig.getGeneralMediaServerId());
358   - }
359 357 serverItem.setStatus(true);
  358 +
360 359 if (StringUtils.isEmpty(serverItem.getId())) {
361 360 serverItem.setId(zlmServerConfig.getGeneralMediaServerId());
362 361 mediaServerMapper.updateByHostAndPort(serverItem);
363 362 }else {
364 363 mediaServerMapper.update(serverItem);
365 364 }
366   - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + serverItem.getId();
  365 + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + zlmServerConfig.getGeneralMediaServerId();
367 366 if (redisUtil.get(key) == null) {
368   - SsrcConfig ssrcConfig = new SsrcConfig(serverItem.getId(), null, sipConfig.getDomain());
  367 + SsrcConfig ssrcConfig = new SsrcConfig(zlmServerConfig.getGeneralMediaServerId(), null, sipConfig.getDomain());
369 368 serverItem.setSsrcConfig(ssrcConfig);
370   - redisUtil.set(key, serverItem);
  369 + }else {
  370 + MediaServerItem mediaServerItemInRedis = (MediaServerItem)redisUtil.get(key);
  371 + serverItem.setSsrcConfig(mediaServerItemInRedis.getSsrcConfig());
371 372 }
372   -
  373 + redisUtil.set(key, serverItem);
373 374 resetOnlineServerItem(serverItem);
374 375 updateMediaServerKeepalive(serverItem.getId(), null);
375 376 setZLMConfig(serverItem);
  377 + publisher.zlmOnlineEventPublish(serverItem.getId());
  378 +
376 379 }
377 380  
  381 +
378 382 @Override
379 383 public void zlmServerOffline(String mediaServerId) {
380 384 delete(mediaServerId);
... ... @@ -567,6 +571,10 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR
567 571 @Override
568 572 public void updateMediaServerKeepalive(String mediaServerId, JSONObject data) {
569 573 MediaServerItem mediaServerItem = getOne(mediaServerId);
  574 + if (mediaServerItem == null) {
  575 + logger.warn("[更新ZLM 保活信息]失败,未找到流媒体信息");
  576 + return;
  577 + }
570 578 String key = VideoManagerConstants.MEDIA_SERVER_KEEPALIVE_PREFIX + userSetup.getServerId() + "_" + mediaServerId;
571 579 int hookAliveInterval = mediaServerItem.getHookAliveInterval() + 2;
572 580 redisUtil.set(key, data, hookAliveInterval);
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
... ... @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.service.impl;
2 2  
3 3 import com.alibaba.fastjson.JSONObject;
4 4 import com.genersoft.iot.vmp.common.StreamInfo;
  5 +import com.genersoft.iot.vmp.conf.UserSetup;
5 6 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
6 7 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
7 8 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
... ... @@ -28,8 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired;
28 29 import org.springframework.stereotype.Service;
29 30 import org.springframework.util.StringUtils;
30 31  
31   -import java.util.ArrayList;
32   -import java.util.List;
  32 +import java.util.*;
33 33  
34 34 /**
35 35 * 视频代理业务
... ... @@ -55,6 +55,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
55 55 private IRedisCatchStorage redisCatchStorage;
56 56  
57 57 @Autowired
  58 + private UserSetup userSetup;
  59 +
  60 + @Autowired
58 61 private GbStreamMapper gbStreamMapper;
59 62  
60 63 @Autowired
... ... @@ -160,6 +163,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
160 163 }else {
161 164 mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
162 165 }
  166 + if (mediaServerItem == null) {
  167 + return null;
  168 + }
163 169 if ("default".equals(param.getType())){
164 170 result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl(),
165 171 param.isEnable_hls(), param.isEnable_mp4(), param.getRtp_type());
... ... @@ -244,7 +250,6 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
244 250 }
245 251 }
246 252 }
247   -
248 253 return result;
249 254 }
250 255  
... ... @@ -255,18 +260,41 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
255 260 }
256 261  
257 262 @Override
258   - public void zlmServerOnline(ZLMServerConfig zlmServerConfig) {
259   -
  263 + public void zlmServerOnline(String mediaServerId) {
  264 + zlmServerOffline(mediaServerId);
260 265 }
261 266  
262 267 @Override
263 268 public void zlmServerOffline(String mediaServerId) {
264 269 // 移除开启了无人观看自动移除的流
  270 + List<StreamProxyItem> streamProxyItemList = streamProxyMapper.selecAutoRemoveItemByMediaServerId(mediaServerId);
  271 + if (streamProxyItemList.size() > 0) {
  272 + gbStreamMapper.batchDel(streamProxyItemList);
  273 + }
265 274 streamProxyMapper.deleteAutoRemoveItemByMediaServerId(mediaServerId);
266 275 // 其他的流设置未启用
267 276 streamProxyMapper.updateStatus(false, mediaServerId);
268   - // 移除redis内流的信息
269   - redisCatchStorage.removeStream(mediaServerId, "PULL");
  277 + String type = "PULL";
  278 +
  279 + // 发送redis消息
  280 + List<StreamInfo> streamInfoList = redisCatchStorage.getStreams(mediaServerId, type);
  281 + if (streamInfoList.size() > 0) {
  282 + for (StreamInfo streamInfo : streamInfoList) {
  283 + JSONObject jsonObject = new JSONObject();
  284 + jsonObject.put("serverId", userSetup.getServerId());
  285 + jsonObject.put("app", streamInfo.getApp());
  286 + jsonObject.put("stream", streamInfo.getStreamId());
  287 + jsonObject.put("register", false);
  288 + jsonObject.put("mediaServerId", mediaServerId);
  289 + redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
  290 + // 移除redis内流的信息
  291 + redisCatchStorage.removeStream(mediaServerId, type, streamInfo.getApp(), streamInfo.getStreamId());
  292 + }
  293 + }
  294 + }
  295 +
  296 + @Override
  297 + public void clean() {
270 298  
271 299 }
272 300 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
... ... @@ -3,11 +3,15 @@ package com.genersoft.iot.vmp.service.impl;
3 3 import com.alibaba.fastjson.JSON;
4 4 import com.alibaba.fastjson.JSONObject;
5 5 import com.alibaba.fastjson.TypeReference;
  6 +import com.genersoft.iot.vmp.common.StreamInfo;
  7 +import com.genersoft.iot.vmp.conf.UserSetup;
6 8 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
  9 +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
7 10 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
8 11 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
9 12 import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
10 13 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  14 +import com.genersoft.iot.vmp.media.zlm.dto.OriginType;
11 15 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
12 16 import com.genersoft.iot.vmp.service.IMediaServerService;
13 17 import com.genersoft.iot.vmp.service.IStreamPushService;
... ... @@ -20,10 +24,7 @@ import com.github.pagehelper.PageInfo;
20 24 import org.springframework.beans.factory.annotation.Autowired;
21 25 import org.springframework.stereotype.Service;
22 26  
23   -import java.util.ArrayList;
24   -import java.util.HashMap;
25   -import java.util.List;
26   -import java.util.Map;
  27 +import java.util.*;
27 28  
28 29 @Service
29 30 public class StreamPushServiceImpl implements IStreamPushService {
... ... @@ -44,6 +45,9 @@ public class StreamPushServiceImpl implements IStreamPushService {
44 45 private IRedisCatchStorage redisCatchStorage;
45 46  
46 47 @Autowired
  48 + private UserSetup userSetup;
  49 +
  50 + @Autowired
47 51 private IMediaServerService mediaServerService;
48 52  
49 53 @Override
... ... @@ -56,7 +60,9 @@ public class StreamPushServiceImpl implements IStreamPushService {
56 60 for (MediaItem item : mediaItems) {
57 61  
58 62 // 不保存国标推理以及拉流代理的流
59   - if (item.getOriginType() == 1 || item.getOriginType() == 2 || item.getOriginType() == 8) {
  63 + if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal()
  64 + || item.getOriginType() == OriginType.RTMP_PUSH.ordinal()
  65 + || item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) {
60 66 String key = item.getApp() + "_" + item.getStream();
61 67 StreamPushItem streamPushItem = result.get(key);
62 68 if (streamPushItem == null) {
... ... @@ -98,6 +104,11 @@ public class StreamPushServiceImpl implements IStreamPushService {
98 104 }
99 105  
100 106 @Override
  107 + public List<StreamPushItem> getPushList(String mediaServerId) {
  108 + return streamPushMapper.selectAllByMediaServerId(mediaServerId);
  109 + }
  110 +
  111 + @Override
101 112 public boolean saveToGB(GbStream stream) {
102 113 stream.setStreamType("push");
103 114 stream.setStatus(true);
... ... @@ -137,17 +148,84 @@ public class StreamPushServiceImpl implements IStreamPushService {
137 148 }
138 149  
139 150 @Override
140   - public void zlmServerOnline(ZLMServerConfig zlmServerConfig) {
141   - // 似乎没啥需要做的
  151 + public void zlmServerOnline(String mediaServerId) {
  152 + // 同步zlm推流信息
  153 + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
  154 + if (mediaServerItem == null) {
  155 + return;
  156 + }
  157 + List<StreamPushItem> pushList = getPushList(mediaServerId);
  158 + if (pushList.size() > 0) {
  159 + Map<String, StreamPushItem> pushItemMap = new HashMap<>();
  160 + for (StreamPushItem streamPushItem : pushList) {
  161 + pushItemMap.put(streamPushItem.getApp() + streamPushItem.getStream(), streamPushItem);
  162 + }
  163 + zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{
  164 + if (mediaList == null) return;
  165 + String dataStr = mediaList.getString("data");
  166 +
  167 + Integer code = mediaList.getInteger("code");
  168 + List<StreamPushItem> streamPushItems = null;
  169 + if (code == 0 ) {
  170 + if (dataStr != null) {
  171 + streamPushItems = handleJSON(dataStr, mediaServerItem);
  172 + }
  173 + }
  174 +
  175 + if (streamPushItems != null) {
  176 + for (StreamPushItem streamPushItem : streamPushItems) {
  177 + pushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream());
  178 + }
  179 + }
  180 + Collection<StreamPushItem> offlinePushItems = pushItemMap.values();
  181 + if (offlinePushItems.size() > 0) {
  182 + String type = "PUSH";
  183 + streamPushMapper.delAll(new ArrayList<>(offlinePushItems));
  184 + for (StreamPushItem offlinePushItem : offlinePushItems) {
  185 + JSONObject jsonObject = new JSONObject();
  186 + jsonObject.put("serverId", userSetup.getServerId());
  187 + jsonObject.put("app", offlinePushItem.getApp());
  188 + jsonObject.put("stream", offlinePushItem.getStream());
  189 + jsonObject.put("register", false);
  190 + jsonObject.put("mediaServerId", mediaServerId);
  191 + redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
  192 + // 移除redis内流的信息
  193 + redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", offlinePushItem.getApp(), offlinePushItem.getStream());
  194 + }
  195 + }
  196 + }));
  197 + }
142 198 }
143 199  
144 200 @Override
145 201 public void zlmServerOffline(String mediaServerId) {
146   - // 移除没有serverId的推流
  202 + List<StreamPushItem> streamPushItems = streamPushMapper.selectAllByMediaServerId(mediaServerId);
  203 + // 移除没有GBId的推流
147 204 streamPushMapper.deleteWithoutGBId(mediaServerId);
  205 + gbStreamMapper.deleteWithoutGBId("push", mediaServerId);
148 206 // 其他的流设置未启用
149 207 gbStreamMapper.updateStatusByMediaServerId(mediaServerId, false);
150   - // 移除redis内流的信息
151   - redisCatchStorage.removeStream(mediaServerId, "PUSH");
  208 + // 发送流停止消息
  209 + String type = "PUSH";
  210 + // 发送redis消息
  211 + List<StreamInfo> streamInfoList = redisCatchStorage.getStreams(mediaServerId, type);
  212 + if (streamInfoList.size() > 0) {
  213 + for (StreamInfo streamInfo : streamInfoList) {
  214 + JSONObject jsonObject = new JSONObject();
  215 + jsonObject.put("serverId", userSetup.getServerId());
  216 + jsonObject.put("app", streamInfo.getApp());
  217 + jsonObject.put("stream", streamInfo.getStreamId());
  218 + jsonObject.put("register", false);
  219 + jsonObject.put("mediaServerId", mediaServerId);
  220 + redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
  221 + // 移除redis内流的信息
  222 + redisCatchStorage.removeStream(mediaServerId, type, streamInfo.getApp(), streamInfo.getStreamId());
  223 + }
  224 + }
  225 + }
  226 +
  227 + @Override
  228 + public void clean() {
  229 +
152 230 }
153 231 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
... ... @@ -140,11 +140,11 @@ public interface IRedisCatchStorage {
140 140  
141 141 /**
142 142 * 移除流信息从redis
143   - * @param mediaServerItem
  143 + * @param mediaServerId
144 144 * @param app
145 145 * @param streamId
146 146 */
147   - void removeStream(MediaServerItem mediaServerItem, String type, String app, String streamId);
  147 + void removeStream(String mediaServerId, String type, String app, String streamId);
148 148  
149 149  
150 150 /**
... ... @@ -167,4 +167,6 @@ public interface IRedisCatchStorage {
167 167 * @return
168 168 */
169 169 ThirdPartyGB queryMemberNoGBId(String queryKey);
  170 +
  171 + List<StreamInfo> getStreams(String mediaServerId, String pull);
170 172 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java
... ... @@ -65,4 +65,18 @@ public interface GbStreamMapper {
65 65 "SET status=${status} " +
66 66 "WHERE mediaServerId=#{mediaServerId} ")
67 67 void updateStatusByMediaServerId(String mediaServerId, boolean status);
  68 +
  69 + @Select("SELECT * FROM gb_stream WHERE mediaServerId=#{mediaServerId}")
  70 + void delByMediaServerId(String mediaServerId);
  71 +
  72 + @Delete("DELETE FROM gb_stream WHERE streamType=#{type} AND gbId=NULL AND mediaServerId=#{mediaServerId}")
  73 + void deleteWithoutGBId(String type, String mediaServerId);
  74 +
  75 + @Delete("<script> "+
  76 + "DELETE FROM gb_stream where " +
  77 + "<foreach collection='streamProxyItemList' item='item' separator='or'>" +
  78 + "(app=#{item.app} and stream=#{item.stream}) " +
  79 + "</foreach>" +
  80 + "</script>")
  81 + void batchDel(List<StreamProxyItem> streamProxyItemList);
68 82 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java
... ... @@ -62,6 +62,9 @@ public interface StreamProxyMapper {
62 62 "WHERE mediaServerId=#{mediaServerId}")
63 63 void updateStatus(boolean status, String mediaServerId);
64 64  
65   - @Delete("DELETE FROM stream_proxy WHERE mediaServerId=#{mediaServerId}")
  65 + @Delete("DELETE FROM stream_proxy WHERE enable_remove_none_reader=true AND mediaServerId=#{mediaServerId}")
66 66 void deleteAutoRemoveItemByMediaServerId(String mediaServerId);
  67 +
  68 + @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable_remove_none_reader=true AND st.mediaServerId=#{mediaServerId} order by st.createTime desc")
  69 + List<StreamProxyItem> selecAutoRemoveItemByMediaServerId(String mediaServerId);
67 70 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java
... ... @@ -4,6 +4,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
4 4 import org.apache.ibatis.annotations.*;
5 5 import org.springframework.stereotype.Repository;
6 6  
  7 +import java.util.Collection;
7 8 import java.util.List;
8 9  
9 10 @Mapper
... ... @@ -31,6 +32,14 @@ public interface StreamPushMapper {
31 32 @Delete("DELETE FROM stream_push WHERE app=#{app} AND stream=#{stream}")
32 33 int del(String app, String stream);
33 34  
  35 + @Delete("<script> "+
  36 + "DELETE FROM stream_push where " +
  37 + "<foreach collection='streamPushItems' item='item' separator='or'>" +
  38 + "(app=#{item.app} and stream=#{item.stream}) " +
  39 + "</foreach>" +
  40 + "</script>")
  41 + int delAll(List<StreamPushItem> streamPushItems);
  42 +
34 43 @Select("SELECT st.*, pgs.gbId, pgs.status, pgs.name, pgs.longitude, pgs.latitude FROM stream_push st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream")
35 44 List<StreamPushItem> selectAll();
36 45  
... ... @@ -56,4 +65,7 @@ public interface StreamPushMapper {
56 65 @Delete("DELETE FROM stream_push WHERE mediaServerId=#{mediaServerId}")
57 66 void deleteWithoutGBId(String mediaServerId);
58 67  
  68 + @Select("SELECT * FROM stream_push WHERE mediaServerId=#{mediaServerId}")
  69 + List<StreamPushItem> selectAllByMediaServerId(String mediaServerId);
  70 +
59 71 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
... ... @@ -338,8 +338,8 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
338 338 }
339 339  
340 340 @Override
341   - public void removeStream(MediaServerItem mediaServerItem, String type, String app, String streamId) {
342   - String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetup.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerItem.getId();
  341 + public void removeStream(String mediaServerId, String type, String app, String streamId) {
  342 + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetup.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerId;
343 343 redis.del(key);
344 344 }
345 345  
... ... @@ -365,4 +365,16 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
365 365 redis.del((String) stream);
366 366 }
367 367 }
  368 +
  369 + @Override
  370 + public List<StreamInfo> getStreams(String mediaServerId, String type) {
  371 + List<StreamInfo> result = new ArrayList<>();
  372 + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetup.getServerId() + "_" + type + "_*_*_" + mediaServerId;
  373 + List<Object> streams = redis.scan(key);
  374 + for (Object stream : streams) {
  375 + StreamInfo streamInfo = (StreamInfo)redis.get((String) stream);
  376 + result.add(streamInfo);
  377 + }
  378 + return result;
  379 + }
368 380 }
... ...