Commit def56793ba7c636fbbcabad38e3ac113a3764087
1 parent
a77628e8
增加上级推流和停止推流的通知
Showing
9 changed files
with
151 additions
and
14 deletions
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 | } | ... | ... |