Commit fd091e545ba174adc36a9d3370e6d4c040ad33fd
1 parent
f84eebdb
优化hook订阅机制
Showing
12 changed files
with
296 additions
and
112 deletions
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| ... | ... | @@ -10,6 +10,9 @@ import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| 10 | 10 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 11 | 11 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| 12 | 12 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; |
| 13 | +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; | |
| 14 | +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; | |
| 15 | +import com.genersoft.iot.vmp.media.zlm.dto.HookType; | |
| 13 | 16 | import com.genersoft.iot.vmp.utils.DateUtil; |
| 14 | 17 | import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; |
| 15 | 18 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
| ... | ... | @@ -348,25 +351,19 @@ public class SIPCommander implements ISIPCommander { |
| 348 | 351 | @Override |
| 349 | 352 | public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, |
| 350 | 353 | ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) { |
| 351 | - String streamId = ssrcInfo.getStream(); | |
| 354 | + String stream = ssrcInfo.getStream(); | |
| 352 | 355 | try { |
| 353 | 356 | if (device == null) { |
| 354 | 357 | return; |
| 355 | 358 | } |
| 356 | 359 | String streamMode = device.getStreamMode().toUpperCase(); |
| 357 | 360 | |
| 358 | - logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); | |
| 359 | - // 添加订阅 | |
| 360 | - JSONObject subscribeKey = new JSONObject(); | |
| 361 | - subscribeKey.put("app", "rtp"); | |
| 362 | - subscribeKey.put("stream", streamId); | |
| 363 | - subscribeKey.put("regist", true); | |
| 364 | - subscribeKey.put("schema", "rtmp"); | |
| 365 | - subscribeKey.put("mediaServerId", mediaServerItem.getId()); | |
| 366 | - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, | |
| 367 | - (MediaServerItem mediaServerItemInUse, JSONObject json)->{ | |
| 361 | + logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); | |
| 362 | + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtmp", mediaServerItem.getId()); | |
| 363 | + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{ | |
| 368 | 364 | if (event != null) { |
| 369 | 365 | event.response(mediaServerItemInUse, json); |
| 366 | + subscribe.removeSubscribe(hookSubscribe); | |
| 370 | 367 | } |
| 371 | 368 | }); |
| 372 | 369 | // |
| ... | ... | @@ -440,7 +437,7 @@ public class SIPCommander implements ISIPCommander { |
| 440 | 437 | errorEvent.response(e); |
| 441 | 438 | }), e ->{ |
| 442 | 439 | // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 |
| 443 | - streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play); | |
| 440 | + streamSession.put(device.getDeviceId(), channelId ,"play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play); | |
| 444 | 441 | streamSession.put(device.getDeviceId(), channelId ,"play", e.dialog); |
| 445 | 442 | okEvent.response(e); |
| 446 | 443 | }); |
| ... | ... | @@ -530,21 +527,14 @@ public class SIPCommander implements ISIPCommander { |
| 530 | 527 | |
| 531 | 528 | CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() |
| 532 | 529 | : udpSipProvider.getNewCallId(); |
| 533 | - | |
| 530 | + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtmp", mediaServerItem.getId()); | |
| 534 | 531 | // 添加订阅 |
| 535 | - JSONObject subscribeKey = new JSONObject(); | |
| 536 | - subscribeKey.put("app", "rtp"); | |
| 537 | - subscribeKey.put("stream", ssrcInfo.getStream()); | |
| 538 | - subscribeKey.put("regist", true); | |
| 539 | - subscribeKey.put("schema", "rtmp"); | |
| 540 | - subscribeKey.put("mediaServerId", mediaServerItem.getId()); | |
| 541 | - logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey); | |
| 542 | - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, | |
| 543 | - (MediaServerItem mediaServerItemInUse, JSONObject json)->{ | |
| 532 | + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{ | |
| 544 | 533 | if (hookEvent != null) { |
| 545 | 534 | InviteStreamInfo inviteStreamInfo = new InviteStreamInfo(mediaServerItemInUse, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()); |
| 546 | 535 | hookEvent.call(inviteStreamInfo); |
| 547 | 536 | } |
| 537 | + subscribe.removeSubscribe(hookSubscribe); | |
| 548 | 538 | }); |
| 549 | 539 | Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc()); |
| 550 | 540 | |
| ... | ... | @@ -643,21 +633,15 @@ public class SIPCommander implements ISIPCommander { |
| 643 | 633 | CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() |
| 644 | 634 | : udpSipProvider.getNewCallId(); |
| 645 | 635 | |
| 636 | + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId()); | |
| 646 | 637 | // 添加订阅 |
| 647 | - JSONObject subscribeKey = new JSONObject(); | |
| 648 | - subscribeKey.put("app", "rtp"); | |
| 649 | - subscribeKey.put("stream", ssrcInfo.getStream()); | |
| 650 | - subscribeKey.put("regist", true); | |
| 651 | - subscribeKey.put("mediaServerId", mediaServerItem.getId()); | |
| 652 | - logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString()); | |
| 653 | - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, | |
| 654 | - (MediaServerItem mediaServerItemInUse, JSONObject json)->{ | |
| 638 | + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{ | |
| 655 | 639 | hookEvent.call(new InviteStreamInfo(mediaServerItem, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream())); |
| 656 | - subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey); | |
| 657 | - subscribeKey.put("regist", false); | |
| 658 | - subscribeKey.put("schema", "rtmp"); | |
| 640 | + subscribe.removeSubscribe(hookSubscribe); | |
| 641 | + hookSubscribe.getContent().put("regist", false); | |
| 642 | + hookSubscribe.getContent().put("schema", "rtmp"); | |
| 659 | 643 | // 添加流注销的订阅,注销了后向设备发送bye |
| 660 | - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, | |
| 644 | + subscribe.addSubscribe(hookSubscribe, | |
| 661 | 645 | (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd)->{ |
| 662 | 646 | ClientTransaction transaction = streamSession.getTransaction(device.getDeviceId(), channelId, ssrcInfo.getStream(), callIdHeader.getCallId()); |
| 663 | 647 | if (transaction != null) { | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
| ... | ... | @@ -102,7 +102,7 @@ public class ZLMHttpHookListener { |
| 102 | 102 | logger.debug("[ ZLM HOOK ] on_server_keepalive API调用,参数:" + json.toString()); |
| 103 | 103 | } |
| 104 | 104 | String mediaServerId = json.getString("mediaServerId"); |
| 105 | - List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_keepalive); | |
| 105 | + List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive); | |
| 106 | 106 | if (subscribes != null && subscribes.size() > 0) { |
| 107 | 107 | for (ZLMHttpHookSubscribe.Event subscribe : subscribes) { |
| 108 | 108 | subscribe.response(null, json); |
| ... | ... | @@ -168,7 +168,7 @@ public class ZLMHttpHookListener { |
| 168 | 168 | logger.debug("[ ZLM HOOK ]on_play API调用,参数:" + JSON.toJSONString(param)); |
| 169 | 169 | } |
| 170 | 170 | String mediaServerId = param.getMediaServerId(); |
| 171 | - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_play, json); | |
| 171 | + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json); | |
| 172 | 172 | if (subscribe != null ) { |
| 173 | 173 | MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); |
| 174 | 174 | if (mediaInfo != null) { |
| ... | ... | @@ -253,7 +253,7 @@ public class ZLMHttpHookListener { |
| 253 | 253 | } |
| 254 | 254 | |
| 255 | 255 | |
| 256 | - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); | |
| 256 | + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json); | |
| 257 | 257 | if (subscribe != null) { |
| 258 | 258 | if (mediaInfo != null) { |
| 259 | 259 | subscribe.response(mediaInfo, json); |
| ... | ... | @@ -377,7 +377,7 @@ public class ZLMHttpHookListener { |
| 377 | 377 | logger.debug("[ ZLM HOOK ]on_shell_login API调用,参数:" + json.toString()); |
| 378 | 378 | } |
| 379 | 379 | String mediaServerId = json.getString("mediaServerId"); |
| 380 | - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_shell_login, json); | |
| 380 | + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_shell_login, json); | |
| 381 | 381 | if (subscribe != null ) { |
| 382 | 382 | MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); |
| 383 | 383 | if (mediaInfo != null) { |
| ... | ... | @@ -403,7 +403,7 @@ public class ZLMHttpHookListener { |
| 403 | 403 | logger.info("[ ZLM HOOK ]on_stream_changed API调用,参数:" + JSONObject.toJSONString(item)); |
| 404 | 404 | String mediaServerId = item.getMediaServerId(); |
| 405 | 405 | JSONObject json = (JSONObject) JSON.toJSON(item); |
| 406 | - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, json); | |
| 406 | + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json); | |
| 407 | 407 | if (subscribe != null ) { |
| 408 | 408 | MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); |
| 409 | 409 | if (mediaInfo != null) { |
| ... | ... | @@ -614,7 +614,7 @@ public class ZLMHttpHookListener { |
| 614 | 614 | } |
| 615 | 615 | String remoteAddr = request.getRemoteAddr(); |
| 616 | 616 | jsonObject.put("ip", remoteAddr); |
| 617 | - List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_started); | |
| 617 | + List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started); | |
| 618 | 618 | if (subscribes != null && subscribes.size() > 0) { |
| 619 | 619 | for (ZLMHttpHookSubscribe.Event subscribe : subscribes) { |
| 620 | 620 | subscribe.response(null, jsonObject); | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
| 1 | 1 | package com.genersoft.iot.vmp.media.zlm; |
| 2 | 2 | |
| 3 | 3 | import com.alibaba.fastjson.JSONObject; |
| 4 | +import com.genersoft.iot.vmp.media.zlm.dto.HookType; | |
| 5 | +import com.genersoft.iot.vmp.media.zlm.dto.IHookSubscribe; | |
| 4 | 6 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 5 | 7 | import org.springframework.stereotype.Component; |
| 6 | 8 | import org.springframework.util.CollectionUtils; |
| 7 | 9 | |
| 10 | +import java.time.Instant; | |
| 8 | 11 | import java.util.*; |
| 9 | 12 | import java.util.concurrent.ConcurrentHashMap; |
| 13 | +import java.util.concurrent.TimeUnit; | |
| 10 | 14 | |
| 11 | 15 | /** |
| 12 | 16 | * @description:针对 ZLMediaServer的hook事件订阅 |
| ... | ... | @@ -16,49 +20,38 @@ import java.util.concurrent.ConcurrentHashMap; |
| 16 | 20 | @Component |
| 17 | 21 | public class ZLMHttpHookSubscribe { |
| 18 | 22 | |
| 19 | - public enum HookType{ | |
| 20 | - on_flow_report, | |
| 21 | - on_http_access, | |
| 22 | - on_play, | |
| 23 | - on_publish, | |
| 24 | - on_record_mp4, | |
| 25 | - on_rtsp_auth, | |
| 26 | - on_rtsp_realm, | |
| 27 | - on_shell_login, | |
| 28 | - on_stream_changed, | |
| 29 | - on_stream_none_reader, | |
| 30 | - on_stream_not_found, | |
| 31 | - on_server_started, | |
| 32 | - on_server_keepalive | |
| 33 | - } | |
| 34 | - | |
| 35 | 23 | @FunctionalInterface |
| 36 | 24 | public interface Event{ |
| 37 | 25 | void response(MediaServerItem mediaServerItem, JSONObject response); |
| 38 | 26 | } |
| 39 | 27 | |
| 40 | - private Map<HookType, Map<JSONObject, ZLMHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>(); | |
| 28 | + private Map<HookType, Map<IHookSubscribe, ZLMHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>(); | |
| 41 | 29 | |
| 42 | - public void addSubscribe(HookType type, JSONObject hookResponse, ZLMHttpHookSubscribe.Event event) { | |
| 43 | - allSubscribes.computeIfAbsent(type, k -> new ConcurrentHashMap<>()).put(hookResponse, event); | |
| 30 | + public void addSubscribe(IHookSubscribe hookSubscribe, ZLMHttpHookSubscribe.Event event) { | |
| 31 | + if (hookSubscribe.getExpires() == null) { | |
| 32 | + // 默认5分钟过期 | |
| 33 | + Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(5)); | |
| 34 | + hookSubscribe.setExpires(expiresInstant); | |
| 35 | + } | |
| 36 | + allSubscribes.computeIfAbsent(hookSubscribe.getHookType(), k -> new ConcurrentHashMap<>()).put(hookSubscribe, event); | |
| 44 | 37 | } |
| 45 | 38 | |
| 46 | - public ZLMHttpHookSubscribe.Event getSubscribe(HookType type, JSONObject hookResponse) { | |
| 39 | + public ZLMHttpHookSubscribe.Event sendNotify(HookType type, JSONObject hookResponse) { | |
| 47 | 40 | ZLMHttpHookSubscribe.Event event= null; |
| 48 | - Map<JSONObject, Event> eventMap = allSubscribes.get(type); | |
| 41 | + Map<IHookSubscribe, Event> eventMap = allSubscribes.get(type); | |
| 49 | 42 | if (eventMap == null) { |
| 50 | 43 | return null; |
| 51 | 44 | } |
| 52 | - for (JSONObject key : eventMap.keySet()) { | |
| 45 | + for (IHookSubscribe key : eventMap.keySet()) { | |
| 53 | 46 | Boolean result = null; |
| 54 | - for (String s : key.keySet()) { | |
| 47 | + for (String s : key.getContent().keySet()) { | |
| 55 | 48 | if (result == null) { |
| 56 | - result = key.getString(s).equals(hookResponse.getString(s)); | |
| 49 | + result = key.getContent().getString(s).equals(hookResponse.getString(s)); | |
| 57 | 50 | }else { |
| 58 | - if (key.getString(s) == null) { | |
| 51 | + if (key.getContent().getString(s) == null) { | |
| 59 | 52 | continue; |
| 60 | 53 | } |
| 61 | - result = result && key.getString(s).equals(hookResponse.getString(s)); | |
| 54 | + result = result && key.getContent().getString(s).equals(hookResponse.getString(s)); | |
| 62 | 55 | } |
| 63 | 56 | |
| 64 | 57 | } |
| ... | ... | @@ -69,26 +62,30 @@ public class ZLMHttpHookSubscribe { |
| 69 | 62 | return event; |
| 70 | 63 | } |
| 71 | 64 | |
| 72 | - public void removeSubscribe(HookType type, JSONObject hookResponse) { | |
| 73 | - Map<JSONObject, Event> eventMap = allSubscribes.get(type); | |
| 65 | + public void removeSubscribe(IHookSubscribe hookSubscribe) { | |
| 66 | + Map<IHookSubscribe, Event> eventMap = allSubscribes.get(hookSubscribe.getHookType()); | |
| 74 | 67 | if (eventMap == null) { |
| 75 | 68 | return; |
| 76 | 69 | } |
| 77 | 70 | |
| 78 | - Set<Map.Entry<JSONObject, Event>> entries = eventMap.entrySet(); | |
| 71 | + Set<Map.Entry<IHookSubscribe, Event>> entries = eventMap.entrySet(); | |
| 79 | 72 | if (entries.size() > 0) { |
| 80 | - List<Map.Entry<JSONObject, ZLMHttpHookSubscribe.Event>> entriesToRemove = new ArrayList<>(); | |
| 81 | - for (Map.Entry<JSONObject, ZLMHttpHookSubscribe.Event> entry : entries) { | |
| 82 | - JSONObject key = entry.getKey(); | |
| 73 | + List<Map.Entry<IHookSubscribe, ZLMHttpHookSubscribe.Event>> entriesToRemove = new ArrayList<>(); | |
| 74 | + for (Map.Entry<IHookSubscribe, ZLMHttpHookSubscribe.Event> entry : entries) { | |
| 75 | + JSONObject content = entry.getKey().getContent(); | |
| 76 | + if (content == null || content.size() == 0) { | |
| 77 | + entriesToRemove.add(entry); | |
| 78 | + continue; | |
| 79 | + } | |
| 83 | 80 | Boolean result = null; |
| 84 | - for (String s : key.keySet()) { | |
| 81 | + for (String s : content.keySet()) { | |
| 85 | 82 | if (result == null) { |
| 86 | - result = key.getString(s).equals(hookResponse.getString(s)); | |
| 83 | + result = content.getString(s).equals(hookSubscribe.getContent().getString(s)); | |
| 87 | 84 | }else { |
| 88 | - if (key.getString(s) == null) { | |
| 85 | + if (content.getString(s) == null) { | |
| 89 | 86 | continue; |
| 90 | 87 | } |
| 91 | - result = result && key.getString(s).equals(hookResponse.getString(s)); | |
| 88 | + result = result && content.getString(s).equals(hookSubscribe.getContent().getString(s)); | |
| 92 | 89 | } |
| 93 | 90 | } |
| 94 | 91 | if (null != result && result){ |
| ... | ... | @@ -97,7 +94,7 @@ public class ZLMHttpHookSubscribe { |
| 97 | 94 | } |
| 98 | 95 | |
| 99 | 96 | if (!CollectionUtils.isEmpty(entriesToRemove)) { |
| 100 | - for (Map.Entry<JSONObject, ZLMHttpHookSubscribe.Event> entry : entriesToRemove) { | |
| 97 | + for (Map.Entry<IHookSubscribe, ZLMHttpHookSubscribe.Event> entry : entriesToRemove) { | |
| 101 | 98 | entries.remove(entry); |
| 102 | 99 | } |
| 103 | 100 | } |
| ... | ... | @@ -111,17 +108,25 @@ public class ZLMHttpHookSubscribe { |
| 111 | 108 | * @return |
| 112 | 109 | */ |
| 113 | 110 | public List<ZLMHttpHookSubscribe.Event> getSubscribes(HookType type) { |
| 114 | - // ZLMHttpHookSubscribe.Event event= null; | |
| 115 | - Map<JSONObject, Event> eventMap = allSubscribes.get(type); | |
| 111 | + Map<IHookSubscribe, Event> eventMap = allSubscribes.get(type); | |
| 116 | 112 | if (eventMap == null) { |
| 117 | 113 | return null; |
| 118 | 114 | } |
| 119 | 115 | List<ZLMHttpHookSubscribe.Event> result = new ArrayList<>(); |
| 120 | - for (JSONObject key : eventMap.keySet()) { | |
| 116 | + for (IHookSubscribe key : eventMap.keySet()) { | |
| 121 | 117 | result.add(eventMap.get(key)); |
| 122 | 118 | } |
| 123 | 119 | return result; |
| 124 | 120 | } |
| 125 | 121 | |
| 122 | + public List<IHookSubscribe> getAll(){ | |
| 123 | + ArrayList<IHookSubscribe> result = new ArrayList<>(); | |
| 124 | + Collection<Map<IHookSubscribe, Event>> values = allSubscribes.values(); | |
| 125 | + for (Map<IHookSubscribe, Event> value : values) { | |
| 126 | + result.addAll(value.keySet()); | |
| 127 | + } | |
| 128 | + return result; | |
| 129 | + } | |
| 130 | + | |
| 126 | 131 | |
| 127 | 132 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
| ... | ... | @@ -6,22 +6,22 @@ import com.alibaba.fastjson.JSONObject; |
| 6 | 6 | import com.genersoft.iot.vmp.conf.DynamicTask; |
| 7 | 7 | import com.genersoft.iot.vmp.conf.MediaConfig; |
| 8 | 8 | import com.genersoft.iot.vmp.gb28181.event.EventPublisher; |
| 9 | +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; | |
| 10 | +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForServerStarted; | |
| 11 | +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; | |
| 9 | 12 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 10 | 13 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 11 | -import com.genersoft.iot.vmp.service.IStreamProxyService; | |
| 12 | -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 13 | 14 | import org.slf4j.Logger; |
| 14 | 15 | import org.slf4j.LoggerFactory; |
| 15 | 16 | import org.springframework.beans.factory.annotation.Autowired; |
| 16 | -import org.springframework.beans.factory.annotation.Qualifier; | |
| 17 | 17 | import org.springframework.boot.CommandLineRunner; |
| 18 | 18 | import org.springframework.core.annotation.Order; |
| 19 | 19 | import org.springframework.scheduling.annotation.Async; |
| 20 | -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |
| 21 | 20 | import org.springframework.stereotype.Component; |
| 22 | -import org.springframework.util.StringUtils; | |
| 23 | 21 | |
| 22 | +import java.time.Instant; | |
| 24 | 23 | import java.util.*; |
| 24 | +import java.util.concurrent.TimeUnit; | |
| 25 | 25 | |
| 26 | 26 | @Component |
| 27 | 27 | @Order(value=1) |
| ... | ... | @@ -38,18 +38,12 @@ public class ZLMRunner implements CommandLineRunner { |
| 38 | 38 | private ZLMHttpHookSubscribe hookSubscribe; |
| 39 | 39 | |
| 40 | 40 | @Autowired |
| 41 | - private IStreamProxyService streamProxyService; | |
| 42 | - | |
| 43 | - @Autowired | |
| 44 | 41 | private EventPublisher publisher; |
| 45 | 42 | |
| 46 | 43 | @Autowired |
| 47 | 44 | private IMediaServerService mediaServerService; |
| 48 | 45 | |
| 49 | 46 | @Autowired |
| 50 | - private IRedisCatchStorage redisCatchStorage; | |
| 51 | - | |
| 52 | - @Autowired | |
| 53 | 47 | private MediaConfig mediaConfig; |
| 54 | 48 | |
| 55 | 49 | @Autowired |
| ... | ... | @@ -67,17 +61,25 @@ public class ZLMRunner implements CommandLineRunner { |
| 67 | 61 | mediaServerService.updateToDatabase(mediaSerItem); |
| 68 | 62 | } |
| 69 | 63 | mediaServerService.syncCatchFromDatabase(); |
| 64 | + HookSubscribeForServerStarted hookSubscribeForServerStarted = HookSubscribeFactory.on_server_started(); | |
| 65 | +// Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.SECONDS.toSeconds(60)); | |
| 66 | +// hookSubscribeForStreamChange.setExpires(expiresInstant); | |
| 70 | 67 | // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 |
| 71 | - hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,new JSONObject(), | |
| 68 | + hookSubscribe.addSubscribe(hookSubscribeForServerStarted, | |
| 72 | 69 | (MediaServerItem mediaServerItem, JSONObject response)->{ |
| 73 | 70 | ZLMServerConfig zlmServerConfig = JSONObject.toJavaObject(response, ZLMServerConfig.class); |
| 74 | 71 | if (zlmServerConfig !=null ) { |
| 75 | 72 | if (startGetMedia != null) { |
| 76 | 73 | startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId()); |
| 74 | + if (startGetMedia.size() == 0) { | |
| 75 | + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); | |
| 76 | + } | |
| 77 | 77 | } |
| 78 | 78 | } |
| 79 | 79 | }); |
| 80 | 80 | |
| 81 | + | |
| 82 | + | |
| 81 | 83 | // 获取zlm信息 |
| 82 | 84 | logger.info("[zlm] 等待默认zlm中..."); |
| 83 | 85 | |
| ... | ... | @@ -103,7 +105,6 @@ public class ZLMRunner implements CommandLineRunner { |
| 103 | 105 | } |
| 104 | 106 | startGetMedia = null; |
| 105 | 107 | } |
| 106 | - hookSubscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started, new JSONObject()); | |
| 107 | 108 | // TODO 清理数据库中与redis不匹配的zlm |
| 108 | 109 | }, 60 * 1000 ); |
| 109 | 110 | } |
| ... | ... | @@ -116,6 +117,9 @@ public class ZLMRunner implements CommandLineRunner { |
| 116 | 117 | zlmServerConfigFirst.setIp(mediaServerItem.getIp()); |
| 117 | 118 | zlmServerConfigFirst.setHttpPort(mediaServerItem.getHttpPort()); |
| 118 | 119 | startGetMedia.remove(mediaServerItem.getId()); |
| 120 | + if (startGetMedia.size() == 0) { | |
| 121 | + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); | |
| 122 | + } | |
| 119 | 123 | mediaServerService.zlmServerOnline(zlmServerConfigFirst); |
| 120 | 124 | }else { |
| 121 | 125 | logger.info("[ {} ]-[ {}:{} ]主动连接失败, 清理相关资源, 开始尝试重试连接", |
| ... | ... | @@ -130,6 +134,9 @@ public class ZLMRunner implements CommandLineRunner { |
| 130 | 134 | zlmServerConfig.setIp(mediaServerItem.getIp()); |
| 131 | 135 | zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort()); |
| 132 | 136 | startGetMedia.remove(mediaServerItem.getId()); |
| 137 | + if (startGetMedia.size() == 0) { | |
| 138 | + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); | |
| 139 | + } | |
| 133 | 140 | mediaServerService.zlmServerOnline(zlmServerConfig); |
| 134 | 141 | } |
| 135 | 142 | }, 2000); | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.media.zlm.dto; | |
| 2 | + | |
| 3 | + | |
| 4 | +import com.alibaba.fastjson.JSONObject; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * hook 订阅工厂 | |
| 8 | + * @author lin | |
| 9 | + */ | |
| 10 | +public class HookSubscribeFactory { | |
| 11 | + | |
| 12 | + public static HookSubscribeForStreamChange on_stream_changed(String app, String stream, boolean regist, String scheam, String mediaServerId) { | |
| 13 | + HookSubscribeForStreamChange hookSubscribe = new HookSubscribeForStreamChange(); | |
| 14 | + JSONObject subscribeKey = new com.alibaba.fastjson.JSONObject(); | |
| 15 | + subscribeKey.put("app", app); | |
| 16 | + subscribeKey.put("stream", stream); | |
| 17 | + subscribeKey.put("regist", regist); | |
| 18 | + if (scheam != null) { | |
| 19 | + subscribeKey.put("schema", scheam); | |
| 20 | + } | |
| 21 | + subscribeKey.put("mediaServerId", mediaServerId); | |
| 22 | + hookSubscribe.setContent(subscribeKey); | |
| 23 | + | |
| 24 | + return hookSubscribe; | |
| 25 | + } | |
| 26 | + | |
| 27 | + public static HookSubscribeForServerStarted on_server_started() { | |
| 28 | + HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted(); | |
| 29 | + hookSubscribe.setContent(new JSONObject()); | |
| 30 | + | |
| 31 | + return hookSubscribe; | |
| 32 | + } | |
| 33 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForServerStarted.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.media.zlm.dto; | |
| 2 | + | |
| 3 | +import com.alibaba.fastjson.JSONObject; | |
| 4 | +import com.alibaba.fastjson.annotation.JSONField; | |
| 5 | + | |
| 6 | +import java.time.Instant; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * hook订阅-流变化 | |
| 10 | + * @author lin | |
| 11 | + */ | |
| 12 | +public class HookSubscribeForServerStarted implements IHookSubscribe{ | |
| 13 | + | |
| 14 | + private HookType hookType = HookType.on_server_started; | |
| 15 | + | |
| 16 | + private JSONObject content; | |
| 17 | + | |
| 18 | + @JSONField(format="yyyy-MM-dd HH:mm:ss") | |
| 19 | + private Instant expires; | |
| 20 | + | |
| 21 | + @Override | |
| 22 | + public HookType getHookType() { | |
| 23 | + return hookType; | |
| 24 | + } | |
| 25 | + | |
| 26 | + @Override | |
| 27 | + public JSONObject getContent() { | |
| 28 | + return content; | |
| 29 | + } | |
| 30 | + | |
| 31 | + public void setContent(JSONObject content) { | |
| 32 | + this.content = content; | |
| 33 | + } | |
| 34 | + | |
| 35 | + @Override | |
| 36 | + public Instant getExpires() { | |
| 37 | + return expires; | |
| 38 | + } | |
| 39 | + | |
| 40 | + @Override | |
| 41 | + public void setExpires(Instant expires) { | |
| 42 | + this.expires = expires; | |
| 43 | + } | |
| 44 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.media.zlm.dto; | |
| 2 | + | |
| 3 | +import com.alibaba.fastjson.JSONObject; | |
| 4 | +import com.alibaba.fastjson.annotation.JSONField; | |
| 5 | + | |
| 6 | +import java.time.Instant; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * hook订阅-流变化 | |
| 10 | + * @author lin | |
| 11 | + */ | |
| 12 | +public class HookSubscribeForStreamChange implements IHookSubscribe{ | |
| 13 | + | |
| 14 | + private HookType hookType = HookType.on_stream_changed; | |
| 15 | + | |
| 16 | + private JSONObject content; | |
| 17 | + | |
| 18 | + private Instant expires; | |
| 19 | + | |
| 20 | + @Override | |
| 21 | + public HookType getHookType() { | |
| 22 | + return hookType; | |
| 23 | + } | |
| 24 | + | |
| 25 | + @Override | |
| 26 | + public JSONObject getContent() { | |
| 27 | + return content; | |
| 28 | + } | |
| 29 | + | |
| 30 | + public void setContent(JSONObject content) { | |
| 31 | + this.content = content; | |
| 32 | + } | |
| 33 | + | |
| 34 | + @Override | |
| 35 | + public Instant getExpires() { | |
| 36 | + return expires; | |
| 37 | + } | |
| 38 | + | |
| 39 | + @Override | |
| 40 | + public void setExpires(Instant expires) { | |
| 41 | + this.expires = expires; | |
| 42 | + } | |
| 43 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.media.zlm.dto; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * hook类型 | |
| 5 | + * @author lin | |
| 6 | + */ | |
| 7 | + | |
| 8 | +public enum HookType { | |
| 9 | + | |
| 10 | + on_flow_report, | |
| 11 | + on_http_access, | |
| 12 | + on_play, | |
| 13 | + on_publish, | |
| 14 | + on_record_mp4, | |
| 15 | + on_rtsp_auth, | |
| 16 | + on_rtsp_realm, | |
| 17 | + on_shell_login, | |
| 18 | + on_stream_changed, | |
| 19 | + on_stream_none_reader, | |
| 20 | + on_stream_not_found, | |
| 21 | + on_server_started, | |
| 22 | + on_server_keepalive | |
| 23 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IHookSubscribe.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.media.zlm.dto; | |
| 2 | + | |
| 3 | +import com.alibaba.fastjson.JSONObject; | |
| 4 | + | |
| 5 | +import java.time.Instant; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * zlm hook事件的参数 | |
| 9 | + * @author lin | |
| 10 | + */ | |
| 11 | +public interface IHookSubscribe { | |
| 12 | + | |
| 13 | + /** | |
| 14 | + * 获取hook类型 | |
| 15 | + * @return hook类型 | |
| 16 | + */ | |
| 17 | + HookType getHookType(); | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 获取hook的具体内容 | |
| 21 | + * @return hook的具体内容 | |
| 22 | + */ | |
| 23 | + JSONObject getContent(); | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 设置过期时间 | |
| 27 | + * @param instant 过期时间 | |
| 28 | + */ | |
| 29 | + void setExpires(Instant instant); | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * 获取过期时间 | |
| 33 | + * @return 过期时间 | |
| 34 | + */ | |
| 35 | + Instant getExpires(); | |
| 36 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
| ... | ... | @@ -13,6 +13,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 13 | 13 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| 14 | 14 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| 15 | 15 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; |
| 16 | +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; | |
| 17 | +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; | |
| 18 | +import com.genersoft.iot.vmp.media.zlm.dto.HookType; | |
| 16 | 19 | import com.genersoft.iot.vmp.utils.DateUtil; |
| 17 | 20 | import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils; |
| 18 | 21 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
| ... | ... | @@ -296,16 +299,10 @@ public class PlayServiceImpl implements IPlayService { |
| 296 | 299 | // 单端口模式streamId也有变化,需要重新设置监听 |
| 297 | 300 | if (!mediaServerItem.isRtpEnable()) { |
| 298 | 301 | // 添加订阅 |
| 299 | - JSONObject subscribeKey = new JSONObject(); | |
| 300 | - subscribeKey.put("app", "rtp"); | |
| 301 | - subscribeKey.put("stream", stream); | |
| 302 | - subscribeKey.put("regist", true); | |
| 303 | - subscribeKey.put("schema", "rtmp"); | |
| 304 | - subscribeKey.put("mediaServerId", mediaServerItem.getId()); | |
| 305 | - subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed,subscribeKey); | |
| 306 | - subscribeKey.put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); | |
| 307 | - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, | |
| 308 | - (MediaServerItem mediaServerItemInUse, JSONObject response)->{ | |
| 302 | + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtmp", mediaServerItem.getId()); | |
| 303 | + subscribe.removeSubscribe(hookSubscribe); | |
| 304 | + hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); | |
| 305 | + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response)->{ | |
| 309 | 306 | logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); |
| 310 | 307 | dynamicTask.stop(timeOutTaskKey); |
| 311 | 308 | // hook响应 | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/RedisGbPlayMsgListener.java
| ... | ... | @@ -8,6 +8,9 @@ import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; |
| 8 | 8 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
| 9 | 9 | import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager; |
| 10 | 10 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| 11 | +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; | |
| 12 | +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; | |
| 13 | +import com.genersoft.iot.vmp.media.zlm.dto.HookType; | |
| 11 | 14 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 12 | 15 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 13 | 16 | import com.genersoft.iot.vmp.service.bean.*; |
| ... | ... | @@ -270,14 +273,9 @@ public class RedisGbPlayMsgListener implements MessageListener { |
| 270 | 273 | }, userSetting.getPlatformPlayTimeout()); |
| 271 | 274 | |
| 272 | 275 | // 添加订阅 |
| 273 | - JSONObject subscribeKey = new JSONObject(); | |
| 274 | - subscribeKey.put("app", content.getApp()); | |
| 275 | - subscribeKey.put("stream", content.getStream()); | |
| 276 | - subscribeKey.put("regist", true); | |
| 277 | - subscribeKey.put("schema", "rtmp"); | |
| 278 | - subscribeKey.put("mediaServerId", mediaServerItem.getId()); | |
| 279 | - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, | |
| 280 | - (MediaServerItem mediaServerItemInUse, JSONObject json)->{ | |
| 276 | + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(content.getApp(), content.getStream(), true, "rtmp", mediaServerItem.getId()); | |
| 277 | + | |
| 278 | + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{ | |
| 281 | 279 | dynamicTask.stop(taskKey); |
| 282 | 280 | responseSendItem(mediaServerItem, content, toId, serial); |
| 283 | 281 | }); | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
| ... | ... | @@ -8,6 +8,8 @@ import com.genersoft.iot.vmp.conf.DynamicTask; |
| 8 | 8 | import com.genersoft.iot.vmp.conf.SipConfig; |
| 9 | 9 | import com.genersoft.iot.vmp.conf.UserSetting; |
| 10 | 10 | import com.genersoft.iot.vmp.conf.VersionInfo; |
| 11 | +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; | |
| 12 | +import com.genersoft.iot.vmp.media.zlm.dto.IHookSubscribe; | |
| 11 | 13 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 12 | 14 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 13 | 15 | import com.genersoft.iot.vmp.utils.SpringBeanFactory; |
| ... | ... | @@ -38,7 +40,7 @@ import java.util.Set; |
| 38 | 40 | public class ServerController { |
| 39 | 41 | |
| 40 | 42 | @Autowired |
| 41 | - private ConfigurableApplicationContext context; | |
| 43 | + private ZLMHttpHookSubscribe zlmHttpHookSubscribe; | |
| 42 | 44 | |
| 43 | 45 | @Autowired |
| 44 | 46 | private IMediaServerService mediaServerService; |
| ... | ... | @@ -254,6 +256,18 @@ public class ServerController { |
| 254 | 256 | return result; |
| 255 | 257 | } |
| 256 | 258 | |
| 259 | + @ApiOperation("获取当前所有hook") | |
| 260 | + @GetMapping(value = "/hooks") | |
| 261 | + @ResponseBody | |
| 262 | + public WVPResult<List<IHookSubscribe>> getHooks(){ | |
| 263 | + WVPResult<List<IHookSubscribe>> result = new WVPResult<>(); | |
| 264 | + result.setCode(0); | |
| 265 | + result.setMsg("success"); | |
| 266 | + List<IHookSubscribe> all = zlmHttpHookSubscribe.getAll(); | |
| 267 | + result.setData(all); | |
| 268 | + return result; | |
| 269 | + } | |
| 270 | + | |
| 257 | 271 | // @ApiOperation("当前进行中的动态任务") |
| 258 | 272 | // @GetMapping(value = "/dynamicTask") |
| 259 | 273 | // @ResponseBody | ... | ... |