Commit def56793ba7c636fbbcabad38e3ac113a3764087

Authored by 648540858
1 parent a77628e8

增加上级推流和停止推流的通知

src/main/java/com/genersoft/iot/vmp/conf/redis/RedisMsgListenConfig.java
... ... @@ -63,6 +63,7 @@ public class RedisMsgListenConfig {
63 63 container.addMessageListener(redisPushStreamStatusMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_STATUS_CHANGE));
64 64 container.addMessageListener(redisPushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE));
65 65 container.addMessageListener(redisPushStreamResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE));
  66 +// container.addMessageListener(, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE_REQUESTED));
66 67 return container;
67 68 }
68 69 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
... ... @@ -3,6 +3,8 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
3 3 import com.alibaba.fastjson2.JSON;
4 4 import com.alibaba.fastjson2.JSONObject;
5 5 import com.genersoft.iot.vmp.conf.DynamicTask;
  6 +import com.genersoft.iot.vmp.conf.UserSetting;
  7 +import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType;
6 8 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
7 9 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
8 10 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
... ... @@ -14,6 +16,7 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
14 16 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
15 17 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
16 18 import com.genersoft.iot.vmp.service.IMediaServerService;
  19 +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
17 20 import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
18 21 import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
19 22 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
... ... @@ -58,6 +61,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
58 61 private IRedisCatchStorage redisCatchStorage;
59 62  
60 63 @Autowired
  64 + private UserSetting userSetting;
  65 +
  66 + @Autowired
61 67 private IVideoManagerStorage storager;
62 68  
63 69 @Autowired
... ... @@ -155,6 +161,13 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
155 161 } else if (jsonObject.getInteger("code") == 0) {
156 162 logger.info("调用ZLM推流接口, 结果: {}", jsonObject);
157 163 logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port"));
  164 + if (sendRtpItem.getPlayType() == InviteStreamType.PUSH) {
  165 + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStreamId(),
  166 + sendRtpItem.getChannelId(), parentPlatform.getServerGBId(), parentPlatform.getName(), userSetting.getServerId(),
  167 + sendRtpItem.getMediaServerId());
  168 + messageForPushChannel.setPlatFormIndex(parentPlatform.getId());
  169 + redisCatchStorage.sendPlatformStartPlayMsg(messageForPushChannel);
  170 + }
158 171 } else {
159 172 logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param));
160 173 if (sendRtpItem.isOnlyAudio()) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
2 2  
3 3 import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.conf.UserSetting;
4 5 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
5   -import com.genersoft.iot.vmp.gb28181.bean.Device;
6   -import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType;
7   -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
8   -import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
  6 +import com.genersoft.iot.vmp.gb28181.bean.*;
9 7 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
10 8 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
11 9 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
... ... @@ -15,6 +13,7 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
15 13 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
16 14 import com.genersoft.iot.vmp.service.IDeviceService;
17 15 import com.genersoft.iot.vmp.service.IMediaServerService;
  16 +import com.genersoft.iot.vmp.service.IPlatformService;
18 17 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
19 18 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
20 19 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
... ... @@ -25,7 +24,9 @@ import org.springframework.beans.factory.InitializingBean;
25 24 import org.springframework.beans.factory.annotation.Autowired;
26 25 import org.springframework.stereotype.Component;
27 26  
28   -import javax.sip.*;
  27 +import javax.sip.InvalidArgumentException;
  28 +import javax.sip.RequestEvent;
  29 +import javax.sip.SipException;
29 30 import javax.sip.address.SipURI;
30 31 import javax.sip.header.CallIdHeader;
31 32 import javax.sip.header.FromHeader;
... ... @@ -52,6 +53,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
52 53 private IRedisCatchStorage redisCatchStorage;
53 54  
54 55 @Autowired
  56 + private IPlatformService platformService;
  57 +
  58 + @Autowired
55 59 private IDeviceService deviceService;
56 60  
57 61 @Autowired
... ... @@ -69,6 +73,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
69 73 @Autowired
70 74 private VideoStreamSessionManager streamSession;
71 75  
  76 + @Autowired
  77 + private UserSetting userSetting;
  78 +
72 79 @Override
73 80 public void afterPropertiesSet() throws Exception {
74 81 // 添加消息处理的订阅
... ... @@ -103,6 +110,19 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
103 110 MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
104 111 redisCatchStorage.deleteSendRTPServer(platformGbId, channelId, callIdHeader.getCallId(), null);
105 112 zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
  113 + if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
  114 + ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId());
  115 + if (platform != null) {
  116 + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
  117 + sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(),
  118 + sendRtpItem.getPlatformId(), platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
  119 + messageForPushChannel.setPlatFormIndex(platform.getId());
  120 + redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
  121 + }else {
  122 + logger.info("[上级平台停止观看] 未找到平台{}的信息,发送redis消息失败", sendRtpItem.getPlatformId());
  123 + }
  124 + }
  125 +
106 126 int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId);
107 127 if (totalReaderCount <= 0) {
108 128 logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId);
... ... @@ -119,12 +139,12 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
119 139 logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage());
120 140 }
121 141 }
122   - if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
123   - MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
124   - sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(),
125   - sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId());
126   - redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
127   - }
  142 +// if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
  143 +// MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
  144 +// sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(),
  145 +// sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId());
  146 +// redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
  147 +// }
128 148 }
129 149 }
130 150 // 可能是设备主动停止
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -19,6 +19,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
19 19 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
20 20 import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
21 21 import com.genersoft.iot.vmp.service.*;
  22 +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
22 23 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
23 24 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
24 25 import com.genersoft.iot.vmp.vmanager.bean.DeferredResultEx;
... ... @@ -463,6 +464,13 @@ public class ZLMHttpHookListener {
463 464 }
464 465 redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
465 466 sendRtpItem.getCallId(), sendRtpItem.getStreamId());
  467 + if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) {
  468 + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
  469 + sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(),
  470 + sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
  471 + messageForPushChannel.setPlatFormIndex(parentPlatform.getId());
  472 + redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
  473 + }
466 474 }
467 475 }
468 476 }
... ...
src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java
... ... @@ -34,7 +34,7 @@ public class MessageForPushChannel {
34 34 /**
35 35 * 请求的平台自增ID
36 36 */
37   - private String platFormIndex;
  37 + private int platFormIndex;
38 38  
39 39 /**
40 40 * 请求平台名称
... ... @@ -132,11 +132,11 @@ public class MessageForPushChannel {
132 132 this.mediaServerId = mediaServerId;
133 133 }
134 134  
135   - public String getPlatFormIndex() {
  135 + public int getPlatFormIndex() {
136 136 return platFormIndex;
137 137 }
138 138  
139   - public void setPlatFormIndex(String platFormIndex) {
  139 + public void setPlatFormIndex(int platFormIndex) {
140 140 this.platFormIndex = platFormIndex;
141 141 }
142 142 }
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
... ... @@ -129,6 +129,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
129 129 case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
130 130 RequestPushStreamMsg param = JSON.to(RequestPushStreamMsg.class, wvpRedisMsg.getContent());
131 131 requestPushStreamMsgHand(param, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
  132 +
132 133 break;
133 134 default:
134 135 break;
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamCloseResponseListener.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.redisMsg;
  2 +
  3 +import com.alibaba.fastjson2.JSON;
  4 +import com.genersoft.iot.vmp.service.bean.MessageForPushChannelResponse;
  5 +import org.slf4j.Logger;
  6 +import org.slf4j.LoggerFactory;
  7 +import org.springframework.beans.factory.annotation.Autowired;
  8 +import org.springframework.beans.factory.annotation.Qualifier;
  9 +import org.springframework.data.redis.connection.Message;
  10 +import org.springframework.data.redis.connection.MessageListener;
  11 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  12 +import org.springframework.stereotype.Component;
  13 +import org.springframework.util.ObjectUtils;
  14 +
  15 +import java.util.Map;
  16 +import java.util.concurrent.ConcurrentHashMap;
  17 +import java.util.concurrent.ConcurrentLinkedQueue;
  18 +
  19 +/**
  20 + * 接收redis发送的结束推流请求
  21 + * @author lin
  22 + */
  23 +@Component
  24 +public class RedisPushStreamCloseResponseListener implements MessageListener {
  25 +
  26 + private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamCloseResponseListener.class);
  27 +
  28 + private ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
  29 +
  30 + @Qualifier("taskExecutor")
  31 + @Autowired
  32 + private ThreadPoolTaskExecutor taskExecutor;
  33 +
  34 +
  35 + private Map<String, PushStreamResponseEvent> responseEvents = new ConcurrentHashMap<>();
  36 +
  37 + public interface PushStreamResponseEvent{
  38 + void run(MessageForPushChannelResponse response);
  39 + }
  40 +
  41 + @Override
  42 + public void onMessage(Message message, byte[] bytes) {
  43 + logger.info("[REDIS消息-请求推流结果]: {}", new String(message.getBody()));
  44 + boolean isEmpty = taskQueue.isEmpty();
  45 + taskQueue.offer(message);
  46 + if (isEmpty) {
  47 + taskExecutor.execute(() -> {
  48 + while (!taskQueue.isEmpty()) {
  49 + Message msg = taskQueue.poll();
  50 + try {
  51 + MessageForPushChannelResponse response = JSON.parseObject(new String(msg.getBody()), MessageForPushChannelResponse.class);
  52 + if (response == null || ObjectUtils.isEmpty(response.getApp()) || ObjectUtils.isEmpty(response.getStream())){
  53 + logger.info("[REDIS消息-请求推流结果]:参数不全");
  54 + continue;
  55 + }
  56 + // 查看正在等待的invite消息
  57 + if (responseEvents.get(response.getApp() + response.getStream()) != null) {
  58 + responseEvents.get(response.getApp() + response.getStream()).run(response);
  59 + }
  60 + }catch (Exception e) {
  61 + logger.warn("[REDIS消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
  62 + logger.error("[REDIS消息-请求推流结果] 异常内容: ", e);
  63 + }
  64 + }
  65 + });
  66 + }
  67 + }
  68 +
  69 + public void addEvent(String app, String stream, PushStreamResponseEvent callback) {
  70 + responseEvents.put(app + stream, callback);
  71 + }
  72 +
  73 + public void removeEvent(String app, String stream) {
  74 + responseEvents.remove(app + stream);
  75 + }
  76 +}
... ...
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
... ... @@ -263,4 +263,8 @@ public interface IRedisCatchStorage {
263 263 void removeAllDevice();
264 264  
265 265 void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online);
  266 +
  267 + void sendPlatformStartPlayMsg(MessageForPushChannel messageForPushChannel);
  268 +
  269 + void sendPlatformStopPlayMsg(MessageForPushChannel messageForPushChannel);
266 270 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
... ... @@ -925,4 +925,18 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
925 925 // 使用 RedisTemplate<Object, Object> 发送字符串消息会导致发送的消息多带了双引号
926 926 stringRedisTemplate.convertAndSend(key, msg.toString());
927 927 }
  928 +
  929 + @Override
  930 + public void sendPlatformStartPlayMsg(MessageForPushChannel msg) {
  931 + String key = VideoManagerConstants.VM_MSG_STREAM_START_PLAY_NOTIFY;
  932 + logger.info("[redis发送通知] 推流被上级平台观看 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId());
  933 + redisTemplate.convertAndSend(key, JSON.toJSON(msg));
  934 + }
  935 +
  936 + @Override
  937 + public void sendPlatformStopPlayMsg(MessageForPushChannel msg) {
  938 + String key = VideoManagerConstants.VM_MSG_STREAM_STOP_PLAY_NOTIFY;
  939 + logger.info("[redis发送通知] 上级平台停止观看 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId());
  940 + redisTemplate.convertAndSend(key, JSON.toJSON(msg));
  941 + }
928 942 }
... ...