Преглед на файлове

Merge branch 'refs/heads/master' into develop-add-api-key

leesam преди 1 година
родител
ревизия
16b7e4a7ef
променени са 100 файла, в които са добавени 4696 реда и са изтрити 3534 реда
  1. 1 1
      pom.xml
  2. 27 7
      src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
  3. 2 2
      src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
  4. 7 15
      src/main/java/com/genersoft/iot/vmp/conf/CloudRecordTimer.java
  5. 67 29
      src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
  6. 12 12
      src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
  7. 0 4
      src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java
  8. 1 1
      src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
  9. 5 5
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java
  10. 5 5
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamInfo.java
  11. 7 8
      src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
  12. 5 0
      src/main/java/com/genersoft/iot/vmp/gb28181/session/AudioBroadcastManager.java
  13. 5 15
      src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java
  14. 8 8
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
  15. 4 4
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
  16. 36 45
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
  17. 14 15
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
  18. 30 58
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
  19. 6 5
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
  20. 38 34
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
  21. 16 16
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java
  22. 14 17
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java
  23. 6 7
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java
  24. 64 0
      src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java
  25. 306 0
      src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java
  26. 63 5
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
  27. 92 0
      src/main/java/com/genersoft/iot/vmp/media/bean/RecordInfo.java
  28. 59 0
      src/main/java/com/genersoft/iot/vmp/media/bean/ResultForOnPublish.java
  29. 86 0
      src/main/java/com/genersoft/iot/vmp/media/event/hook/Hook.java
  30. 132 0
      src/main/java/com/genersoft/iot/vmp/media/event/hook/HookData.java
  31. 107 0
      src/main/java/com/genersoft/iot/vmp/media/event/hook/HookSubscribe.java
  32. 15 0
      src/main/java/com/genersoft/iot/vmp/media/event/hook/HookType.java
  33. 46 0
      src/main/java/com/genersoft/iot/vmp/media/event/media/MediaArrivalEvent.java
  34. 22 0
      src/main/java/com/genersoft/iot/vmp/media/event/media/MediaDepartureEvent.java
  35. 56 0
      src/main/java/com/genersoft/iot/vmp/media/event/media/MediaEvent.java
  36. 22 0
      src/main/java/com/genersoft/iot/vmp/media/event/media/MediaNotFoundEvent.java
  37. 33 0
      src/main/java/com/genersoft/iot/vmp/media/event/media/MediaPublishEvent.java
  38. 35 0
      src/main/java/com/genersoft/iot/vmp/media/event/media/MediaRecordMp4Event.java
  39. 22 0
      src/main/java/com/genersoft/iot/vmp/media/event/media/MediaRtpServerTimeoutEvent.java
  40. 52 0
      src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaSendRtpStoppedEvent.java
  41. 34 0
      src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerChangeEvent.java
  42. 11 0
      src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerDeleteEvent.java
  43. 3 4
      src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMEventAbstract.java
  44. 11 0
      src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerOfflineEvent.java
  45. 11 0
      src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerOnlineEvent.java
  46. 7 12
      src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java
  47. 61 0
      src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java
  48. 145 0
      src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java
  49. 837 0
      src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java
  50. 6 6
      src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
  51. 3 2
      src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java
  52. 100 647
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
  53. 6 11
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
  54. 349 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java
  55. 306 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaServerStatusManger.java
  56. 34 36
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
  57. 0 173
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
  58. 18 17
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java
  59. 0 161
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZlmHttpHookSubscribe.java
  60. 0 71
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java
  61. 0 44
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForRecordMp4.java
  62. 0 44
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForRtpServerTimeout.java
  63. 0 44
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForServerStarted.java
  64. 0 44
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java
  65. 0 43
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamPush.java
  66. 0 26
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java
  67. 0 36
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IHookSubscribe.java
  68. 0 168
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItemLite.java
  69. 13 12
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamAuthorityInfo.java
  70. 43 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java
  71. 1 1
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java
  72. 2 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookParam.java
  73. 2 2
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResult.java
  74. 13 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
  75. 3 3
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamChangedHookParam.java
  76. 24 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/event/HookZlmServerKeepaliveEvent.java
  77. 24 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/event/HookZlmServerStartEvent.java
  78. 0 11
      src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOfflineEvent.java
  79. 0 11
      src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOnlineEvent.java
  80. 4 10
      src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java
  81. 0 100
      src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
  82. 6 31
      src/main/java/com/genersoft/iot/vmp/service/IMediaService.java
  83. 4 4
      src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java
  84. 11 12
      src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
  85. 10 5
      src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java
  86. 1 4
      src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java
  87. 10 9
      src/main/java/com/genersoft/iot/vmp/service/bean/CloudRecordItem.java
  88. 4 4
      src/main/java/com/genersoft/iot/vmp/service/bean/PlayBackResult.java
  89. 4 4
      src/main/java/com/genersoft/iot/vmp/service/bean/ResponseSendItemMsg.java
  90. 19 16
      src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java
  91. 5 12
      src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
  92. 34 0
      src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java
  93. 0 756
      src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
  94. 286 64
      src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
  95. 82 47
      src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
  96. 291 263
      src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
  97. 137 132
      src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
  98. 176 96
      src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
  99. 17 18
      src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
  100. 0 0
      src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamCloseResponseListener.java

+ 1 - 1
pom.xml

@@ -11,7 +11,7 @@
 
     <groupId>com.genersoft</groupId>
     <artifactId>wvp-pro</artifactId>
-    <version>2.7.0</version>
+    <version>2.7.1</version>
     <name>web video platform</name>
     <description>国标28181视频平台</description>
     <packaging>${project.packaging}</packaging>

+ 27 - 7
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java

@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp.common;
 
+import com.genersoft.iot.vmp.media.bean.MediaInfo;
 import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
 import io.swagger.v3.oas.annotations.media.Schema;
 
@@ -70,7 +71,7 @@ public class StreamInfo implements Serializable, Cloneable{
     @Schema(description = "流媒体ID")
     private String mediaServerId;
     @Schema(description = "流编码信息")
-    private Object tracks;
+    private MediaInfo mediaInfo;
     @Schema(description = "开始时间")
     private String startTime;
     @Schema(description = "结束时间")
@@ -83,6 +84,9 @@ public class StreamInfo implements Serializable, Cloneable{
     @Schema(description = "是否暂停(录像回放使用)")
     private boolean pause;
 
+    @Schema(description = "产生源类型,包括 unknown = 0,rtmp_push=1,rtsp_push=2,rtp_push=3,pull=4,ffmpeg_pull=5,mp4_vod=6,device_chn=7")
+    private int originType;
+
     public void setFlv(StreamURL flv) {
         this.flv = flv;
     }
@@ -191,8 +195,7 @@ public class StreamInfo implements Serializable, Cloneable{
         }
     }
 
-    public void setFlv(String host, int port, int sslPort, String app, String stream, String callIdParam) {
-        String file = String.format("%s/%s.live.flv%s", app, stream, callIdParam);
+    public void setFlv(String host, int port, int sslPort, String file) {
         if (port > 0) {
             this.flv = new StreamURL("http", host, port, file);
         }
@@ -203,6 +206,15 @@ public class StreamInfo implements Serializable, Cloneable{
         }
     }
 
+    public void setWsFlv(String host, int port, int sslPort, String file) {
+        if (port > 0) {
+            this.ws_flv = new StreamURL("ws", host, port, file);
+        }
+        if (sslPort > 0) {
+            this.wss_flv = new StreamURL("wss", host, sslPort, file);
+        }
+    }
+
     public void setFmp4(String host, int port, int sslPort, String app, String stream, String callIdParam) {
         String file = String.format("%s/%s.live.mp4%s", app, stream, callIdParam);
         if (port > 0) {
@@ -473,12 +485,12 @@ public class StreamInfo implements Serializable, Cloneable{
         this.mediaServerId = mediaServerId;
     }
 
-    public Object getTracks() {
-        return tracks;
+    public MediaInfo getMediaInfo() {
+        return mediaInfo;
     }
 
-    public void setTracks(Object tracks) {
-        this.tracks = tracks;
+    public void setMediaInfo(MediaInfo mediaInfo) {
+        this.mediaInfo = mediaInfo;
     }
 
     public String getStartTime() {
@@ -615,4 +627,12 @@ public class StreamInfo implements Serializable, Cloneable{
     public void setDownLoadFilePath(DownloadFileInfo downLoadFilePath) {
         this.downLoadFilePath = downLoadFilePath;
     }
+
+    public int getOriginType() {
+        return originType;
+    }
+
+    public void setOriginType(int originType) {
+        this.originType = originType;
+    }
 }

+ 2 - 2
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java

@@ -12,9 +12,9 @@ public class VideoManagerConstants {
 
 	public static final String WVP_SERVER_STREAM_PREFIX = "VMP_SIGNALLING_STREAM_";
 
-	public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_";
+	public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER:";
 
-	public static final String MEDIA_SERVERS_ONLINE_PREFIX = "VMP_MEDIA_ONLINE_SERVERS_";
+	public static final String ONLINE_MEDIA_SERVERS_PREFIX = "VMP_ONLINE_MEDIA_SERVERS:";
 
 	public static final String DEVICE_PREFIX = "VMP_DEVICE_";
 

+ 7 - 15
src/main/java/com/genersoft/iot/vmp/conf/CloudRecordTimer.java

@@ -1,14 +1,10 @@
 package com.genersoft.iot.vmp.conf;
 
 
-import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
 import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
-import com.genersoft.iot.vmp.vmanager.cloudRecord.CloudRecordController;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -16,7 +12,6 @@ import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
 import java.io.File;
-import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
@@ -35,9 +30,6 @@ public class CloudRecordTimer {
     @Autowired
     private CloudRecordServiceMapper cloudRecordServiceMapper;
 
-    @Autowired
-    private ZLMRESTfulUtils zlmresTfulUtils;
-
     /**
      * 定时查询待删除的录像文件
      */
@@ -46,12 +38,12 @@ public class CloudRecordTimer {
     public void execute(){
         logger.info("[录像文件定时清理] 开始清理过期录像文件");
         // 获取配置了assist的流媒体节点
-        List<MediaServerItem> mediaServerItemList =  mediaServerService.getAllOnline();
+        List<MediaServer> mediaServerItemList =  mediaServerService.getAllOnline();
         if (mediaServerItemList.isEmpty()) {
             return;
         }
         long result = 0;
-        for (MediaServerItem mediaServerItem : mediaServerItemList) {
+        for (MediaServer mediaServerItem : mediaServerItemList) {
 
             Calendar lastCalendar = Calendar.getInstance();
             if (mediaServerItem.getRecordDay() > 0) {
@@ -69,10 +61,10 @@ public class CloudRecordTimer {
                 // TODO 后续可以删除空了的过期日期文件夹
                 for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
                     String date = new File(cloudRecordItem.getFilePath()).getParentFile().getName();
-                    JSONObject jsonObject = zlmresTfulUtils.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(),
+                    boolean deleteResult = mediaServerService.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(),
                             cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
-                    if (jsonObject.getInteger("code") != 0) {
-                        logger.warn("[录像文件定时清理] 删除磁盘文件错误: {}:{}", cloudRecordItem.getFilePath(), jsonObject);
+                    if (deleteResult) {
+                        logger.warn("[录像文件定时清理] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
                     }
                 }
                 result += cloudRecordServiceMapper.deleteList(cloudRecordItemList);

+ 67 - 29
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.conf;
 
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -42,12 +42,24 @@ public class MediaConfig{
     @Value("${media.stream-ip:${media.ip}}")
     private String streamIp;
 
-    @Value("${media.http-port}")
+    @Value("${media.http-port:0}")
     private Integer httpPort;
 
+    @Value("${media.flv-port:0}")
+    private Integer flvPort = 0;
+
+    @Value("${media.ws-flv-port:0}")
+    private Integer wsFlvPort = 0;
+
     @Value("${media.http-ssl-port:0}")
     private Integer httpSSlPort = 0;
 
+    @Value("${media.flv-ssl-port:0}")
+    private Integer flvSSlPort = 0;
+
+    @Value("${media.ws-flv-ssl-port:0}")
+    private Integer wsFlvSSlPort = 0;
+
     @Value("${media.rtmp-port:0}")
     private Integer rtmpPort = 0;
 
@@ -87,6 +99,9 @@ public class MediaConfig{
     @Value("${media.record-path:}")
     private String recordPath;
 
+    @Value("${media.type:zlm}")
+    private String type;
+
     public String getId() {
         return id;
     }
@@ -196,36 +211,59 @@ public class MediaConfig{
         return sipDomain;
     }
 
-    public MediaServerItem getMediaSerItem(){
-        MediaServerItem mediaServerItem = new MediaServerItem();
-        mediaServerItem.setId(id);
-        mediaServerItem.setIp(ip);
-        mediaServerItem.setDefaultServer(true);
-        mediaServerItem.setHookIp(getHookIp());
-        mediaServerItem.setSdpIp(getSdpIp());
-        mediaServerItem.setStreamIp(getStreamIp());
-        mediaServerItem.setHttpPort(httpPort);
-        mediaServerItem.setHttpSSlPort(httpSSlPort);
-        mediaServerItem.setRtmpPort(rtmpPort);
-        mediaServerItem.setRtmpSSlPort(rtmpSSlPort);
-        mediaServerItem.setRtpProxyPort(getRtpProxyPort());
-        mediaServerItem.setRtspPort(rtspPort);
-        mediaServerItem.setRtspSSLPort(rtspSSLPort);
-        mediaServerItem.setAutoConfig(autoConfig);
-        mediaServerItem.setSecret(secret);
-        mediaServerItem.setRtpEnable(rtpEnable);
-        mediaServerItem.setRtpPortRange(rtpPortRange);
-        mediaServerItem.setSendRtpPortRange(rtpSendPortRange);
-        mediaServerItem.setRecordAssistPort(recordAssistPort);
-        mediaServerItem.setHookAliveInterval(30.00f);
-        mediaServerItem.setRecordDay(recordDay);
+    public MediaServer getMediaSerItem(){
+        MediaServer mediaServer = new MediaServer();
+        mediaServer.setId(id);
+        mediaServer.setIp(ip);
+        mediaServer.setDefaultServer(true);
+        mediaServer.setHookIp(getHookIp());
+        mediaServer.setSdpIp(getSdpIp());
+        mediaServer.setStreamIp(getStreamIp());
+        mediaServer.setHttpPort(httpPort);
+        if (flvPort == 0) {
+            mediaServer.setFlvPort(httpPort);
+        }else {
+            mediaServer.setFlvPort(flvPort);
+        }
+        if (wsFlvPort == 0) {
+            mediaServer.setWsFlvPort(httpPort);
+        }else {
+            mediaServer.setWsFlvPort(wsFlvPort);
+        }
+        if (flvSSlPort == 0) {
+            mediaServer.setFlvSSLPort(httpSSlPort);
+        }else {
+            mediaServer.setFlvSSLPort(flvSSlPort);
+        }
+        if (wsFlvSSlPort == 0) {
+            mediaServer.setWsFlvSSLPort(httpSSlPort);
+        }else {
+            mediaServer.setWsFlvSSLPort(wsFlvSSlPort);
+        }
+
+        mediaServer.setHttpSSlPort(httpSSlPort);
+        mediaServer.setRtmpPort(rtmpPort);
+        mediaServer.setRtmpSSlPort(rtmpSSlPort);
+        mediaServer.setRtpProxyPort(getRtpProxyPort());
+        mediaServer.setRtspPort(rtspPort);
+        mediaServer.setRtspSSLPort(rtspSSLPort);
+        mediaServer.setAutoConfig(autoConfig);
+        mediaServer.setSecret(secret);
+        mediaServer.setRtpEnable(rtpEnable);
+        mediaServer.setRtpPortRange(rtpPortRange);
+        mediaServer.setSendRtpPortRange(rtpSendPortRange);
+        mediaServer.setRecordAssistPort(recordAssistPort);
+        mediaServer.setHookAliveInterval(10f);
+        mediaServer.setRecordDay(recordDay);
+        mediaServer.setStatus(false);
+        mediaServer.setType(type);
         if (recordPath != null) {
-            mediaServerItem.setRecordPath(recordPath);
+            mediaServer.setRecordPath(recordPath);
         }
-        mediaServerItem.setCreateTime(DateUtil.getNow());
-        mediaServerItem.setUpdateTime(DateUtil.getNow());
+        mediaServer.setCreateTime(DateUtil.getNow());
+        mediaServer.setUpdateTime(DateUtil.getNow());
 
-        return mediaServerItem;
+        return mediaServer;
     }
 
     public Integer getRecordDay() {

+ 12 - 12
src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java

@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.conf;
 
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
@@ -54,7 +54,7 @@ public class ProxyServletConfig {
         @Override
         protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
             String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
-            MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
+            MediaServer mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
             if (mediaInfo != null) {
                 if (!ObjectUtils.isEmpty(queryStr)) {
                     queryStr += "&secret=" + mediaInfo.getSecret();
@@ -103,7 +103,7 @@ public class ProxyServletConfig {
         @Override
         protected String getTargetUri(HttpServletRequest servletRequest) {
             String requestURI = servletRequest.getRequestURI();
-            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            MediaServer mediaInfo = getMediaInfoByUri(requestURI);
 
             String uri = null;
             if (mediaInfo != null) {
@@ -121,7 +121,7 @@ public class ProxyServletConfig {
         @Override
         protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
             String requestURI = servletRequest.getRequestURI();
-            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            MediaServer mediaInfo = getMediaInfoByUri(requestURI);
             HttpHost host;
             if (mediaInfo != null) {
                 host = new HttpHost(mediaInfo.getIp(), mediaInfo.getHttpPort());
@@ -135,7 +135,7 @@ public class ProxyServletConfig {
         /**
          * 根据uri获取流媒体信息
          */
-        MediaServerItem getMediaInfoByUri(String uri){
+        MediaServer getMediaInfoByUri(String uri){
             String[] split = uri.split("/");
             String mediaServerId = split[2];
             if ("default".equalsIgnoreCase(mediaServerId)) {
@@ -151,7 +151,7 @@ public class ProxyServletConfig {
         @Override
         protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
             String requestURI = servletRequest.getRequestURI();
-            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            MediaServer mediaInfo = getMediaInfoByUri(requestURI);
             String url = super.rewriteUrlFromRequest(servletRequest);
             if (mediaInfo == null) {
                 logger.error("[ZLM服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI);
@@ -181,7 +181,7 @@ public class ProxyServletConfig {
         @Override
         protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
             String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
-            MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
+            MediaServer mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
             if (mediaInfo == null) {
                 return null;
             }
@@ -238,7 +238,7 @@ public class ProxyServletConfig {
         @Override
         protected String getTargetUri(HttpServletRequest servletRequest) {
             String requestURI = servletRequest.getRequestURI();
-            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            MediaServer mediaInfo = getMediaInfoByUri(requestURI);
 
             String uri = null;
             if (mediaInfo != null) {
@@ -256,7 +256,7 @@ public class ProxyServletConfig {
         @Override
         protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
             String requestURI = servletRequest.getRequestURI();
-            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            MediaServer mediaInfo = getMediaInfoByUri(requestURI);
             HttpHost host;
             if (mediaInfo != null) {
                 host = new HttpHost(mediaInfo.getIp(), mediaInfo.getRecordAssistPort());
@@ -270,7 +270,7 @@ public class ProxyServletConfig {
         /**
          * 根据uri获取流媒体信息
          */
-        MediaServerItem getMediaInfoByUri(String uri){
+        MediaServer getMediaInfoByUri(String uri){
             String[] split = uri.split("/");
             String mediaServerId = split[2];
             if ("default".equalsIgnoreCase(mediaServerId)) {
@@ -287,7 +287,7 @@ public class ProxyServletConfig {
         @Override
         protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
             String requestURI = servletRequest.getRequestURI();
-            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            MediaServer mediaInfo = getMediaInfoByUri(requestURI);
             String url = super.rewriteUrlFromRequest(servletRequest);
             if (mediaInfo == null) {
                 logger.error("[录像服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI);

+ 0 - 4
src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java

@@ -1,7 +1,6 @@
 package com.genersoft.iot.vmp.conf;
 
 import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -14,9 +13,6 @@ public class WVPTimerTask {
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
 
-    @Autowired
-    private IMediaServerService mediaServerService;
-
     @Value("${server.port}")
     private int serverPort;
 

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java

@@ -117,7 +117,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .authorizeRequests()
                 .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                 .antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll()
-                .antMatchers("/api/user/login", "/index/hook/**", "/swagger-ui/**", "/doc.html").permitAll()
+                .antMatchers("/api/user/login", "/index/hook/**","/index/hook/abl/**", "/swagger-ui/**", "/doc.html").permitAll()
                 .anyRequest().authenticated()
                 // 异常处理器
                 .and()

+ 5 - 5
src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java

@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.bean;
 
 
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
 import gov.nist.javax.sip.message.SIPResponse;
 
@@ -15,7 +15,7 @@ public class AudioBroadcastCatch {
     public AudioBroadcastCatch(
             String deviceId,
             String channelId,
-            MediaServerItem mediaServerItem,
+            MediaServer mediaServerItem,
             String app,
             String stream,
             AudioBroadcastEvent event,
@@ -48,7 +48,7 @@ public class AudioBroadcastCatch {
     /**
      * 流媒体信息
      */
-    private MediaServerItem mediaServerItem;
+    private MediaServer mediaServerItem;
 
     /**
      * 关联的流APP
@@ -109,11 +109,11 @@ public class AudioBroadcastCatch {
         return sipTransactionInfo;
     }
 
-    public MediaServerItem getMediaServerItem() {
+    public MediaServer getMediaServerItem() {
         return mediaServerItem;
     }
 
-    public void setMediaServerItem(MediaServerItem mediaServerItem) {
+    public void setMediaServerItem(MediaServer mediaServerItem) {
         this.mediaServerItem = mediaServerItem;
     }
 

+ 5 - 5
src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamInfo.java

@@ -1,11 +1,11 @@
 package com.genersoft.iot.vmp.gb28181.bean;
 
 import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 
 public class InviteStreamInfo {
 
-    public InviteStreamInfo(MediaServerItem mediaServerItem, JSONObject response, String callId, String app, String stream) {
+    public InviteStreamInfo(MediaServer mediaServerItem, JSONObject response, String callId, String app, String stream) {
         this.mediaServerItem = mediaServerItem;
         this.response = response;
         this.callId = callId;
@@ -13,17 +13,17 @@ public class InviteStreamInfo {
         this.stream = stream;
     }
 
-    private MediaServerItem mediaServerItem;
+    private MediaServer mediaServerItem;
     private JSONObject response;
     private String callId;
     private String app;
     private String stream;
 
-    public MediaServerItem getMediaServerItem() {
+    public MediaServer getMediaServerItem() {
         return mediaServerItem;
     }
 
-    public void setMediaServerItem(MediaServerItem mediaServerItem) {
+    public void setMediaServerItem(MediaServer mediaServerItem) {
         this.mediaServerItem = mediaServerItem;
     }
 

+ 7 - 8
src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java

@@ -1,18 +1,17 @@
 package com.genersoft.iot.vmp.gb28181.event;
 
 import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEvent;
 import com.genersoft.iot.vmp.gb28181.event.device.RequestTimeoutEvent;
 import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEvent;
 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
 import com.genersoft.iot.vmp.gb28181.event.subscribe.mobilePosition.MobilePositionEvent;
-import com.genersoft.iot.vmp.media.zlm.event.ZLMOfflineEvent;
-import com.genersoft.iot.vmp.media.zlm.event.ZLMOnlineEvent;
+import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOfflineEvent;
+import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOnlineEvent;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Component;
 
-import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEvent;
-
 import javax.sip.TimeoutEvent;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -40,14 +39,14 @@ public class EventPublisher {
 		applicationEventPublisher.publishEvent(alarmEvent);
 	}
 
-	public void zlmOfflineEventPublish(String mediaServerId){
-		ZLMOfflineEvent outEvent = new ZLMOfflineEvent(this);
+	public void mediaServerOfflineEventPublish(String mediaServerId){
+		MediaServerOfflineEvent outEvent = new MediaServerOfflineEvent(this);
 		outEvent.setMediaServerId(mediaServerId);
 		applicationEventPublisher.publishEvent(outEvent);
 	}
 
-	public void zlmOnlineEventPublish(String mediaServerId) {
-		ZLMOnlineEvent outEvent = new ZLMOnlineEvent(this);
+	public void mediaServerOnlineEventPublish(String mediaServerId) {
+		MediaServerOnlineEvent outEvent = new MediaServerOnlineEvent(this);
 		outEvent.setMediaServerId(mediaServerId);
 		applicationEventPublisher.publishEvent(outEvent);
 	}

+ 5 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/session/AudioBroadcastManager.java

@@ -3,6 +3,8 @@ package com.genersoft.iot.vmp.gb28181.session;
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -18,11 +20,14 @@ import java.util.stream.Stream;
 @Component
 public class AudioBroadcastManager {
 
+    private final static Logger logger = LoggerFactory.getLogger(AudioBroadcastManager.class);
+
     @Autowired
     private SipConfig config;
 
     public static Map<String, AudioBroadcastCatch> data = new ConcurrentHashMap<>();
 
+
     public void update(AudioBroadcastCatch audioBroadcastCatch) {
         if (SipUtils.isFrontEnd(audioBroadcastCatch.getDeviceId())) {
             audioBroadcastCatch.setChannelId(audioBroadcastCatch.getDeviceId());

+ 5 - 15
src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java

@@ -1,16 +1,14 @@
 package com.genersoft.iot.vmp.gb28181.task;
 
-import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
 import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
-import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.service.IDeviceService;
-import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IPlatformService;
 import com.genersoft.iot.vmp.service.impl.PlatformServiceImpl;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -53,9 +51,6 @@ public class SipRunner implements CommandLineRunner {
     @Autowired
     private IDeviceService deviceService;
 
-    @Autowired
-    private ZLMRESTfulUtils zlmresTfulUtils;
-
     @Autowired
     private IMediaServerService mediaServerService;
 
@@ -105,17 +100,12 @@ public class SipRunner implements CommandLineRunner {
         List<SendRtpItem> sendRtpItems = redisCatchStorage.queryAllSendRTPServer();
         if (sendRtpItems.size() > 0) {
             for (SendRtpItem sendRtpItem : sendRtpItems) {
-                MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+                MediaServer mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId());
                 redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(),sendRtpItem.getChannelId(), sendRtpItem.getCallId(),sendRtpItem.getStream());
                 if (mediaServerItem != null) {
                     ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
-                    Map<String, Object> param = new HashMap<>();
-                    param.put("vhost","__defaultVhost__");
-                    param.put("app",sendRtpItem.getApp());
-                    param.put("stream",sendRtpItem.getStream());
-                    param.put("ssrc",sendRtpItem.getSsrc());
-                    JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param);
-                    if (jsonObject != null && jsonObject.getInteger("code") == 0) {
+                    boolean stopResult = mediaServerService.stopSendRtp(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc());
+                    if (stopResult) {
                         ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId());
                         if (platform != null) {
                             try {

+ 8 - 8
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java

@@ -7,8 +7,8 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import gov.nist.javax.sip.message.SIPRequest;
 
@@ -100,7 +100,7 @@ public interface ISIPCommander {
 	 * @param device  视频设备
 	 * @param channel  预览通道
 	 */
-	void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
+	void playStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, HookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 
 	/**
 	 * 请求回放视频流
@@ -110,7 +110,7 @@ public interface ISIPCommander {
 	 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 */
-	void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime,ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
+	void playbackStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime, HookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 
 	/**
 	 * 请求历史媒体下载
@@ -121,9 +121,9 @@ public interface ISIPCommander {
 	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 * @param downloadSpeed 下载倍速参数
 	 */ 
-	void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
-						   String startTime, String endTime, int downloadSpeed, ZlmHttpHookSubscribe.Event hookEvent,
-						   SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
+	void downloadStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
+                           String startTime, String endTime, int downloadSpeed, HookSubscribe.Event hookEvent,
+                           SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
 
 
 	/**
@@ -131,7 +131,7 @@ public interface ISIPCommander {
 	 */
 	void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException;
 
-	void talkStreamCmd(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, ZlmHttpHookSubscribe.Event event, ZlmHttpHookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
+	void talkStreamCmd(MediaServer mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, HookSubscribe.Event event, HookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 
 
 	void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException;

+ 4 - 4
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java

@@ -3,8 +3,8 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd;
 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 
@@ -154,8 +154,8 @@ public interface ISIPCommanderForPlatform {
 
     void streamByeCmd(ParentPlatform platform, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException;
 
-    void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem,
-                            SSRCInfo ssrcInfo, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent,
+    void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServer mediaServerItem,
+                            SSRCInfo ssrcInfo, HookSubscribe.Event event, SipSubscribe.Event okEvent,
                             SipSubscribe.Event errorEvent) throws ParseException, SipException, InvalidArgumentException;
 
     void broadcastResultCmd(ParentPlatform platform, DeviceChannel deviceChannel, String sn, boolean result, SipSubscribe.Event errorEvent,  SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;

+ 36 - 45
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java

@@ -7,10 +7,6 @@ import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.gb28181.SipLayer;
 import com.genersoft.iot.vmp.gb28181.bean.*;
-import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
-import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
-import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
@@ -18,14 +14,12 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
+import com.genersoft.iot.vmp.media.event.hook.Hook;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.event.hook.HookType;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamPush;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
-import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import gov.nist.javax.sip.message.SIPRequest;
@@ -44,7 +38,6 @@ import javax.sip.SipFactory;
 import javax.sip.header.CallIdHeader;
 import javax.sip.message.Request;
 import java.text.ParseException;
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -77,7 +70,7 @@ public class SIPCommander implements ISIPCommander {
     private UserSetting userSetting;
 
     @Autowired
-    private ZlmHttpHookSubscribe subscribe;
+    private HookSubscribe subscribe;
 
     @Autowired
     private IMediaServerService mediaServerService;
@@ -275,8 +268,8 @@ public class SIPCommander implements ISIPCommander {
      * @param errorEvent sip错误订阅
      */
     @Override
-    public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel,
-                              ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+    public void playStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel,
+                              HookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
         String stream = ssrcInfo.getStream();
 
         if (device == null) {
@@ -284,11 +277,11 @@ public class SIPCommander implements ISIPCommander {
         }
 
         logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
-        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
-        subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
+        Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, "rtp", stream, mediaServerItem.getId());
+        subscribe.addSubscribe(rtpHook, (hookData) -> {
             if (event != null) {
-                event.response(mediaServerItemInUse, hookParam);
-                subscribe.removeSubscribe(hookSubscribe);
+                event.response(hookData);
+                subscribe.removeSubscribe(rtpHook);
             }
         });
         String sdpIp;
@@ -384,8 +377,8 @@ public class SIPCommander implements ISIPCommander {
      * @param endTime   结束时间,格式要求:yyyy-MM-dd HH:mm:ss
      */
     @Override
-    public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
-                                  String startTime, String endTime, ZlmHttpHookSubscribe.Event hookEvent,
+    public void playbackStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
+                                  String startTime, String endTime, HookSubscribe.Event hookEvent,
                                   SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
 
 
@@ -458,13 +451,13 @@ public class SIPCommander implements ISIPCommander {
         //ssrc
         content.append("y=" + ssrcInfo.getSsrc() + "\r\n");
 
-        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
+        Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, "rtp", ssrcInfo.getStream(), mediaServerItem.getId());
         // 添加订阅
-        subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
+        subscribe.addSubscribe(rtpHook, (hookData) -> {
             if (hookEvent != null) {
-                hookEvent.response(mediaServerItemInUse, hookParam);
+                hookEvent.response(hookData);
             }
-            subscribe.removeSubscribe(hookSubscribe);
+            subscribe.removeSubscribe(rtpHook);
         });
         Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc());
 
@@ -486,10 +479,10 @@ public class SIPCommander implements ISIPCommander {
      * @param downloadSpeed 下载倍速参数
      */
     @Override
-    public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
+    public void downloadStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
                                   String startTime, String endTime, int downloadSpeed,
-                                  ZlmHttpHookSubscribe.Event hookEvent,
-                                  SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
+                                  HookSubscribe.Event hookEvent,
+                                  SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
 
         logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
         String sdpIp;
@@ -559,19 +552,18 @@ public class SIPCommander implements ISIPCommander {
 
         content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
         logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc());
-        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
+        Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, "rtp", ssrcInfo.getStream(), mediaServerItem.getId());
         // 添加订阅
         CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
         String callId= newCallIdHeader.getCallId();
-        subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> {
+        subscribe.addSubscribe(rtpHook, (hookData) -> {
             logger.debug("sipc 添加订阅===callId {}",callId);
-            hookEvent.response(mediaServerItemInUse, hookParam);
-            subscribe.removeSubscribe(hookSubscribe);
-            hookSubscribe.getContent().put("regist", false);
-            hookSubscribe.getContent().put("schema", "rtsp");
+            hookEvent.response(hookData);
+            subscribe.removeSubscribe(rtpHook);
             // 添加流注销的订阅,注销了后向设备发送bye
-            subscribe.addSubscribe(hookSubscribe,
-                    (mediaServerItemForEnd, hookParam1) -> {
+            Hook departureHook = Hook.getInstance(HookType.on_media_departure, "rtp", ssrcInfo.getStream(), mediaServerItem.getId());
+            subscribe.addSubscribe(departureHook,
+                    (departureHookData) -> {
                         logger.info("[录像]下载结束, 发送BYE");
                         try {
                             streamByeCmd(device, channelId, ssrcInfo.getStream(), callId);
@@ -595,7 +587,7 @@ public class SIPCommander implements ISIPCommander {
     }
 
     @Override
-    public void talkStreamCmd(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, ZlmHttpHookSubscribe.Event event, ZlmHttpHookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+    public void talkStreamCmd(MediaServer mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, HookSubscribe.Event event, HookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
 
         String stream = sendRtpItem.getStream();
 
@@ -609,20 +601,20 @@ public class SIPCommander implements ISIPCommander {
         }
 
         logger.info("[语音喊话] {} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), sendRtpItem.getPort());
-        HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
-        subscribe.addSubscribe(hookSubscribeForStreamChange, (mediaServerItemInUse, hookParam) -> {
+        Hook hook = Hook.getInstance(HookType.on_media_arrival, "rtp", stream, mediaServerItem.getId());
+        subscribe.addSubscribe(hook, (hookData) -> {
             if (event != null) {
-                event.response(mediaServerItemInUse, hookParam);
-                subscribe.removeSubscribe(hookSubscribeForStreamChange);
+                event.response(hookData);
+                subscribe.removeSubscribe(hook);
             }
         });
 
         CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
         callIdHeader.setCallId(callId);
-        HookSubscribeForStreamPush hookSubscribeForStreamPush = HookSubscribeFactory.on_publish("rtp", stream,  null, mediaServerItem.getId());
-        subscribe.addSubscribe(hookSubscribeForStreamPush, (mediaServerItemInUse, hookParam) -> {
+        Hook publishHook = Hook.getInstance(HookType.on_publish, "rtp", stream, mediaServerItem.getId());
+        subscribe.addSubscribe(publishHook, (hookData) -> {
             if (eventForPush != null) {
-                eventForPush.response(mediaServerItemInUse, hookParam);
+                eventForPush.response(hookData);
             }
         });
         //
@@ -1265,7 +1257,6 @@ public class SIPCommander implements ISIPCommander {
      * @param startPriority 报警起始级别(可选)
      * @param endPriority   报警终止级别(可选)
      * @param alarmMethod   报警方式条件(可选)
-     * @param alarmType     报警类型
      * @param startTime     报警发生起始时间(可选)
      * @param endTime       报警发生终止时间(可选)
      * @return true = 命令发送成功

+ 14 - 15
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java

@@ -13,13 +13,12 @@ import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderPlarformProvider;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
+import com.genersoft.iot.vmp.media.event.hook.Hook;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.event.hook.HookType;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
-import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -76,7 +75,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
     private SIPSender sipSender;
 
     @Autowired
-    private ZlmHttpHookSubscribe subscribe;
+    private HookSubscribe subscribe;
 
     @Autowired
     private UserSetting userSetting;
@@ -844,7 +843,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         }
         logger.info("[向上级发送BYE], {}/{}", platform.getServerGBId(), sendRtpItem.getChannelId());
         String mediaServerId = sendRtpItem.getMediaServerId();
-        MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
+        MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId);
         if (mediaServerItem != null) {
             mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
             zlmServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStream());
@@ -895,8 +894,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
     }
 
     @Override
-    public void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem,
-                                   SSRCInfo ssrcInfo, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent,
+    public void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServer mediaServerItem,
+                                   SSRCInfo ssrcInfo, HookSubscribe.Event event, SipSubscribe.Event okEvent,
                                    SipSubscribe.Event errorEvent) throws ParseException, SipException, InvalidArgumentException {
         String stream = ssrcInfo.getStream();
 
@@ -905,11 +904,11 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         }
 
         logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
-        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
-        subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
+        Hook hook = Hook.getInstance(HookType.on_media_arrival, "rtp", stream, mediaServerItem.getId());
+        subscribe.addSubscribe(hook, (hookData) -> {
             if (event != null) {
-                event.response(mediaServerItemInUse, hookParam);
-                subscribe.removeSubscribe(hookSubscribe);
+                event.response(hookData);
+                subscribe.removeSubscribe(hook);
             }
         });
         String sdpIp = mediaServerItem.getSdpIp();
@@ -949,7 +948,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         sipSender.transmitRequest(sipLayer.getLocalIp(platform.getDeviceIp()), request, (e -> {
             streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream());
             mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
-            subscribe.removeSubscribe(hookSubscribe);
+            subscribe.removeSubscribe(hook);
             errorEvent.response(e);
         }), e -> {
             ResponseEvent responseEvent = (ResponseEvent) e.event;

+ 30 - 58
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java

@@ -3,6 +3,9 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
 import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.conf.exception.ControllerException;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
+import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
@@ -10,10 +13,10 @@ import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.service.IDeviceService;
-import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IPlayService;
 import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
 import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
@@ -25,12 +28,15 @@ import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import javax.sip.InvalidArgumentException;
 import javax.sip.RequestEvent;
+import javax.sip.SipException;
 import javax.sip.address.SipURI;
 import javax.sip.header.CallIdHeader;
 import javax.sip.header.FromHeader;
 import javax.sip.header.HeaderAddress;
 import javax.sip.header.ToHeader;
+import java.text.ParseException;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -69,7 +75,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
 	private ZLMServerFactory zlmServerFactory;
 
 	@Autowired
-	private ZlmHttpHookSubscribe hookSubscribe;
+	private HookSubscribe hookSubscribe;
 
 	@Autowired
 	private IMediaServerService mediaServerService;
@@ -104,7 +110,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
 			logger.info("收到ACK,rtp/{} TCP主动方式后续处理", sendRtpItem.getStream());
 			return;
 		}
-		MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+		MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
 		logger.info("收到ACK,rtp/{}开始向上级推流, 目标={}:{},SSRC={}, 协议:{}",
 				sendRtpItem.getStream(),
 				sendRtpItem.getIp(),
@@ -115,19 +121,24 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
 		ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(fromUserId);
 
 		if (parentPlatform != null) {
-			Map<String, Object> param = getSendRtpParam(sendRtpItem);
 			if (!userSetting.getServerId().equals(sendRtpItem.getServerId())) {
 				RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance(
 						sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStream(),
 						sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(),
 						sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio());
 				redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, json -> {
-					playService.startSendRtpStreamHand(sendRtpItem, parentPlatform, json, param, callIdHeader);
+					playService.startSendRtpStreamFailHand(sendRtpItem, parentPlatform, callIdHeader);
 				});
 			} else {
-				JSONObject startSendRtpStreamResult = sendRtp(sendRtpItem, mediaInfo, param);
-				if (startSendRtpStreamResult != null) {
-					playService.startSendRtpStreamHand(sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader);
+				try {
+					if (sendRtpItem.isTcpActive()) {
+						mediaServerService.startSendRtpPassive(mediaInfo, parentPlatform, sendRtpItem, null);
+					} else {
+						mediaServerService.startSendRtpStream(mediaInfo, parentPlatform, sendRtpItem);
+					}
+				}catch (ControllerException e) {
+					logger.error("RTP推流失败: {}", e.getMessage());
+					playService.startSendRtpStreamFailHand(sendRtpItem, parentPlatform, callIdHeader);
 				}
 			}
 		}else {
@@ -144,56 +155,17 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
 				logger.warn("[收到ACK]:来自{},目标为({})的推流信息为找到流体服务[{}]信息",fromUserId, toUserId, sendRtpItem.getMediaServerId());
 				return;
 			}
-			Map<String, Object> param = getSendRtpParam(sendRtpItem);
-			JSONObject startSendRtpStreamResult = sendRtp(sendRtpItem, mediaInfo, param);
-			if (startSendRtpStreamResult != null) {
-				playService.startSendRtpStreamHand(sendRtpItem, device, startSendRtpStreamResult, param, callIdHeader);
-			}
-		}
-	}
-
-	private Map<String, Object> getSendRtpParam(SendRtpItem sendRtpItem) {
-		String isUdp = sendRtpItem.isTcp() ? "0" : "1";
-		Map<String, Object> param = new HashMap<>(12);
-		param.put("vhost","__defaultVhost__");
-		param.put("app",sendRtpItem.getApp());
-		param.put("stream",sendRtpItem.getStream());
-		param.put("ssrc", sendRtpItem.getSsrc());
-		param.put("dst_url",sendRtpItem.getIp());
-		param.put("dst_port", sendRtpItem.getPort());
-		param.put("src_port", sendRtpItem.getLocalPort());
-		param.put("pt", sendRtpItem.getPt());
-		param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0");
-		param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0");
-		param.put("is_udp", isUdp);
-		if (!sendRtpItem.isTcp()) {
-			// udp模式下开启rtcp保活
-			param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0");
-		}
-		return param;
-	}
-
-	private JSONObject sendRtp(SendRtpItem sendRtpItem, MediaServerItem mediaInfo, Map<String, Object> param){
-		JSONObject startSendRtpStreamResult = null;
-		if (sendRtpItem.getLocalPort() != 0) {
-			if (sendRtpItem.isTcpActive()) {
-				startSendRtpStreamResult = zlmServerFactory.startSendRtpPassive(mediaInfo, param);
-			}else {
-				param.put("dst_url", sendRtpItem.getIp());
-				param.put("dst_port", sendRtpItem.getPort());
-				startSendRtpStreamResult = zlmServerFactory.startSendRtpStream(mediaInfo, param);
-			}
-		}else {
-			if (sendRtpItem.isTcpActive()) {
-				startSendRtpStreamResult = zlmServerFactory.startSendRtpPassive(mediaInfo, param);
-			}else {
-				param.put("dst_url", sendRtpItem.getIp());
-				param.put("dst_port", sendRtpItem.getPort());
-				startSendRtpStreamResult = zlmServerFactory.startSendRtpStream(mediaInfo, param);
+			try {
+				if (sendRtpItem.isTcpActive()) {
+					mediaServerService.startSendRtpPassive(mediaInfo, null, sendRtpItem, null);
+				} else {
+					mediaServerService.startSendRtpStream(mediaInfo, null, sendRtpItem);
+				}
+			}catch (ControllerException e) {
+				logger.error("RTP推流失败: {}", e.getMessage());
+				playService.startSendRtpStreamFailHand(sendRtpItem, null, callIdHeader);
 			}
 		}
-		return startSendRtpStreamResult;
-
 	}
 
 }

+ 6 - 5
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java

@@ -13,8 +13,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
 import com.genersoft.iot.vmp.service.*;
 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
@@ -145,7 +146,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
 						redisGbPlayMsgListener.sendMsgForStopSendRtpStream(sendRtpItem.getServerId(), streamMsg);
 					}
 				}else {
-					MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+					MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
 					redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(),
 							callIdHeader.getCallId(), null);
 					zlmServerFactory.stopSendRtpStream(mediaInfo, param);
@@ -165,7 +166,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
 					}
 				}
 			}else {
-				MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+				MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
 				redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(),
 						callIdHeader.getCallId(), null);
 				zlmServerFactory.stopSendRtpStream(mediaInfo, param);
@@ -173,7 +174,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
 					mediaServerService.releaseSsrc(mediaInfo.getId(), sendRtpItem.getSsrc());
 				}
 			}
-			MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+			MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
 			if (mediaInfo != null) {
 				AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
 				if (audioBroadcastCatch != null && audioBroadcastCatch.getSipTransactionInfo().getCallId().equals(callIdHeader.getCallId())) {
@@ -245,7 +246,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
 				}
 			}
 			// 释放ssrc
-			MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
+			MediaServer mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
 			if (mediaServerItem != null) {
 				mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc());
 			}

+ 38 - 34
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java

@@ -18,12 +18,19 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.event.hook.Hook;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.event.hook.HookType;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.*;
-import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
-import com.genersoft.iot.vmp.service.*;
+import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
+import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
+import com.genersoft.iot.vmp.service.IInviteStreamService;
+import com.genersoft.iot.vmp.service.IPlayService;
+import com.genersoft.iot.vmp.service.IStreamProxyService;
+import com.genersoft.iot.vmp.service.IStreamPushService;
 import com.genersoft.iot.vmp.service.bean.ErrorCallback;
 import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
@@ -113,7 +120,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
     private IMediaServerService mediaServerService;
 
     @Autowired
-    private ZlmHttpHookSubscribe zlmHttpHookSubscribe;
+    private HookSubscribe hookSubscribe;
 
     @Autowired
     private SIPProcessorObserver sipProcessorObserver;
@@ -192,7 +199,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                 GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
                 PlatformCatalog catalog = storager.getCatalog(requesterId, channelId);
 
-                MediaServerItem mediaServerItem = null;
+                MediaServer mediaServerItem = null;
                 StreamPushItem streamPushItem = null;
                 StreamProxyItem proxyByAppAndStream = null;
                 // 不是通道可能是直播流
@@ -398,7 +405,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     Long finalStopTime = stopTime;
                     ErrorCallback<Object> hookEvent = (code, msg, data) -> {
                         StreamInfo streamInfo = (StreamInfo)data;
-                        MediaServerItem mediaServerItemInUSe = mediaServerService.getOne(streamInfo.getMediaServerId());
+                        MediaServer mediaServerItemInUSe = mediaServerService.getOne(streamInfo.getMediaServerId());
                         logger.info("[上级Invite]下级已经开始推流。 回复200OK(SDP), {}/{}", streamInfo.getApp(), streamInfo.getStream());
                         //     * 0 等待设备推流上来
                         //     * 1 下级已经推流,等待上级平台回复ack
@@ -455,7 +462,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                             responseSdpAck(request, content.toString(), platform);
                             // tcp主动模式,回复sdp后开启监听
                             if (sendRtpItem.isTcpActive()) {
-                                MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+                                MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
                                 Map<String, Object> param = new HashMap<>(12);
                                 param.put("vhost","__defaultVhost__");
                                 param.put("app",sendRtpItem.getApp());
@@ -485,12 +492,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                         }
                     };
                     ErrorCallback<Object> errorEvent = ((statusCode, msg, data) -> {
+                        logger.info("[上级Invite] {}, 失败, 平台:{}, 通道:{}, code: {}, msg;{}", sessionName, username, channelId, statusCode, msg);
                         // 未知错误。直接转发设备点播的错误
                         try {
-                            if (statusCode > 0) {
-                                Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
-                                sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
-                            }
+                            Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
+                            sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
                         } catch (ParseException | SipException e) {
                             logger.error("未处理的异常 ", e);
                         }
@@ -501,7 +507,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                         String startTimeStr = DateUtil.urlFormatter.format(start);
                         String endTimeStr = DateUtil.urlFormatter.format(end);
                         String stream = device.getDeviceId() + "_" + channelId + "_" + startTimeStr + "_" + endTimeStr;
-                        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, stream, null, device.isSsrcCheck(), true, 0,false, false, device.getStreamModeForParam());
+                        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, stream, null, device.isSsrcCheck(), true, 0,false,!channel.isHasAudio(), false, device.getStreamModeForParam());
                         sendRtpItem.setStream(stream);
                         // 写入redis, 超时时回复
                         redisCatchStorage.updateSendRTPSever(sendRtpItem);
@@ -531,7 +537,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                         }
 
                         sendRtpItem.setPlayType(InviteStreamType.DOWNLOAD);
-                        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, false, device.getStreamModeForParam());
+                        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,!channel.isHasAudio(), false, device.getStreamModeForParam());
                         sendRtpItem.setStream(ssrcInfo.getStream());
                         // 写入redis, 超时时回复
                         redisCatchStorage.updateSendRTPSever(sendRtpItem);
@@ -581,12 +587,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     if ("push".equals(gbStream.getStreamType())) {
                         if (streamPushItem != null) {
                             // 从redis查询是否正在接收这个推流
-                            OnStreamChangedHookParam pushListItem = redisCatchStorage.getPushListItem(gbStream.getApp(), gbStream.getStream());
+                            StreamPushItem pushListItem = redisCatchStorage.getPushListItem(gbStream.getApp(), gbStream.getStream());
                             if (pushListItem != null) {
-                                StreamPushItem transform = streamPushService.transform(pushListItem);
-                                transform.setSelf(userSetting.getServerId().equals(pushListItem.getSeverId()));
+                                pushListItem.setSelf(userSetting.getServerId().equals(pushListItem.getServerId()));
                                 // 推流状态
-                                pushStream(evt, request, gbStream, transform, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                                pushStream(evt, request, gbStream, pushListItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
                                         mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
                             }else {
                                 // 未推流 拉起
@@ -633,10 +638,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
      * 安排推流
      */
     private void pushProxyStream(RequestEvent evt, SIPRequest request, GbStream gbStream, ParentPlatform platform,
-                            CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
+                            CallIdHeader callIdHeader, MediaServer mediaServerItem,
                             int port, Boolean tcpActive, boolean mediaTransmissionTCP,
                             String channelId, String addressStr, String ssrc, String requesterId) {
-            Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
+            Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
             if (streamReady != null && streamReady) {
                 // 自平台内容
                 SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
@@ -671,12 +676,12 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
     }
 
     private void pushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
-                            CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
+                            CallIdHeader callIdHeader, MediaServer mediaServerItem,
                             int port, Boolean tcpActive, boolean mediaTransmissionTCP,
                             String channelId, String addressStr, String ssrc, String requesterId) {
         // 推流
         if (streamPushItem.isSelf()) {
-            Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
+            Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
             if (streamReady != null && streamReady) {
                 // 自平台内容
                 SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
@@ -723,24 +728,23 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
      * 通知流上线
      */
     private void notifyStreamOnline(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
-                                    CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
+                                    CallIdHeader callIdHeader, MediaServer mediaServerItem,
                                     int port, Boolean tcpActive, boolean mediaTransmissionTCP,
                                     String channelId, String addressStr, String ssrc, String requesterId) {
         if ("proxy".equals(gbStream.getStreamType())) {
             // TODO 控制启用以使设备上线
             logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", gbStream.getApp(), gbStream.getStream());
             // 监听流上线
-            HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(gbStream.getApp(), gbStream.getStream(), true, "rtsp", mediaServerItem.getId());
-            zlmHttpHookSubscribe.addSubscribe(hookSubscribe, (mediaServerItemInUSe, hookParam) -> {
-                OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam)hookParam;
-                logger.info("[上级点播]拉流代理已经就绪, {}/{}", streamChangedHookParam.getApp(), streamChangedHookParam.getStream());
+            Hook hook = Hook.getInstance(HookType.on_media_arrival, gbStream.getApp(), gbStream.getStream(), mediaServerItem.getId());
+            this.hookSubscribe.addSubscribe(hook, (hookData) -> {
+                logger.info("[上级点播]拉流代理已经就绪, {}/{}", hookData.getApp(), hookData.getStream());
                 dynamicTask.stop(callIdHeader.getCallId());
                 pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive,
                         mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
             });
             dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
                 logger.info("[ app={}, stream={} ] 等待拉流代理流超时", gbStream.getApp(), gbStream.getStream());
-                zlmHttpHookSubscribe.removeSubscribe(hookSubscribe);
+                this.hookSubscribe.removeSubscribe(hook);
             }, userSetting.getPlatformPlayTimeout());
             boolean start = streamProxyService.start(gbStream.getApp(), gbStream.getStream());
             if (!start) {
@@ -749,7 +753,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                 } catch (SipException | InvalidArgumentException | ParseException e) {
                     logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
                 }
-                zlmHttpHookSubscribe.removeSubscribe(hookSubscribe);
+                this.hookSubscribe.removeSubscribe(hook);
                 dynamicTask.stop(callIdHeader.getCallId());
             }
         } else if ("push".equals(gbStream.getStreamType())) {
@@ -846,7 +850,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
      * 来自其他wvp的推流
      */
     private void otherWvpPushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
-                                    CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
+                                    CallIdHeader callIdHeader, MediaServer mediaServerItem,
                                     int port, Boolean tcpActive, boolean mediaTransmissionTCP,
                                     String channelId, String addressStr, String ssrc, String requesterId) {
         logger.info("[级联点播]直播流来自其他平台,发送redis消息");
@@ -909,7 +913,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                 });
     }
 
-    public SIPResponse sendStreamAck(MediaServerItem mediaServerItem, SIPRequest request, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt) {
+    public SIPResponse sendStreamAck(MediaServer mediaServerItem, SIPRequest request, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt) {
 
         String sdpIp = mediaServerItem.getSdpIp();
         if (!ObjectUtils.isEmpty(platform.getSendStreamIp())) {
@@ -1055,7 +1059,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                 logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}, {}", requesterId, addressStr, port, gb28181Sdp.getSsrc(),
                         mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP");
 
-                MediaServerItem mediaServerItem = broadcastCatch.getMediaServerItem();
+                MediaServer mediaServerItem = broadcastCatch.getMediaServerItem();
                 if (mediaServerItem == null) {
                     logger.warn("未找到语音喊话使用的zlm");
                     try {
@@ -1104,7 +1108,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
 
                 redisCatchStorage.updateSendRTPSever(sendRtpItem);
 
-                Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, broadcastCatch.getApp(), broadcastCatch.getStream());
+                Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, broadcastCatch.getApp(), broadcastCatch.getStream());
                 if (streamReady) {
                     sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, gb28181Sdp.getSsrc());
                 } else {
@@ -1132,7 +1136,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
         }
     }
 
-    SIPResponse sendOk(Device device, SendRtpItem sendRtpItem, SessionDescription sdp, SIPRequest request, MediaServerItem mediaServerItem, boolean mediaTransmissionTCP, String ssrc) {
+    SIPResponse sendOk(Device device, SendRtpItem sendRtpItem, SessionDescription sdp, SIPRequest request, MediaServer mediaServerItem, boolean mediaTransmissionTCP, String ssrc) {
         SIPResponse sipResponse = null;
         try {
             sendRtpItem.setStatus(2);

+ 16 - 16
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java

@@ -37,7 +37,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
 
     private final String method = "MESSAGE";
 
-    private static Map<String, IMessageHandler> messageHandlerMap = new ConcurrentHashMap<>();
+    private static final Map<String, IMessageHandler> messageHandlerMap = new ConcurrentHashMap<>();
 
     @Autowired
     private SIPProcessorObserver sipProcessorObserver;
@@ -100,9 +100,9 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
                     deviceNotFoundEvent.setCallId(callIdHeader.getCallId());
                     SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(deviceNotFoundEvent);
                     sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult);
-                };
+                }
             }else {
-                Element rootElement = null;
+                Element rootElement;
                 try {
                     rootElement = getRootElement(evt);
                     if (rootElement == null) {
@@ -110,24 +110,24 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
                         responseAck(request, Response.BAD_REQUEST, "content is null");
                         return;
                     }
+                    String name = rootElement.getName();
+                    IMessageHandler messageHandler = messageHandlerMap.get(name);
+                    if (messageHandler != null) {
+                        if (device != null) {
+                            messageHandler.handForDevice(evt, device, rootElement);
+                        }else { // 由于上面已经判断都为null则直接返回,所以这里device和parentPlatform必有一个不为null
+                            messageHandler.handForPlatform(evt, parentPlatform, rootElement);
+                        }
+                    }else {
+                        // 不支持的message
+                        // 不存在则回复415
+                        responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response");
+                    }
                 } catch (DocumentException e) {
                     logger.warn("解析XML消息内容异常", e);
                     // 不存在则回复404
                     responseAck(request, Response.BAD_REQUEST, e.getMessage());
                 }
-                String name = rootElement.getName();
-                IMessageHandler messageHandler = messageHandlerMap.get(name);
-                if (messageHandler != null) {
-                    if (device != null) {
-                        messageHandler.handForDevice(evt, device, rootElement);
-                    }else { // 由于上面已经判断都为null则直接返回,所以这里device和parentPlatform必有一个不为null
-                        messageHandler.handForPlatform(evt, parentPlatform, rootElement);
-                    }
-                }else {
-                    // 不支持的message
-                    // 不存在则回复415
-                    responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response");
-                }
             }
         } catch (SipException e) {
             logger.warn("SIP 回复错误", e);

+ 14 - 17
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java

@@ -1,7 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
 
 import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
@@ -9,10 +8,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.service.IDeviceService;
-import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IPlatformService;
 import com.genersoft.iot.vmp.service.IPlayService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -121,15 +119,14 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp
                 return;
             }
 
-            MediaServerItem mediaServerForMinimumLoad = mediaServerService.getMediaServerForMinimumLoad(null);
+            MediaServer mediaServerForMinimumLoad = mediaServerService.getMediaServerForMinimumLoad(null);
             commanderForPlatform.broadcastResultCmd(platform, deviceChannel, sn, true,  eventResult->{
                 logger.info("[国标级联] 语音喊话 回复失败 platform: {}, 错误:{}/{}", platform.getServerGBId(), eventResult.statusCode, eventResult.msg);
             }, eventResult->{
 
                 // 消息发送成功, 向上级发送invite,获取推流
                 try {
-                    platformService.broadcastInvite(platform, deviceChannel.getChannelId(), mediaServerForMinimumLoad,  (mediaServerItem, hookParam)->{
-                        OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam)hookParam;
+                    platformService.broadcastInvite(platform, deviceChannel.getChannelId(), mediaServerForMinimumLoad,  (hookData)->{
                         // 上级平台推流成功
                         AudioBroadcastCatch broadcastCatch = audioBroadcastManager.get(device.getDeviceId(), targetId);
                         if (broadcastCatch != null ) {
@@ -137,20 +134,20 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp
                                 logger.info("[国标级联] 语音喊话 设备正在使用中 platform: {}, channel: {}",
                                         platform.getServerGBId(), deviceChannel.getChannelId());
                                 //  查看语音通道已经建立且已经占用 回复BYE
-                                platformService.stopBroadcast(platform, deviceChannel, streamChangedHookParam.getStream(),  true, mediaServerItem);
+                                platformService.stopBroadcast(platform, deviceChannel, hookData.getStream(),  true, hookData.getMediaServer());
                             }else {
                                 // 查看语音通道已经建立但是未占用
-                                broadcastCatch.setApp(streamChangedHookParam.getApp());
-                                broadcastCatch.setStream(streamChangedHookParam.getStream());
-                                broadcastCatch.setMediaServerItem(mediaServerItem);
+                                broadcastCatch.setApp(hookData.getApp());
+                                broadcastCatch.setStream(hookData.getStream());
+                                broadcastCatch.setMediaServerItem(hookData.getMediaServer());
                                 audioBroadcastManager.update(broadcastCatch);
                                 // 推流到设备
-                                SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, targetId, streamChangedHookParam.getStream(), null);
+                                SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, targetId, hookData.getStream(), null);
                                 if (sendRtpItem == null) {
-                                    logger.warn("[国标级联] 语音喊话 异常,未找到发流信息, channelId: {}, stream: {}", targetId, streamChangedHookParam.getStream());
-                                    logger.info("[国标级联] 语音喊话 重新开始,channelId: {}, stream: {}", targetId, streamChangedHookParam.getStream());
+                                    logger.warn("[国标级联] 语音喊话 异常,未找到发流信息, channelId: {}, stream: {}", targetId, hookData.getStream());
+                                    logger.info("[国标级联] 语音喊话 重新开始,channelId: {}, stream: {}", targetId, hookData.getStream());
                                     try {
-                                        playService.audioBroadcastCmd(device, targetId, mediaServerItem, streamChangedHookParam.getApp(), streamChangedHookParam.getStream(), 60, true, msg -> {
+                                        playService.audioBroadcastCmd(device, targetId, hookData.getMediaServer(), hookData.getApp(), hookData.getStream(), 60, true, msg -> {
                                             logger.info("[语音喊话] 通道建立成功, device: {}, channel: {}", device.getDeviceId(), targetId);
                                         });
                                     } catch (SipException | InvalidArgumentException | ParseException e) {
@@ -158,7 +155,7 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp
                                     }
                                 }else {
                                     // 发流
-                                    JSONObject jsonObject = zlmServerFactory.startSendRtp(mediaServerItem, sendRtpItem);
+                                    JSONObject jsonObject = zlmServerFactory.startSendRtp(hookData.getMediaServer(), sendRtpItem);
                                     if (jsonObject != null && jsonObject.getInteger("code") == 0 ) {
                                         logger.info("[语音喊话] 自动推流成功, device: {}, channel: {}", device.getDeviceId(), targetId);
                                     }else {
@@ -168,7 +165,7 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp
                             }
                         }else {
                             try {
-                                playService.audioBroadcastCmd(device, targetId, mediaServerItem, streamChangedHookParam.getApp(), streamChangedHookParam.getStream(), 60, true, msg -> {
+                                playService.audioBroadcastCmd(device, targetId, hookData.getMediaServer(), hookData.getApp(), hookData.getStream(), 60, true, msg -> {
                                     logger.info("[语音喊话] 通道建立成功, device: {}, channel: {}", device.getDeviceId(), targetId);
                                 });
                             } catch (SipException | InvalidArgumentException | ParseException e) {

+ 6 - 7
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java

@@ -13,9 +13,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
+import com.genersoft.iot.vmp.media.event.hook.Hook;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.event.hook.HookType;
 import com.genersoft.iot.vmp.service.IInviteStreamService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
@@ -64,7 +64,7 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
     private VideoStreamSessionManager sessionManager;
 
     @Autowired
-    private ZlmHttpHookSubscribe subscribe;
+    private HookSubscribe subscribe;
 
     @Autowired
     private IInviteStreamService inviteStreamService;
@@ -106,9 +106,8 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
                     logger.error("[录像流]推送完毕,收到关流通知, 发送BYE失败 {}", e.getMessage());
                 }
                 // 去除监听流注销自动停止下载的监听
-                HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcTransaction.getStream(), false, "rtsp", ssrcTransaction.getMediaServerId());
-                subscribe.removeSubscribe(hookSubscribe);
-
+                Hook hook = Hook.getInstance(HookType.on_media_arrival, "rtp", ssrcTransaction.getStream(), ssrcTransaction.getMediaServerId());
+                subscribe.removeSubscribe(hook);
                 // 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题,需要将点播CallId进行上下级绑定
                 SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null);
                 if (sendRtpItem != null) {

+ 64 - 0
src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java

@@ -0,0 +1,64 @@
+package com.genersoft.iot.vmp.media;
+
+import com.genersoft.iot.vmp.conf.MediaConfig;
+import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 启动是从配置文件加载节点信息,以及发送个节点状态管理去控制节点状态
+ */
+@Component
+@Order(value=12)
+public class MediaServerConfig implements CommandLineRunner {
+
+    private final static Logger logger = LoggerFactory.getLogger(MediaServerConfig.class);
+
+    @Autowired
+    private ApplicationEventPublisher applicationEventPublisher;
+
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+    @Autowired
+    private MediaConfig mediaConfig;
+
+
+    @Override
+    public void run(String... strings) throws Exception {
+        // 清理所有在线节点的缓存信息
+        mediaServerService.clearMediaServerForOnline();
+        MediaServer defaultMediaServer = mediaServerService.getDefaultMediaServer();
+        MediaServer mediaSerItemInConfig = mediaConfig.getMediaSerItem();
+        if (defaultMediaServer != null && mediaSerItemInConfig.getId().equals(defaultMediaServer.getId())) {
+            mediaServerService.update(mediaSerItemInConfig);
+        }else {
+            if (defaultMediaServer != null) {
+                mediaServerService.delete(defaultMediaServer.getId());
+            }
+            MediaServer mediaServerItem = mediaServerService.getOneFromDatabase(mediaSerItemInConfig.getId());
+            if (mediaServerItem == null) {
+                mediaServerService.add(mediaSerItemInConfig);
+            }else {
+                mediaServerService.update(mediaSerItemInConfig);
+            }
+        }
+        // 发送媒体节点变化事件
+        mediaServerService.syncCatchFromDatabase();
+        // 获取所有的zlm, 并开启主动连接
+        List<MediaServer> all = mediaServerService.getAllFromDatabase();
+        logger.info("[媒体节点] 加载节点列表, 共{}个节点", all.size());
+        MediaServerChangeEvent event = new MediaServerChangeEvent(this);
+        event.setMediaServerItemList(all);
+        applicationEventPublisher.publishEvent(event);
+    }
+}

+ 306 - 0
src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java

@@ -0,0 +1,306 @@
+package com.genersoft.iot.vmp.media.bean;
+
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.util.List;
+
+/**
+ * 视频信息
+ */
+@Schema(description = "视频信息")
+public class MediaInfo {
+    @Schema(description = "应用名")
+    private String app;
+    @Schema(description = "流ID")
+    private String stream;
+    @Schema(description = "流媒体节点")
+    private MediaServer mediaServer;
+    @Schema(description = "协议")
+    private String schema;
+
+    @Schema(description = "观看人数")
+    private Integer readerCount;
+    @Schema(description = "视频编码类型")
+    private String videoCodec;
+    @Schema(description = "视频宽度")
+    private Integer width;
+    @Schema(description = "视频高度")
+    private Integer height;
+    @Schema(description = "音频编码类型")
+    private String audioCodec;
+    @Schema(description = "音频通道数")
+    private Integer audioChannels;
+    @Schema(description = "音频采样率")
+    private Integer audioSampleRate;
+    @Schema(description = "音频采样率")
+    private Long duration;
+    @Schema(description = "在线")
+    private Boolean online;
+    @Schema(description = "unknown = 0,rtmp_push=1,rtsp_push=2,rtp_push=3,pull=4,ffmpeg_pull=5,mp4_vod=6,device_chn=7")
+    private Integer originType;
+    @Schema(description = "存活时间,单位秒")
+    private Long aliveSecond;
+    @Schema(description = "数据产生速度,单位byte/s")
+    private Long bytesSpeed;
+
+    public static MediaInfo getInstance(JSONObject jsonObject, MediaServer mediaServer) {
+        MediaInfo mediaInfo = new MediaInfo();
+        mediaInfo.setMediaServer(mediaServer);
+        String app = jsonObject.getString("app");
+        mediaInfo.setApp(app);
+        String stream = jsonObject.getString("stream");
+        mediaInfo.setStream(stream);
+        String schema = jsonObject.getString("schema");
+        mediaInfo.setSchema(schema);
+        Integer totalReaderCount = jsonObject.getInteger("totalReaderCount");
+        Boolean online = jsonObject.getBoolean("online");
+        Integer originType = jsonObject.getInteger("originType");
+        Long aliveSecond = jsonObject.getLong("aliveSecond");
+        Long bytesSpeed = jsonObject.getLong("bytesSpeed");
+        if (totalReaderCount != null) {
+            mediaInfo.setReaderCount(totalReaderCount);
+        }
+        if (online != null) {
+            mediaInfo.setOnline(online);
+        }
+        if (originType != null) {
+            mediaInfo.setOriginType(originType);
+        }
+        if (aliveSecond != null) {
+            mediaInfo.setAliveSecond(aliveSecond);
+        }
+        if (bytesSpeed != null) {
+            mediaInfo.setBytesSpeed(bytesSpeed);
+        }
+        JSONArray jsonArray = jsonObject.getJSONArray("tracks");
+        if (jsonArray.isEmpty()) {
+            return null;
+        }
+        for (int i = 0; i < jsonArray.size(); i++) {
+            JSONObject trackJson = jsonArray.getJSONObject(i);
+            Integer channels = trackJson.getInteger("channels");
+            Integer codecId = trackJson.getInteger("codec_id");
+            Integer codecType = trackJson.getInteger("codec_type");
+            Integer sampleRate = trackJson.getInteger("sample_rate");
+            Integer height = trackJson.getInteger("height");
+            Integer width = trackJson.getInteger("height");
+            Long duration = trackJson.getLongValue("duration");
+            if (channels != null) {
+                mediaInfo.setAudioChannels(channels);
+            }
+            if (sampleRate != null) {
+                mediaInfo.setAudioSampleRate(sampleRate);
+            }
+            if (height != null) {
+                mediaInfo.setHeight(height);
+            }
+            if (width != null) {
+                mediaInfo.setWidth(width);
+            }
+            if (duration > 0L) {
+                mediaInfo.setDuration(duration);
+            }
+            if (codecId != null) {
+                switch (codecId) {
+                    case 0:
+                        mediaInfo.setVideoCodec("H264");
+                        break;
+                    case 1:
+                        mediaInfo.setVideoCodec("H265");
+                        break;
+                    case 2:
+                        mediaInfo.setAudioCodec("AAC");
+                        break;
+                    case 3:
+                        mediaInfo.setAudioCodec("G711A");
+                        break;
+                    case 4:
+                        mediaInfo.setAudioCodec("G711U");
+                        break;
+                }
+            }
+        }
+        return mediaInfo;
+    }
+
+    public static MediaInfo getInstance(OnStreamChangedHookParam param, MediaServer mediaServer) {
+
+        MediaInfo mediaInfo = new MediaInfo();
+        mediaInfo.setApp(param.getApp());
+        mediaInfo.setStream(param.getStream());
+        mediaInfo.setSchema(param.getSchema());
+        mediaInfo.setMediaServer(mediaServer);
+        mediaInfo.setReaderCount(param.getTotalReaderCount());
+        mediaInfo.setOnline(param.isRegist());
+        mediaInfo.setOriginType(param.getOriginType());
+        mediaInfo.setAliveSecond(param.getAliveSecond());
+        mediaInfo.setBytesSpeed(param.getBytesSpeed());
+        List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
+        if (tracks == null || tracks.isEmpty()) {
+            return mediaInfo;
+        }
+        for (OnStreamChangedHookParam.MediaTrack mediaTrack : tracks) {
+            switch (mediaTrack.getCodec_id()) {
+                case 0:
+                    mediaInfo.setVideoCodec("H264");
+                    break;
+                case 1:
+                    mediaInfo.setVideoCodec("H265");
+                    break;
+                case 2:
+                    mediaInfo.setAudioCodec("AAC");
+                    break;
+                case 3:
+                    mediaInfo.setAudioCodec("G711A");
+                    break;
+                case 4:
+                    mediaInfo.setAudioCodec("G711U");
+                    break;
+            }
+            if (mediaTrack.getSample_rate() > 0) {
+                mediaInfo.setAudioSampleRate(mediaTrack.getSample_rate());
+            }
+            if (mediaTrack.getChannels() > 0) {
+                mediaInfo.setAudioChannels(mediaTrack.getChannels());
+            }
+            if (mediaTrack.getHeight() > 0) {
+                mediaInfo.setHeight(mediaTrack.getHeight());
+            }
+            if (mediaTrack.getWidth() > 0) {
+                mediaInfo.setWidth(mediaTrack.getWidth());
+            }
+        }
+        return mediaInfo;
+    }
+
+    public Integer getReaderCount() {
+        return readerCount;
+    }
+
+    public void setReaderCount(Integer readerCount) {
+        this.readerCount = readerCount;
+    }
+
+    public String getVideoCodec() {
+        return videoCodec;
+    }
+
+    public void setVideoCodec(String videoCodec) {
+        this.videoCodec = videoCodec;
+    }
+
+    public Integer getWidth() {
+        return width;
+    }
+
+    public void setWidth(Integer width) {
+        this.width = width;
+    }
+
+    public Integer getHeight() {
+        return height;
+    }
+
+    public void setHeight(Integer height) {
+        this.height = height;
+    }
+
+    public String getAudioCodec() {
+        return audioCodec;
+    }
+
+    public void setAudioCodec(String audioCodec) {
+        this.audioCodec = audioCodec;
+    }
+
+    public Integer getAudioChannels() {
+        return audioChannels;
+    }
+
+    public void setAudioChannels(Integer audioChannels) {
+        this.audioChannels = audioChannels;
+    }
+
+    public Integer getAudioSampleRate() {
+        return audioSampleRate;
+    }
+
+    public void setAudioSampleRate(Integer audioSampleRate) {
+        this.audioSampleRate = audioSampleRate;
+    }
+
+    public Long getDuration() {
+        return duration;
+    }
+
+    public void setDuration(Long duration) {
+        this.duration = duration;
+    }
+
+    public Boolean getOnline() {
+        return online;
+    }
+
+    public void setOnline(Boolean online) {
+        this.online = online;
+    }
+
+    public Integer getOriginType() {
+        return originType;
+    }
+
+    public void setOriginType(Integer originType) {
+        this.originType = originType;
+    }
+
+    public Long getAliveSecond() {
+        return aliveSecond;
+    }
+
+    public void setAliveSecond(Long aliveSecond) {
+        this.aliveSecond = aliveSecond;
+    }
+
+    public Long getBytesSpeed() {
+        return bytesSpeed;
+    }
+
+    public void setBytesSpeed(Long bytesSpeed) {
+        this.bytesSpeed = bytesSpeed;
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+
+    public MediaServer getMediaServer() {
+        return mediaServer;
+    }
+
+    public void setMediaServer(MediaServer mediaServer) {
+        this.mediaServer = mediaServer;
+    }
+
+    public String getSchema() {
+        return schema;
+    }
+
+    public void setSchema(String schema) {
+        this.schema = schema;
+    }
+}

+ 63 - 5
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java

@@ -1,12 +1,12 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
+package com.genersoft.iot.vmp.media.bean;
 
 
-import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
+import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig;
 import io.swagger.v3.oas.annotations.media.Schema;
 import org.springframework.util.ObjectUtils;
 
 @Schema(description = "流媒体服务信息")
-public class MediaServerItem{
+public class MediaServer {
 
     @Schema(description = "ID")
     private String id;
@@ -32,6 +32,18 @@ public class MediaServerItem{
     @Schema(description = "RTMP端口")
     private int rtmpPort;
 
+    @Schema(description = "flv端口")
+    private int flvPort;
+
+    @Schema(description = "https-flv端口")
+    private int flvSSLPort;
+
+    @Schema(description = "ws-flv端口")
+    private int wsFlvPort;
+
+    @Schema(description = "wss-flv端口")
+    private int wsFlvSSLPort;
+
     @Schema(description = "RTMPS端口")
     private int rtmpSSlPort;
 
@@ -85,18 +97,24 @@ public class MediaServerItem{
 
     @Schema(description = "录像存储路径")
     private String recordPath;
+    @Schema(description = "类型: zlm/abl")
+    private String type;
 
-    public MediaServerItem() {
+    public MediaServer() {
     }
 
-    public MediaServerItem(ZLMServerConfig zlmServerConfig, String sipIp) {
+    public MediaServer(ZLMServerConfig zlmServerConfig, String sipIp) {
         id = zlmServerConfig.getGeneralMediaServerId();
         ip = zlmServerConfig.getIp();
         hookIp = ObjectUtils.isEmpty(zlmServerConfig.getHookIp())? sipIp: zlmServerConfig.getHookIp();
         sdpIp = ObjectUtils.isEmpty(zlmServerConfig.getSdpIp())? zlmServerConfig.getIp(): zlmServerConfig.getSdpIp();
         streamIp = ObjectUtils.isEmpty(zlmServerConfig.getStreamIp())? zlmServerConfig.getIp(): zlmServerConfig.getStreamIp();
         httpPort = zlmServerConfig.getHttpPort();
+        flvPort = zlmServerConfig.getHttpPort();
+        wsFlvPort = zlmServerConfig.getHttpPort();
         httpSSlPort = zlmServerConfig.getHttpSSLport();
+        flvSSLPort = zlmServerConfig.getHttpSSLport();
+        wsFlvSSLPort = zlmServerConfig.getHttpSSLport();
         rtmpPort = zlmServerConfig.getRtmpPort();
         rtmpSSlPort = zlmServerConfig.getRtmpSslPort();
         rtpProxyPort = zlmServerConfig.getRtpProxyPort();
@@ -318,4 +336,44 @@ public class MediaServerItem{
     public void setRecordPath(String recordPath) {
         this.recordPath = recordPath;
     }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public int getFlvPort() {
+        return flvPort;
+    }
+
+    public void setFlvPort(int flvPort) {
+        this.flvPort = flvPort;
+    }
+
+    public int getFlvSSLPort() {
+        return flvSSLPort;
+    }
+
+    public void setFlvSSLPort(int flvSSLPort) {
+        this.flvSSLPort = flvSSLPort;
+    }
+
+    public int getWsFlvPort() {
+        return wsFlvPort;
+    }
+
+    public void setWsFlvPort(int wsFlvPort) {
+        this.wsFlvPort = wsFlvPort;
+    }
+
+    public int getWsFlvSSLPort() {
+        return wsFlvSSLPort;
+    }
+
+    public void setWsFlvSSLPort(int wsFlvSSLPort) {
+        this.wsFlvSSLPort = wsFlvSSLPort;
+    }
 }

+ 92 - 0
src/main/java/com/genersoft/iot/vmp/media/bean/RecordInfo.java

@@ -0,0 +1,92 @@
+package com.genersoft.iot.vmp.media.bean;
+
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
+
+public class RecordInfo {
+    private String fileName;
+    private String filePath;
+    private long fileSize;
+    private String folder;
+    private String url;
+    private long startTime;
+    private double timeLen;
+
+    public static RecordInfo getInstance(OnRecordMp4HookParam hookParam) {
+        RecordInfo recordInfo = new RecordInfo();
+        recordInfo.setFileName(hookParam.getFile_name());
+        recordInfo.setUrl(hookParam.getUrl());
+        recordInfo.setFolder(hookParam.getFolder());
+        recordInfo.setFilePath(hookParam.getFile_path());
+        recordInfo.setFileSize(hookParam.getFile_size());
+        recordInfo.setStartTime(hookParam.getStart_time());
+        recordInfo.setTimeLen(hookParam.getTime_len());
+        return recordInfo;
+    }
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+
+    public String getFilePath() {
+        return filePath;
+    }
+
+    public void setFilePath(String filePath) {
+        this.filePath = filePath;
+    }
+
+    public long getFileSize() {
+        return fileSize;
+    }
+
+    public void setFileSize(long fileSize) {
+        this.fileSize = fileSize;
+    }
+
+    public String getFolder() {
+        return folder;
+    }
+
+    public void setFolder(String folder) {
+        this.folder = folder;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public long getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(long startTime) {
+        this.startTime = startTime;
+    }
+
+    public double getTimeLen() {
+        return timeLen;
+    }
+
+    public void setTimeLen(double timeLen) {
+        this.timeLen = timeLen;
+    }
+
+    @Override
+    public String toString() {
+        return "RecordInfo{" +
+                "文件名称='" + fileName + '\'' +
+                ", 文件路径='" + filePath + '\'' +
+                ", 文件大小=" + fileSize +
+                ", 开始时间=" + startTime +
+                ", 时长=" + timeLen +
+                '}';
+    }
+}

+ 59 - 0
src/main/java/com/genersoft/iot/vmp/media/bean/ResultForOnPublish.java

@@ -0,0 +1,59 @@
+package com.genersoft.iot.vmp.media.bean;
+
+public class ResultForOnPublish {
+
+    private boolean enable_audio;
+    private boolean enable_mp4;
+    private int mp4_max_second;
+    private String mp4_save_path;
+    private String stream_replace;
+    private Integer modify_stamp;
+
+    public boolean isEnable_audio() {
+        return enable_audio;
+    }
+
+    public void setEnable_audio(boolean enable_audio) {
+        this.enable_audio = enable_audio;
+    }
+
+    public boolean isEnable_mp4() {
+        return enable_mp4;
+    }
+
+    public void setEnable_mp4(boolean enable_mp4) {
+        this.enable_mp4 = enable_mp4;
+    }
+
+    public int getMp4_max_second() {
+        return mp4_max_second;
+    }
+
+    public void setMp4_max_second(int mp4_max_second) {
+        this.mp4_max_second = mp4_max_second;
+    }
+
+    public String getMp4_save_path() {
+        return mp4_save_path;
+    }
+
+    public void setMp4_save_path(String mp4_save_path) {
+        this.mp4_save_path = mp4_save_path;
+    }
+
+    public String getStream_replace() {
+        return stream_replace;
+    }
+
+    public void setStream_replace(String stream_replace) {
+        this.stream_replace = stream_replace;
+    }
+
+    public Integer getModify_stamp() {
+        return modify_stamp;
+    }
+
+    public void setModify_stamp(Integer modify_stamp) {
+        this.modify_stamp = modify_stamp;
+    }
+}

+ 86 - 0
src/main/java/com/genersoft/iot/vmp/media/event/hook/Hook.java

@@ -0,0 +1,86 @@
+package com.genersoft.iot.vmp.media.event.hook;
+
+/**
+ * zlm hook事件的参数
+ * @author lin
+ */
+public class Hook {
+
+    private HookType hookType;
+
+    private String app;
+
+    private String stream;
+
+    private String mediaServerId;
+
+    private Long createTime;
+
+    public static Hook getInstance(HookType hookType, String app, String stream, String mediaServerId) {
+        Hook hookSubscribe = new Hook();
+        hookSubscribe.setApp(app);
+        hookSubscribe.setStream(stream);
+        hookSubscribe.setHookType(hookType);
+        hookSubscribe.setMediaServerId(mediaServerId);
+        hookSubscribe.setCreateTime(System.currentTimeMillis());
+        return hookSubscribe;
+    }
+
+    public HookType getHookType() {
+        return hookType;
+    }
+
+    public void setHookType(HookType hookType) {
+        this.hookType = hookType;
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+
+    public Long getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Long createTime) {
+        this.createTime = createTime;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Hook) {
+            Hook param = (Hook) obj;
+            return param.getHookType().equals(this.hookType)
+                    && param.getApp().equals(this.app)
+                    && param.getStream().equals(this.stream)
+                    && param.getMediaServerId().equals(this.mediaServerId);
+        }else {
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return this.getHookType() + this.getApp() + this.getStream() + this.getMediaServerId();
+    }
+}

+ 132 - 0
src/main/java/com/genersoft/iot/vmp/media/event/hook/HookData.java

@@ -0,0 +1,132 @@
+package com.genersoft.iot.vmp.media.event.hook;
+
+import com.genersoft.iot.vmp.media.bean.MediaInfo;
+import com.genersoft.iot.vmp.media.bean.RecordInfo;
+import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
+import com.genersoft.iot.vmp.media.event.media.MediaEvent;
+import com.genersoft.iot.vmp.media.event.media.MediaPublishEvent;
+import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * Hook返回的内容
+ */
+public class HookData {
+    /**
+     * 应用名
+     */
+    private String app;
+    /**
+     * 流ID
+     */
+    private String stream;
+    /**
+     * 流媒体节点
+     */
+    private MediaServer mediaServer;
+    /**
+     * 协议
+     */
+    private String schema;
+
+    /**
+     * 流信息
+     */
+    private MediaInfo mediaInfo;
+
+    /**
+     * 录像信息
+     */
+    private RecordInfo recordInfo;
+
+    @Schema(description = "推流的额外参数")
+    private String params;
+    public static HookData getInstance(MediaEvent mediaEvent) {
+        HookData hookData = new HookData();
+        if (mediaEvent instanceof MediaPublishEvent) {
+            MediaPublishEvent event = (MediaPublishEvent) mediaEvent;
+            hookData.setApp(event.getApp());
+            hookData.setStream(event.getStream());
+            hookData.setSchema(event.getSchema());
+            hookData.setMediaServer(event.getMediaServer());
+            hookData.setParams(event.getParams());
+        }else if (mediaEvent instanceof MediaArrivalEvent) {
+            MediaArrivalEvent event = (MediaArrivalEvent) mediaEvent;
+            hookData.setApp(event.getApp());
+            hookData.setStream(event.getStream());
+            hookData.setSchema(event.getSchema());
+            hookData.setMediaServer(event.getMediaServer());
+            hookData.setMediaInfo(event.getMediaInfo());
+        }else if (mediaEvent instanceof MediaRecordMp4Event) {
+            MediaRecordMp4Event event = (MediaRecordMp4Event) mediaEvent;
+            hookData.setApp(event.getApp());
+            hookData.setStream(event.getStream());
+            hookData.setSchema(event.getSchema());
+            hookData.setMediaServer(event.getMediaServer());
+            hookData.setRecordInfo(event.getRecordInfo());
+        }else {
+            hookData.setApp(mediaEvent.getApp());
+            hookData.setStream(mediaEvent.getStream());
+            hookData.setSchema(mediaEvent.getSchema());
+            hookData.setMediaServer(mediaEvent.getMediaServer());
+        }
+        return hookData;
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+
+    public MediaServer getMediaServer() {
+        return mediaServer;
+    }
+
+    public void setMediaServer(MediaServer mediaServer) {
+        this.mediaServer = mediaServer;
+    }
+
+    public String getSchema() {
+        return schema;
+    }
+
+    public void setSchema(String schema) {
+        this.schema = schema;
+    }
+
+    public MediaInfo getMediaInfo() {
+        return mediaInfo;
+    }
+
+    public void setMediaInfo(MediaInfo mediaInfo) {
+        this.mediaInfo = mediaInfo;
+    }
+
+    public String getParams() {
+        return params;
+    }
+
+    public void setParams(String params) {
+        this.params = params;
+    }
+
+    public RecordInfo getRecordInfo() {
+        return recordInfo;
+    }
+
+    public void setRecordInfo(RecordInfo recordInfo) {
+        this.recordInfo = recordInfo;
+    }
+}

+ 107 - 0
src/main/java/com/genersoft/iot/vmp/media/event/hook/HookSubscribe.java

@@ -0,0 +1,107 @@
+package com.genersoft.iot.vmp.media.event.hook;
+
+import com.genersoft.iot.vmp.media.event.media.*;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * zlm hook事件的参数
+ * @author lin
+ */
+@Component
+public class HookSubscribe {
+
+    /**
+     * 订阅数据过期时间
+     */
+    private final long subscribeExpire = 5 * 60 * 1000;
+
+    @FunctionalInterface
+    public interface Event{
+        void response(HookData data);
+    }
+
+    /**
+     * 流到来的处理
+     */
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaArrivalEvent event) {
+        if (event.getSchema() == null || "rtsp".equals(event.getSchema())) {
+            sendNotify(HookType.on_media_arrival, event);
+        }
+
+    }
+
+    /**
+     * 流结束事件
+     */
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaDepartureEvent event) {
+        if (event.getSchema() == null || "rtsp".equals(event.getSchema())) {
+            sendNotify(HookType.on_media_departure, event);
+        }
+
+    }
+    /**
+     * 推流鉴权事件
+     */
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaPublishEvent event) {
+        sendNotify(HookType.on_publish, event);
+    }
+    /**
+     * 推流鉴权事件
+     */
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaRecordMp4Event event) {
+        sendNotify(HookType.on_record_mp4, event);
+    }
+
+    private final Map<String, Event> allSubscribes = new ConcurrentHashMap<>();
+    private final Map<String, Hook> allHook = new ConcurrentHashMap<>();
+
+    private void sendNotify(HookType hookType, MediaEvent event) {
+        Hook paramHook = Hook.getInstance(hookType, event.getApp(), event.getStream(), event.getMediaServer().getId());
+        Event hookSubscribeEvent = allSubscribes.get(paramHook.toString());
+        if (hookSubscribeEvent != null) {
+            HookData data = HookData.getInstance(event);
+            hookSubscribeEvent.response(data);
+        }
+    }
+
+    public void addSubscribe(Hook hook, HookSubscribe.Event event) {
+        if (hook.getCreateTime() == null) {
+            hook.setCreateTime(System.currentTimeMillis());
+        }
+        allSubscribes.put(hook.toString(), event);
+        allHook.put(hook.toString(), hook);
+    }
+
+    public void removeSubscribe(Hook hook) {
+        allSubscribes.remove(hook.toString());
+        allHook.remove(hook.toString());
+    }
+
+    /**
+     * 对订阅数据进行过期清理
+     */
+    @Scheduled(fixedRate=subscribeExpire)   //每5分钟执行一次
+    public void execute(){
+        long expireTime = System.currentTimeMillis() - subscribeExpire;
+        for (Hook hook : allHook.values()) {
+            if (hook.getCreateTime() < expireTime) {
+                allSubscribes.remove(hook.toString());
+                allHook.remove(hook.toString());
+            }
+        }
+    }
+}

+ 15 - 0
src/main/java/com/genersoft/iot/vmp/media/event/hook/HookType.java

@@ -0,0 +1,15 @@
+package com.genersoft.iot.vmp.media.event.hook;
+
+/**
+ * hook类型
+ * @author lin
+ */
+
+public enum HookType {
+
+    on_publish,
+    on_record_mp4,
+    on_media_arrival,
+    on_media_departure,
+    on_rtp_server_timeout,
+}

+ 46 - 0
src/main/java/com/genersoft/iot/vmp/media/event/media/MediaArrivalEvent.java

@@ -0,0 +1,46 @@
+package com.genersoft.iot.vmp.media.event.media;
+
+import com.genersoft.iot.vmp.media.bean.MediaInfo;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
+
+/**
+ * 流到来事件
+ */
+public class MediaArrivalEvent extends MediaEvent {
+    public MediaArrivalEvent(Object source) {
+        super(source);
+    }
+
+    public static MediaArrivalEvent getInstance(Object source, OnStreamChangedHookParam hookParam, MediaServer mediaServer){
+        MediaArrivalEvent mediaArrivalEvent = new MediaArrivalEvent(source);
+        mediaArrivalEvent.setMediaInfo(MediaInfo.getInstance(hookParam, mediaServer));
+        mediaArrivalEvent.setApp(hookParam.getApp());
+        mediaArrivalEvent.setStream(hookParam.getStream());
+        mediaArrivalEvent.setMediaServer(mediaServer);
+        mediaArrivalEvent.setSchema(hookParam.getSchema());
+        mediaArrivalEvent.setCallId(hookParam.getCallId());
+        return mediaArrivalEvent;
+    }
+
+    private MediaInfo mediaInfo;
+
+    private String callId;
+
+    public MediaInfo getMediaInfo() {
+        return mediaInfo;
+    }
+
+    public void setMediaInfo(MediaInfo mediaInfo) {
+        this.mediaInfo = mediaInfo;
+    }
+
+
+    public String getCallId() {
+        return callId;
+    }
+
+    public void setCallId(String callId) {
+        this.callId = callId;
+    }
+}

+ 22 - 0
src/main/java/com/genersoft/iot/vmp/media/event/media/MediaDepartureEvent.java

@@ -0,0 +1,22 @@
+package com.genersoft.iot.vmp.media.event.media;
+
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
+
+/**
+ * 流离开事件
+ */
+public class MediaDepartureEvent extends MediaEvent {
+    public MediaDepartureEvent(Object source) {
+        super(source);
+    }
+
+    public static MediaDepartureEvent getInstance(Object source, OnStreamChangedHookParam hookParam, MediaServer mediaServer){
+        MediaDepartureEvent mediaDepartureEven = new MediaDepartureEvent(source);
+        mediaDepartureEven.setApp(hookParam.getApp());
+        mediaDepartureEven.setStream(hookParam.getStream());
+        mediaDepartureEven.setSchema(hookParam.getSchema());
+        mediaDepartureEven.setMediaServer(mediaServer);
+        return mediaDepartureEven;
+    }
+}

+ 56 - 0
src/main/java/com/genersoft/iot/vmp/media/event/media/MediaEvent.java

@@ -0,0 +1,56 @@
+package com.genersoft.iot.vmp.media.event.media;
+
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * 流到来事件
+ */
+public class MediaEvent extends ApplicationEvent {
+
+    public MediaEvent(Object source) {
+        super(source);
+    }
+
+    private String app;
+
+    private String stream;
+
+    private MediaServer mediaServer;
+
+    private String schema;
+
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+
+    public MediaServer getMediaServer() {
+        return mediaServer;
+    }
+
+    public void setMediaServer(MediaServer mediaServer) {
+        this.mediaServer = mediaServer;
+    }
+
+    public String getSchema() {
+        return schema;
+    }
+
+    public void setSchema(String schema) {
+        this.schema = schema;
+    }
+
+}

+ 22 - 0
src/main/java/com/genersoft/iot/vmp/media/event/media/MediaNotFoundEvent.java

@@ -0,0 +1,22 @@
+package com.genersoft.iot.vmp.media.event.media;
+
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamNotFoundHookParam;
+
+/**
+ * 流未找到
+ */
+public class MediaNotFoundEvent extends MediaEvent {
+    public MediaNotFoundEvent(Object source) {
+        super(source);
+    }
+
+    public static MediaNotFoundEvent getInstance(Object source, OnStreamNotFoundHookParam hookParam, MediaServer mediaServer){
+        MediaNotFoundEvent mediaDepartureEven = new MediaNotFoundEvent(source);
+        mediaDepartureEven.setApp(hookParam.getApp());
+        mediaDepartureEven.setStream(hookParam.getStream());
+        mediaDepartureEven.setSchema(hookParam.getSchema());
+        mediaDepartureEven.setMediaServer(mediaServer);
+        return mediaDepartureEven;
+    }
+}

+ 33 - 0
src/main/java/com/genersoft/iot/vmp/media/event/media/MediaPublishEvent.java

@@ -0,0 +1,33 @@
+package com.genersoft.iot.vmp.media.event.media;
+
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnPublishHookParam;
+
+/**
+ * 推流鉴权事件
+ */
+public class MediaPublishEvent extends MediaEvent {
+    public MediaPublishEvent(Object source) {
+        super(source);
+    }
+
+    public static MediaPublishEvent getInstance(Object source, OnPublishHookParam hookParam, MediaServer mediaServer){
+        MediaPublishEvent mediaPublishEvent = new MediaPublishEvent(source);
+        mediaPublishEvent.setApp(hookParam.getApp());
+        mediaPublishEvent.setStream(hookParam.getStream());
+        mediaPublishEvent.setMediaServer(mediaServer);
+        mediaPublishEvent.setSchema(hookParam.getSchema());
+        mediaPublishEvent.setParams(hookParam.getParams());
+        return mediaPublishEvent;
+    }
+
+    private String params;
+
+    public String getParams() {
+        return params;
+    }
+
+    public void setParams(String params) {
+        this.params = params;
+    }
+}

+ 35 - 0
src/main/java/com/genersoft/iot/vmp/media/event/media/MediaRecordMp4Event.java

@@ -0,0 +1,35 @@
+package com.genersoft.iot.vmp.media.event.media;
+
+import com.genersoft.iot.vmp.media.bean.RecordInfo;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
+
+/**
+ * 录像文件生成事件
+ */
+public class MediaRecordMp4Event extends MediaEvent {
+    public MediaRecordMp4Event(Object source) {
+        super(source);
+    }
+
+    private RecordInfo recordInfo;
+
+    public static MediaRecordMp4Event getInstance(Object source, OnRecordMp4HookParam hookParam, MediaServer mediaServer){
+        MediaRecordMp4Event mediaRecordMp4Event = new MediaRecordMp4Event(source);
+        mediaRecordMp4Event.setApp(hookParam.getApp());
+        mediaRecordMp4Event.setStream(hookParam.getStream());
+        RecordInfo recordInfo = RecordInfo.getInstance(hookParam);
+        mediaRecordMp4Event.setRecordInfo(recordInfo);
+        mediaRecordMp4Event.setMediaServer(mediaServer);
+        return mediaRecordMp4Event;
+    }
+
+    public RecordInfo getRecordInfo() {
+        return recordInfo;
+    }
+
+    public void setRecordInfo(RecordInfo recordInfo) {
+        this.recordInfo = recordInfo;
+    }
+
+}

+ 22 - 0
src/main/java/com/genersoft/iot/vmp/media/event/media/MediaRtpServerTimeoutEvent.java

@@ -0,0 +1,22 @@
+package com.genersoft.iot.vmp.media.event.media;
+
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
+
+/**
+ * RtpServer收流超时事件
+ */
+public class MediaRtpServerTimeoutEvent extends MediaEvent {
+    public MediaRtpServerTimeoutEvent(Object source) {
+        super(source);
+    }
+
+    public static MediaRtpServerTimeoutEvent getInstance(Object source, OnStreamChangedHookParam hookParam, MediaServer mediaServer){
+        MediaRtpServerTimeoutEvent mediaDepartureEven = new MediaRtpServerTimeoutEvent(source);
+        mediaDepartureEven.setApp(hookParam.getApp());
+        mediaDepartureEven.setStream(hookParam.getStream());
+        mediaDepartureEven.setSchema(hookParam.getSchema());
+        mediaDepartureEven.setMediaServer(mediaServer);
+        return mediaDepartureEven;
+    }
+}

+ 52 - 0
src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaSendRtpStoppedEvent.java

@@ -0,0 +1,52 @@
+package com.genersoft.iot.vmp.media.event.mediaServer;
+
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamNotFoundHookParam;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * 发送流停止事件
+ */
+public class MediaSendRtpStoppedEvent extends ApplicationEvent {
+    public MediaSendRtpStoppedEvent(Object source) {
+        super(source);
+    }
+
+    private String app;
+
+    private String stream;
+
+    private MediaServer mediaServer;
+
+    public static MediaSendRtpStoppedEvent getInstance(Object source, OnStreamNotFoundHookParam hookParam, MediaServer mediaServer){
+        MediaSendRtpStoppedEvent mediaDepartureEven = new MediaSendRtpStoppedEvent(source);
+        mediaDepartureEven.setApp(hookParam.getApp());
+        mediaDepartureEven.setStream(hookParam.getStream());
+        mediaDepartureEven.setMediaServer(mediaServer);
+        return mediaDepartureEven;
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+
+    public MediaServer getMediaServer() {
+        return mediaServer;
+    }
+
+    public void setMediaServer(MediaServer mediaServer) {
+        this.mediaServer = mediaServer;
+    }
+}

+ 34 - 0
src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerChangeEvent.java

@@ -0,0 +1,34 @@
+package com.genersoft.iot.vmp.media.event.mediaServer;
+
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import org.springframework.context.ApplicationEvent;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class MediaServerChangeEvent extends ApplicationEvent {
+
+    public MediaServerChangeEvent(Object source) {
+        super(source);
+    }
+
+    private List<MediaServer> mediaServerItemList;
+
+    public List<MediaServer> getMediaServerItemList() {
+        return mediaServerItemList;
+    }
+
+    public void setMediaServerItemList(List<MediaServer> mediaServerItemList) {
+        this.mediaServerItemList = mediaServerItemList;
+    }
+
+    public void setMediaServerItemList(MediaServer... mediaServerItemArray) {
+        this.mediaServerItemList = new ArrayList<>();
+        this.mediaServerItemList.addAll(Arrays.asList(mediaServerItemArray));
+    }
+
+    public void setMediaServerItem(List<MediaServer> mediaServerItemList) {
+        this.mediaServerItemList = mediaServerItemList;
+    }
+}

+ 11 - 0
src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerDeleteEvent.java

@@ -0,0 +1,11 @@
+package com.genersoft.iot.vmp.media.event.mediaServer;
+
+/**
+ * zlm在线事件
+ */
+public class MediaServerDeleteEvent extends MediaServerEventAbstract {
+
+	public MediaServerDeleteEvent(Object source) {
+		super(source);
+	}
+}

+ 3 - 4
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMEventAbstract.java

@@ -1,9 +1,8 @@
-package com.genersoft.iot.vmp.media.zlm.event;
+package com.genersoft.iot.vmp.media.event.mediaServer;
 
-import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
 import org.springframework.context.ApplicationEvent;
 
-public abstract class ZLMEventAbstract extends ApplicationEvent {
+public abstract class MediaServerEventAbstract extends ApplicationEvent {
 
 
     private static final long serialVersionUID = 1L;
@@ -11,7 +10,7 @@ public abstract class ZLMEventAbstract extends ApplicationEvent {
     private String mediaServerId;
 
 
-    public ZLMEventAbstract(Object source) {
+    public MediaServerEventAbstract(Object source) {
         super(source);
     }
 

+ 11 - 0
src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerOfflineEvent.java

@@ -0,0 +1,11 @@
+package com.genersoft.iot.vmp.media.event.mediaServer;
+
+/**
+ * zlm离线事件类
+ */
+public class MediaServerOfflineEvent extends MediaServerEventAbstract {
+
+	public MediaServerOfflineEvent(Object source) {
+		super(source);
+	}
+}

+ 11 - 0
src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerOnlineEvent.java

@@ -0,0 +1,11 @@
+package com.genersoft.iot.vmp.media.event.mediaServer;
+
+/**
+ * zlm在线事件
+ */
+public class MediaServerOnlineEvent extends MediaServerEventAbstract {
+
+	public MediaServerOnlineEvent(Object source) {
+		super(source);
+	}
+}

+ 7 - 12
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java

@@ -1,6 +1,5 @@
-package com.genersoft.iot.vmp.media.zlm.event;
+package com.genersoft.iot.vmp.media.event.mediaServer;
 
-import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IPlayService;
 import com.genersoft.iot.vmp.service.IStreamProxyService;
 import com.genersoft.iot.vmp.service.IStreamPushService;
@@ -20,9 +19,9 @@ import org.springframework.stereotype.Component;
  * @date: 2020年5月6日 下午1:51:23
  */
 @Component
-public class ZLMStatusEventListener {
+public class MediaServerStatusEventListener {
 	
-	private final static Logger logger = LoggerFactory.getLogger(ZLMStatusEventListener.class);
+	private final static Logger logger = LoggerFactory.getLogger(MediaServerStatusEventListener.class);
 
 	@Autowired
 	private IStreamPushService streamPushService;
@@ -30,16 +29,13 @@ public class ZLMStatusEventListener {
 	@Autowired
 	private IStreamProxyService streamProxyService;
 
-	@Autowired
-	private IMediaServerService mediaServerService;
-
 	@Autowired
 	private IPlayService playService;
 
 	@Async("taskExecutor")
 	@EventListener
-	public void onApplicationEvent(ZLMOnlineEvent event) {
-		logger.info("[ZLM] 上线 ID:" + event.getMediaServerId());
+	public void onApplicationEvent(MediaServerOnlineEvent event) {
+		logger.info("[媒体节点] 上线 ID:" + event.getMediaServerId());
 		streamPushService.zlmServerOnline(event.getMediaServerId());
 		streamProxyService.zlmServerOnline(event.getMediaServerId());
 		playService.zlmServerOnline(event.getMediaServerId());
@@ -47,11 +43,10 @@ public class ZLMStatusEventListener {
 
 	@Async("taskExecutor")
 	@EventListener
-	public void onApplicationEvent(ZLMOfflineEvent event) {
+	public void onApplicationEvent(MediaServerOfflineEvent event) {
 
-		logger.info("[ZLM] 离线,ID:" + event.getMediaServerId());
+		logger.info("[媒体节点] 离线,ID:" + event.getMediaServerId());
 		// 处理ZLM离线
-		mediaServerService.zlmServerOffline(event.getMediaServerId());
 		streamProxyService.zlmServerOffline(event.getMediaServerId());
 		streamPushService.zlmServerOffline(event.getMediaServerId());
 		playService.zlmServerOffline(event.getMediaServerId());

+ 61 - 0
src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java

@@ -0,0 +1,61 @@
+package com.genersoft.iot.vmp.media.service;
+
+import com.genersoft.iot.vmp.common.CommonCallback;
+import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.media.bean.MediaInfo;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
+
+import java.util.List;
+import java.util.Map;
+
+public interface IMediaNodeServerService {
+    int createRTPServer(MediaServer mediaServer, String streamId, long ssrc, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode);
+
+    void closeRtpServer(MediaServer mediaServer, String streamId);
+
+    void closeRtpServer(MediaServer mediaServer, String streamId, CommonCallback<Boolean> callback);
+
+    void closeStreams(MediaServer mediaServer, String app, String stream);
+
+    Boolean updateRtpServerSSRC(MediaServer mediaServer, String stream, String ssrc);
+
+    boolean checkNodeId(MediaServer mediaServer);
+
+    void online(MediaServer mediaServer);
+
+    MediaServer checkMediaServer(String ip, int port, String secret);
+
+    boolean stopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc);
+
+    boolean deleteRecordDirectory(MediaServer mediaServer, String app, String stream, String date, String fileName);
+
+    List<StreamInfo> getMediaList(MediaServer mediaServer, String app, String stream, String callId);
+
+    Boolean connectRtpServer(MediaServer mediaServer, String address, int port, String stream);
+
+    void getSnap(MediaServer mediaServer, String streamUrl, int timeoutSec, int expireSec, String path, String fileName);
+
+    MediaInfo getMediaInfo(MediaServer mediaServer, String app, String stream);
+
+    Boolean pauseRtpCheck(MediaServer mediaServer, String streamKey);
+
+    Boolean resumeRtpCheck(MediaServer mediaServer, String streamKey);
+
+    String getFfmpegCmd(MediaServer mediaServer, String cmdKey);
+
+    WVPResult<String> addFFmpegSource(MediaServer mediaServer, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey);
+
+    WVPResult<String> addStreamProxy(MediaServer mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType);
+
+    Boolean delFFmpegSource(MediaServer mediaServer, String streamKey);
+
+    Boolean delStreamProxy(MediaServer mediaServer, String streamKey);
+
+    Map<String, String> getFFmpegCMDs(MediaServer mediaServer);
+
+    void startSendRtpPassive(MediaServer mediaServer, SendRtpItem sendRtpItem, Integer timeout);
+
+    void startSendRtpStream(MediaServer mediaServer, SendRtpItem sendRtpItem);
+}

+ 145 - 0
src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java

@@ -0,0 +1,145 @@
+package com.genersoft.iot.vmp.media.service;
+
+import com.genersoft.iot.vmp.common.CommonCallback;
+import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.media.bean.MediaInfo;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.service.bean.MediaServerLoad;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 媒体服务节点
+ */
+public interface IMediaServerService {
+
+    List<MediaServer> getAllOnlineList();
+
+    List<MediaServer> getAll();
+
+    List<MediaServer> getAllFromDatabase();
+
+    List<MediaServer> getAllOnline();
+
+    MediaServer getOne(String generalMediaServerId);
+
+    void syncCatchFromDatabase();
+
+    MediaServer getMediaServerForMinimumLoad(Boolean hasAssist);
+
+    void updateVmServer(List<MediaServer>  mediaServerItemList);
+
+    SSRCInfo openRTPServer(MediaServer mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck,
+                           boolean isPlayback, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode);
+
+    void closeRTPServer(MediaServer mediaServerItem, String streamId);
+
+    void closeRTPServer(MediaServer mediaServerItem, String streamId, CommonCallback<Boolean> callback);
+    Boolean updateRtpServerSSRC(MediaServer mediaServerItem, String streamId, String ssrc);
+
+    void closeRTPServer(String mediaServerId, String streamId);
+
+    void clearRTPServer(MediaServer mediaServerItem);
+
+    void update(MediaServer mediaSerItem);
+
+    void addCount(String mediaServerId);
+
+    void removeCount(String mediaServerId);
+
+    void releaseSsrc(String mediaServerItemId, String ssrc);
+
+    void clearMediaServerForOnline();
+
+    void add(MediaServer mediaSerItem);
+
+    void resetOnlineServerItem(MediaServer serverItem);
+
+    MediaServer checkMediaServer(String ip, int port, String secret, String type);
+
+    boolean checkMediaRecordServer(String ip, int port);
+
+    void delete(String id);
+
+    MediaServer getDefaultMediaServer();
+
+    MediaServerLoad getLoad(MediaServer mediaServerItem);
+
+    List<MediaServer> getAllWithAssistPort();
+
+    MediaServer getOneFromDatabase(String id);
+
+    boolean stopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc);
+
+    boolean deleteRecordDirectory(MediaServer mediaServerItem, String app, String stream, String date, String fileName);
+
+    List<StreamInfo> getMediaList(MediaServer mediaInfo, String app, String stream, String callId);
+
+    Boolean connectRtpServer(MediaServer mediaServerItem, String address, int port, String stream);
+
+    void getSnap(MediaServer mediaServerItemInuse, String streamUrl, int timeoutSec, int expireSec, String path, String fileName);
+
+    MediaInfo getMediaInfo(MediaServer mediaServerItem, String app, String stream);
+
+    Boolean pauseRtpCheck(MediaServer mediaServerItem, String streamKey);
+
+    boolean resumeRtpCheck(MediaServer mediaServerItem, String streamKey);
+
+    String getFfmpegCmd(MediaServer mediaServer, String cmdKey);
+
+    void closeStreams(MediaServer mediaServerItem, String app, String stream);
+
+    WVPResult<String> addFFmpegSource(MediaServer mediaServerItem, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey);
+
+    WVPResult<String> addStreamProxy(MediaServer mediaServerItem, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType);
+
+    Boolean delFFmpegSource(MediaServer mediaServerItem, String streamKey);
+
+    Boolean delStreamProxy(MediaServer mediaServerItem, String streamKey);
+
+    Map<String, String> getFFmpegCMDs(MediaServer mediaServer);
+
+    /**
+     * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在
+     * @param app
+     * @param stream
+     * @return
+     */
+    StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId,String addr, boolean authority);
+
+
+    /**
+     * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在, 返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况
+     * @param app
+     * @param stream
+     * @return
+     */
+    StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority);
+
+    /**
+     * 根据应用名和流ID获取播放地址, 只是地址拼接
+     * @param app
+     * @param stream
+     * @return
+     */
+    StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServerItem, String app, String stream, MediaInfo mediaInfo, String callId);
+
+    /**
+     * 根据应用名和流ID获取播放地址, 只是地址拼接,返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况
+     * @param app
+     * @param stream
+     * @return
+     */
+    StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServer, String app, String stream, MediaInfo mediaInfo, String addr, String callId, boolean isPlay);
+
+    Boolean isStreamReady(MediaServer mediaServer, String rtp, String streamId);
+
+    void startSendRtpPassive(MediaServer mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem, Integer timeout);
+
+    void startSendRtpStream(MediaServer mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem);
+}

+ 837 - 0
src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java

@@ -0,0 +1,837 @@
+package com.genersoft.iot.vmp.media.service.impl;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import com.genersoft.iot.vmp.common.CommonCallback;
+import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.conf.MediaConfig;
+import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.conf.exception.ControllerException;
+import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
+import com.genersoft.iot.vmp.media.bean.MediaInfo;
+import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
+import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
+import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent;
+import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerDeleteEvent;
+import com.genersoft.iot.vmp.media.service.IMediaNodeServerService;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
+import com.genersoft.iot.vmp.service.IInviteStreamService;
+import com.genersoft.iot.vmp.service.bean.MediaServerLoad;
+import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
+import com.genersoft.iot.vmp.utils.DateUtil;
+import com.genersoft.iot.vmp.utils.JsonUtil;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.event.EventListener;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.util.ObjectUtils;
+
+import java.time.LocalDateTime;
+import java.util.*;
+
+/**
+ * 媒体服务器节点管理
+ */
+@Service
+@DS("master")
+public class MediaServerServiceImpl implements IMediaServerService {
+
+    private final static Logger logger = LoggerFactory.getLogger(MediaServerServiceImpl.class);
+
+    @Autowired
+    private SSRCFactory ssrcFactory;
+
+    @Autowired
+    private UserSetting userSetting;
+
+    @Autowired
+    private MediaServerMapper mediaServerMapper;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private IInviteStreamService inviteStreamService;
+
+    @Autowired
+    private RedisTemplate<Object, Object> redisTemplate;
+
+    @Autowired
+    private Map<String, IMediaNodeServerService> nodeServerServiceMap;
+
+    @Autowired
+    private ApplicationEventPublisher applicationEventPublisher;
+
+    @Autowired
+    private MediaConfig mediaConfig;
+
+
+
+    /**
+     * 流到来的处理
+     */
+    @Async("taskExecutor")
+    @org.springframework.context.event.EventListener
+    public void onApplicationEvent(MediaArrivalEvent event) {
+        if ("rtsp".equals(event.getSchema())) {
+            logger.info("流变化:注册 app->{}, stream->{}", event.getApp(), event.getStream());
+            addCount(event.getMediaServer().getId());
+        }
+    }
+
+    /**
+     * 流离开的处理
+     */
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaDepartureEvent event) {
+        if ("rtsp".equals(event.getSchema())) {
+            logger.info("流变化:注销, app->{}, stream->{}", event.getApp(), event.getStream());
+            removeCount(event.getMediaServer().getId());
+        }
+    }
+
+
+    /**
+     * 初始化
+     */
+    @Override
+    public void updateVmServer(List<MediaServer> mediaServerList) {
+        logger.info("[媒体服务节点] 缓存初始化 ");
+        for (MediaServer mediaServer : mediaServerList) {
+            if (ObjectUtils.isEmpty(mediaServer.getId())) {
+                continue;
+            }
+            // 更新
+            if (!ssrcFactory.hasMediaServerSSRC(mediaServer.getId())) {
+                ssrcFactory.initMediaServerSSRC(mediaServer.getId(), null);
+            }
+            // 查询redis是否存在此mediaServer
+            String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServer.getId();
+            Boolean hasKey = redisTemplate.hasKey(key);
+            if (hasKey != null && ! hasKey) {
+                redisTemplate.opsForValue().set(key, mediaServer);
+            }
+        }
+    }
+
+
+    @Override
+    public SSRCInfo openRTPServer(MediaServer mediaServer, String streamId, String presetSsrc, boolean ssrcCheck,
+                                  boolean isPlayback, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode) {
+        if (mediaServer == null || mediaServer.getId() == null) {
+            logger.info("[openRTPServer] 失败, mediaServer == null || mediaServer.getId() == null");
+            return null;
+        }
+        // 获取mediaServer可用的ssrc
+        String ssrc;
+        if (presetSsrc != null) {
+            ssrc = presetSsrc;
+        }else {
+            if (isPlayback) {
+                ssrc = ssrcFactory.getPlayBackSsrc(mediaServer.getId());
+            }else {
+                ssrc = ssrcFactory.getPlaySsrc(mediaServer.getId());
+            }
+        }
+
+        if (streamId == null) {
+            streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
+        }
+        if (ssrcCheck && tcpMode > 0) {
+            // 目前zlm不支持 tcp模式更新ssrc,暂时关闭ssrc校验
+            logger.warn("[openRTPServer] 平台对接时下级可能自定义ssrc,但是tcp模式zlm收流目前无法更新ssrc,可能收流超时,此时请使用udp收流或者关闭ssrc校验");
+        }
+        int rtpServerPort;
+        if (mediaServer.isRtpEnable()) {
+            IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+            if (mediaNodeServerService == null) {
+                logger.info("[openRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+                return null;
+            }
+            rtpServerPort = mediaNodeServerService.createRTPServer(mediaServer, streamId, ssrcCheck ? Long.parseLong(ssrc) : 0, port, onlyAuto, disableAudio, reUsePort, tcpMode);
+        } else {
+            rtpServerPort = mediaServer.getRtpProxyPort();
+        }
+        return new SSRCInfo(rtpServerPort, ssrc, streamId);
+    }
+
+    @Override
+    public void closeRTPServer(MediaServer mediaServer, String streamId) {
+        if (mediaServer == null) {
+            return;
+        }
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return;
+        }
+        mediaNodeServerService.closeRtpServer(mediaServer, streamId);
+    }
+
+    @Override
+    public void closeRTPServer(MediaServer mediaServer, String streamId, CommonCallback<Boolean> callback) {
+        if (mediaServer == null) {
+            callback.run(false);
+            return;
+        }
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return;
+        }
+        mediaNodeServerService.closeRtpServer(mediaServer, streamId, callback);
+    }
+
+    @Override
+    public void closeRTPServer(String mediaServerId, String streamId) {
+        MediaServer mediaServer = this.getOne(mediaServerId);
+        if (mediaServer == null) {
+            return;
+        }
+        if (mediaServer.isRtpEnable()) {
+            closeRTPServer(mediaServer, streamId);
+        }
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return;
+        }
+        mediaNodeServerService.closeStreams(mediaServer, "rtp", streamId);
+    }
+
+    @Override
+    public Boolean updateRtpServerSSRC(MediaServer mediaServer, String streamId, String ssrc) {
+        if (mediaServer == null) {
+            return false;
+        }
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[updateRtpServerSSRC] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return false;
+        }
+        return mediaNodeServerService.updateRtpServerSSRC(mediaServer, streamId, ssrc);
+    }
+
+    @Override
+    public void releaseSsrc(String mediaServerId, String ssrc) {
+        MediaServer mediaServer = getOne(mediaServerId);
+        if (mediaServer == null || ssrc == null) {
+            return;
+        }
+        ssrcFactory.releaseSsrc(mediaServerId, ssrc);
+    }
+
+    /**
+     * 媒体服务节点 重启后重置他的推流信息, TODO 给正在使用的设备发送停止命令
+     */
+    @Override
+    public void clearRTPServer(MediaServer mediaServer) {
+        ssrcFactory.reset(mediaServer.getId());
+    }
+
+
+    @Override
+    public void update(MediaServer mediaSerItem) {
+        mediaServerMapper.update(mediaSerItem);
+        MediaServer mediaServerInRedis = getOne(mediaSerItem.getId());
+        MediaServer mediaServerInDataBase = mediaServerMapper.queryOne(mediaSerItem.getId());
+        if (mediaServerInDataBase == null) {
+            return;
+        }
+        mediaServerInDataBase.setStatus(mediaSerItem.isStatus());
+        if (mediaServerInRedis == null || !ssrcFactory.hasMediaServerSSRC(mediaServerInDataBase.getId())) {
+            ssrcFactory.initMediaServerSSRC(mediaServerInDataBase.getId(),null);
+        }
+        String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServerInDataBase.getId();
+        redisTemplate.opsForValue().set(key, mediaServerInDataBase);
+        if (mediaServerInDataBase.isStatus()) {
+            resetOnlineServerItem(mediaServerInDataBase);
+        }else {
+            // 发送事件
+            MediaServerChangeEvent event = new MediaServerChangeEvent(this);
+            event.setMediaServerItemList(mediaServerInDataBase);
+            applicationEventPublisher.publishEvent(event);
+        }
+    }
+
+
+    @Override
+    public List<MediaServer> getAllOnlineList() {
+        List<MediaServer> result = new ArrayList<>();
+        List<Object> mediaServerKeys = RedisUtil.scan(redisTemplate, String.format("%S*", VideoManagerConstants.MEDIA_SERVER_PREFIX+ userSetting.getServerId() + ":" ));
+        String onlineKey = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
+        for (Object mediaServerKey : mediaServerKeys) {
+            String key = (String) mediaServerKey;
+            MediaServer mediaServer = JsonUtil.redisJsonToObject(redisTemplate, key, MediaServer.class);
+            if (Objects.isNull(mediaServer)) {
+                continue;
+            }
+            // 检查状态
+            Double aDouble = redisTemplate.opsForZSet().score(onlineKey, mediaServer.getId());
+            if (aDouble != null) {
+                mediaServer.setStatus(true);
+            }
+            result.add(mediaServer);
+        }
+        result.sort((serverItem1, serverItem2)->{
+            int sortResult = 0;
+            LocalDateTime localDateTime1 = LocalDateTime.parse(serverItem1.getCreateTime(), DateUtil.formatter);
+            LocalDateTime localDateTime2 = LocalDateTime.parse(serverItem2.getCreateTime(), DateUtil.formatter);
+
+            sortResult = localDateTime1.compareTo(localDateTime2);
+            return  sortResult;
+        });
+        return result;
+    }
+
+    @Override
+    public List<MediaServer> getAll() {
+        List<MediaServer> mediaServerList = mediaServerMapper.queryAll();
+        if (mediaServerList.isEmpty()) {
+            return new ArrayList<>();
+        }
+        for (MediaServer mediaServer : mediaServerList) {
+            MediaServer mediaServerInRedis = getOne(mediaServer.getId());
+            if (mediaServerInRedis != null) {
+                mediaServer.setStatus(mediaServerInRedis.isStatus());
+            }
+        }
+        return mediaServerList;
+    }
+
+
+    @Override
+    public List<MediaServer> getAllFromDatabase() {
+        return mediaServerMapper.queryAll();
+    }
+
+    @Override
+    public List<MediaServer> getAllOnline() {
+        String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
+        Set<Object> mediaServerIdSet = redisTemplate.opsForZSet().reverseRange(key, 0, -1);
+
+        List<MediaServer> result = new ArrayList<>();
+        if (mediaServerIdSet != null && mediaServerIdSet.size() > 0) {
+            for (Object mediaServerId : mediaServerIdSet) {
+                String mediaServerIdStr = (String) mediaServerId;
+                String serverKey = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServerIdStr;
+                result.add((MediaServer) redisTemplate.opsForValue().get(serverKey));
+            }
+        }
+        Collections.reverse(result);
+        return result;
+    }
+
+    /**
+     * 获取单个媒体服务节点服务器
+     * @param mediaServerId 服务id
+     * @return mediaServer
+     */
+    @Override
+    public MediaServer getOne(String mediaServerId) {
+        if (mediaServerId == null) {
+            return null;
+        }
+        String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServerId;
+        return JsonUtil.redisJsonToObject(redisTemplate, key, MediaServer.class);
+    }
+
+
+    @Override
+    public MediaServer getDefaultMediaServer() {
+        return mediaServerMapper.queryDefault();
+    }
+
+    @Override
+    public void clearMediaServerForOnline() {
+        String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
+        redisTemplate.delete(key);
+    }
+
+    @Override
+    public void add(MediaServer mediaServer) {
+        mediaServer.setCreateTime(DateUtil.getNow());
+        mediaServer.setUpdateTime(DateUtil.getNow());
+        if (mediaServer.getHookAliveInterval() == null || mediaServer.getHookAliveInterval() == 0F) {
+            mediaServer.setHookAliveInterval(10F);
+        }
+        if (mediaServer.getType() == null) {
+            logger.info("[添加媒体节点] 失败, mediaServer的类型:为空");
+            return;
+        }
+        if (mediaServerMapper.queryOne(mediaServer.getId()) != null) {
+            logger.info("[添加媒体节点] 失败, 媒体服务ID已存在,请修改媒体服务器配置, {}", mediaServer.getId());
+            throw new ControllerException(ErrorCode.ERROR100.getCode(),"保存失败,媒体服务ID [ " + mediaServer.getId() + " ] 已存在,请修改媒体服务器配置");
+        }
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[添加媒体节点] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return;
+        }
+        mediaServerMapper.add(mediaServer);
+        if (mediaServer.isStatus()) {
+            mediaNodeServerService.online(mediaServer);
+        }else {
+            // 发送事件
+            MediaServerChangeEvent event = new MediaServerChangeEvent(this);
+            event.setMediaServerItemList(mediaServer);
+            applicationEventPublisher.publishEvent(event);
+        }
+    }
+
+    @Override
+    public void resetOnlineServerItem(MediaServer serverItem) {
+        // 更新缓存
+        String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
+        // 使用zset的分数作为当前并发量, 默认值设置为0
+        if (redisTemplate.opsForZSet().score(key, serverItem.getId()) == null) {  // 不存在则设置默认值 已存在则重置
+            redisTemplate.opsForZSet().add(key, serverItem.getId(), 0L);
+            // 查询服务流数量
+            int count = getMediaList(serverItem);
+            redisTemplate.opsForZSet().add(key, serverItem.getId(), count);
+        }else {
+            clearRTPServer(serverItem);
+        }
+    }
+
+    private int getMediaList(MediaServer serverItem) {
+
+        return 0;
+    }
+
+
+    @Override
+    public void addCount(String mediaServerId) {
+        if (mediaServerId == null) {
+            return;
+        }
+        String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
+        redisTemplate.opsForZSet().incrementScore(key, mediaServerId, 1);
+
+    }
+
+    @Override
+    public void removeCount(String mediaServerId) {
+        String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
+        redisTemplate.opsForZSet().incrementScore(key, mediaServerId, - 1);
+    }
+
+    /**
+     * 获取负载最低的节点
+     * @return mediaServer
+     */
+    @Override
+    public MediaServer getMediaServerForMinimumLoad(Boolean hasAssist) {
+        String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
+        Long size = redisTemplate.opsForZSet().zCard(key);
+        if (size  == null || size == 0) {
+            logger.info("获取负载最低的节点时无在线节点");
+            return null;
+        }
+
+        // 获取分数最低的,及并发最低的
+        Set<Object> objects = redisTemplate.opsForZSet().range(key, 0, -1);
+        ArrayList<Object> mediaServerObjectS = new ArrayList<>(objects);
+        MediaServer mediaServer = null;
+        if (hasAssist == null) {
+            String mediaServerId = (String)mediaServerObjectS.get(0);
+            mediaServer = getOne(mediaServerId);
+        }else if (hasAssist) {
+            for (Object mediaServerObject : mediaServerObjectS) {
+                String mediaServerId = (String)mediaServerObject;
+                MediaServer serverItem = getOne(mediaServerId);
+                if (serverItem.getRecordAssistPort() > 0) {
+                    mediaServer = serverItem;
+                    break;
+                }
+            }
+        }else if (!hasAssist) {
+            for (Object mediaServerObject : mediaServerObjectS) {
+                String mediaServerId = (String)mediaServerObject;
+                MediaServer serverItem = getOne(mediaServerId);
+                if (serverItem.getRecordAssistPort() == 0) {
+                    mediaServer = serverItem;
+                    break;
+                }
+            }
+        }
+
+        return mediaServer;
+    }
+
+    @Override
+    public MediaServer checkMediaServer(String ip, int port, String secret, String type) {
+        if (mediaServerMapper.queryOneByHostAndPort(ip, port) != null) {
+            throw new ControllerException(ErrorCode.ERROR100.getCode(), "此连接已存在");
+        }
+
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(type);
+        if (mediaNodeServerService == null) {
+            logger.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", type);
+            return null;
+        }
+        MediaServer mediaServer = mediaNodeServerService.checkMediaServer(ip, port, secret);
+        if (mediaServer != null) {
+            if (mediaServerMapper.queryOne(mediaServer.getId()) != null) {
+                throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体服务ID [" + mediaServer.getId() + " ] 已存在,请修改媒体服务器配置");
+            }
+        }
+        return mediaServer;
+    }
+
+    @Override
+    public boolean checkMediaRecordServer(String ip, int port) {
+        boolean result = false;
+        OkHttpClient client = new OkHttpClient();
+        String url = String.format("http://%s:%s/index/api/record",  ip, port);
+        Request request = new Request.Builder()
+                .get()
+                .url(url)
+                .build();
+        try {
+            Response response = client.newCall(request).execute();
+            if (response != null) {
+                result = true;
+            }
+        } catch (Exception e) {}
+
+        return result;
+    }
+
+    @Override
+    public void delete(String id) {
+        mediaServerMapper.delOne(id);
+        redisTemplate.opsForZSet().remove(VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(), id);
+        String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + id;
+        redisTemplate.delete(key);
+        // 发送节点移除通知
+        MediaServerDeleteEvent event = new MediaServerDeleteEvent(this);
+        event.setMediaServerId(id);
+        applicationEventPublisher.publishEvent(event);
+    }
+
+    @Override
+    public MediaServer getOneFromDatabase(String mediaServerId) {
+        return mediaServerMapper.queryOne(mediaServerId);
+    }
+
+    @Override
+    public void syncCatchFromDatabase() {
+        List<MediaServer> allInCatch = getAllOnlineList();
+        List<MediaServer> allInDatabase = mediaServerMapper.queryAll();
+        Map<String, MediaServer> mediaServerMap = new HashMap<>();
+
+        for (MediaServer mediaServer : allInDatabase) {
+            mediaServerMap.put(mediaServer.getId(), mediaServer);
+        }
+        for (MediaServer mediaServer : allInCatch) {
+            // 清除数据中不存在但redis缓存数据
+            if (!mediaServerMap.containsKey(mediaServer.getId())) {
+                delete(mediaServer.getId());
+            }
+        }
+    }
+
+    @Override
+    public MediaServerLoad getLoad(MediaServer mediaServer) {
+        MediaServerLoad result = new MediaServerLoad();
+        result.setId(mediaServer.getId());
+        result.setPush(redisCatchStorage.getPushStreamCount(mediaServer.getId()));
+        result.setProxy(redisCatchStorage.getProxyStreamCount(mediaServer.getId()));
+
+        result.setGbReceive(inviteStreamService.getStreamInfoCount(mediaServer.getId()));
+        result.setGbSend(redisCatchStorage.getGbSendCount(mediaServer.getId()));
+        return result;
+    }
+
+    @Override
+    public List<MediaServer> getAllWithAssistPort() {
+        return mediaServerMapper.queryAllWithAssistPort();
+    }
+
+
+    @Override
+    public boolean stopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaInfo.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaInfo.getType());
+            return false;
+        }
+        return mediaNodeServerService.stopSendRtp(mediaInfo, app, stream, ssrc);
+    }
+
+    @Override
+    public boolean deleteRecordDirectory(MediaServer mediaServer, String app, String stream, String date, String fileName) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return false;
+        }
+        return mediaNodeServerService.deleteRecordDirectory(mediaServer, app, stream, date, fileName);
+    }
+
+    @Override
+    public List<StreamInfo> getMediaList(MediaServer mediaServer, String app, String stream, String callId) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[getMediaList] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return new ArrayList<>();
+        }
+        return mediaNodeServerService.getMediaList(mediaServer, app, stream, callId);
+    }
+
+    @Override
+    public Boolean connectRtpServer(MediaServer mediaServer, String address, int port, String stream) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[connectRtpServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return false;
+        }
+        return mediaNodeServerService.connectRtpServer(mediaServer, address, port, stream);
+    }
+
+    @Override
+    public void getSnap(MediaServer mediaServer, String streamUrl, int timeoutSec, int expireSec, String path, String fileName) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[getSnap] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return;
+        }
+        mediaNodeServerService.getSnap(mediaServer, streamUrl, timeoutSec, expireSec, path, fileName);
+    }
+
+    @Override
+    public MediaInfo getMediaInfo(MediaServer mediaServer, String app, String stream) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[getMediaInfo] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return null;
+        }
+        return mediaNodeServerService.getMediaInfo(mediaServer, app, stream);
+    }
+
+    @Override
+    public Boolean pauseRtpCheck(MediaServer mediaServer, String streamKey) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[pauseRtpCheck] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return false;
+        }
+        return mediaNodeServerService.pauseRtpCheck(mediaServer, streamKey);
+    }
+
+    @Override
+    public boolean resumeRtpCheck(MediaServer mediaServer, String streamKey) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[pauseRtpCheck] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return false;
+        }
+        return mediaNodeServerService.resumeRtpCheck(mediaServer, streamKey);
+    }
+
+    @Override
+    public String getFfmpegCmd(MediaServer mediaServer, String cmdKey) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[getFfmpegCmd] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return null;
+        }
+        return mediaNodeServerService.getFfmpegCmd(mediaServer, cmdKey);
+    }
+
+    @Override
+    public void closeStreams(MediaServer mediaServer, String app, String stream) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[closeStreams] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return;
+        }
+        mediaNodeServerService.closeStreams(mediaServer, app, stream);
+    }
+
+    @Override
+    public WVPResult<String> addFFmpegSource(MediaServer mediaServer, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[addFFmpegSource] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return WVPResult.fail(ErrorCode.ERROR400);
+        }
+        return mediaNodeServerService.addFFmpegSource(mediaServer, srcUrl, dstUrl, timeoutMs, enableAudio, enableMp4, ffmpegCmdKey);
+    }
+
+    @Override
+    public WVPResult<String> addStreamProxy(MediaServer mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[addStreamProxy] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return WVPResult.fail(ErrorCode.ERROR400);
+        }
+        return mediaNodeServerService.addStreamProxy(mediaServer, app, stream, url, enableAudio, enableMp4, rtpType);
+    }
+
+    @Override
+    public Boolean delFFmpegSource(MediaServer mediaServer, String streamKey) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[delFFmpegSource] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return false;
+        }
+        return mediaNodeServerService.delFFmpegSource(mediaServer, streamKey);
+    }
+
+    @Override
+    public Boolean delStreamProxy(MediaServer mediaServerItem, String streamKey) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServerItem.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[delStreamProxy] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServerItem.getType());
+            return false;
+        }
+        return mediaNodeServerService.delStreamProxy(mediaServerItem, streamKey);
+    }
+
+    @Override
+    public Map<String, String> getFFmpegCMDs(MediaServer mediaServer) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[getFFmpegCMDs] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return new HashMap<>();
+        }
+        return mediaNodeServerService.getFFmpegCMDs(mediaServer);
+    }
+
+    @Override
+    public StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServerItem, String app, String stream, MediaInfo mediaInfo, String callId) {
+        return getStreamInfoByAppAndStream(mediaServerItem, app, stream, mediaInfo, null, callId, true);
+    }
+
+    @Override
+    public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr, boolean authority) {
+        StreamInfo streamInfo = null;
+        if (mediaServerId == null) {
+            mediaServerId = mediaConfig.getId();
+        }
+        MediaServer mediaInfo = getOne(mediaServerId);
+        if (mediaInfo == null) {
+            return null;
+        }
+        String calld = null;
+        StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream);
+        if (streamAuthorityInfo != null) {
+            calld = streamAuthorityInfo.getCallId();
+        }
+        List<StreamInfo> streamInfoList = getMediaList(mediaInfo, app, stream, calld);
+        if (streamInfoList.isEmpty()) {
+            return null;
+        }else {
+            return streamInfoList.get(0);
+        }
+    }
+
+
+
+    @Override
+    public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority) {
+        return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null, authority);
+    }
+
+    @Override
+    public StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServer, String app, String stream, MediaInfo mediaInfo, String addr, String callId, boolean isPlay) {
+        StreamInfo streamInfoResult = new StreamInfo();
+        streamInfoResult.setStream(stream);
+        streamInfoResult.setApp(app);
+        if (addr == null) {
+            addr = mediaServer.getStreamIp();
+        }
+
+        streamInfoResult.setIp(addr);
+        streamInfoResult.setMediaServerId(mediaServer.getId());
+        String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId;
+        streamInfoResult.setRtmp(addr, mediaServer.getRtmpPort(),mediaServer.getRtmpSSlPort(), app,  stream, callIdParam);
+        streamInfoResult.setRtsp(addr, mediaServer.getRtspPort(),mediaServer.getRtspSSLPort(), app,  stream, callIdParam);
+
+
+        if ("abl".equals(mediaServer.getType())) {
+            String flvFile = String.format("%s/%s.flv%s", app, stream, callIdParam);
+            streamInfoResult.setFlv(addr, mediaServer.getFlvPort(),mediaServer.getFlvSSLPort(), flvFile);
+            streamInfoResult.setWsFlv(addr, mediaServer.getWsFlvPort(),mediaServer.getWsFlvSSLPort(), flvFile);
+        }else {
+            String flvFile = String.format("%s/%s.live.flv%s", app, stream, callIdParam);
+            streamInfoResult.setFlv(addr, mediaServer.getFlvPort(),mediaServer.getFlvSSLPort(), flvFile);
+            streamInfoResult.setWsFlv(addr, mediaServer.getWsFlvPort(),mediaServer.getWsFlvSSLPort(), flvFile);
+        }
+
+        streamInfoResult.setFmp4(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app,  stream, callIdParam);
+        streamInfoResult.setHls(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app,  stream, callIdParam);
+        streamInfoResult.setTs(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app,  stream, callIdParam);
+        streamInfoResult.setRtc(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app,  stream, callIdParam, isPlay);
+
+        streamInfoResult.setMediaInfo(mediaInfo);
+        return streamInfoResult;
+    }
+
+    @Override
+    public Boolean isStreamReady(MediaServer mediaServer, String rtp, String streamId) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[isStreamReady] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            return false;
+        }
+        MediaInfo mediaInfo = mediaNodeServerService.getMediaInfo(mediaServer, rtp, streamId);
+        return mediaInfo != null;
+    }
+
+    @Override
+    public void startSendRtpPassive(MediaServer mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem, Integer timeout) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[startSendRtpPassive] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
+        }
+        mediaNodeServerService.startSendRtpPassive(mediaServer, sendRtpItem, timeout);
+        sendPlatformStartPlayMsg(platform, sendRtpItem);
+    }
+
+    @Override
+    public void startSendRtpStream(MediaServer mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem) {
+        IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+        if (mediaNodeServerService == null) {
+            logger.info("[startSendRtpStream] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+            throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
+        }
+        logger.info("[开始推流] rtp/{}, 目标={}:{},SSRC={}, RTCP={}", sendRtpItem.getStream(),
+                sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp());
+        mediaNodeServerService.startSendRtpStream(mediaServer, sendRtpItem);
+        sendPlatformStartPlayMsg(platform, sendRtpItem);
+
+    }
+
+    private void sendPlatformStartPlayMsg(ParentPlatform platform, SendRtpItem sendRtpItem) {
+        if (sendRtpItem.getPlayType() == InviteStreamType.PUSH && platform  != null) {
+            MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStream(),
+                    sendRtpItem.getChannelId(), platform.getServerGBId(), platform.getName(), userSetting.getServerId(),
+                    sendRtpItem.getMediaServerId());
+            messageForPushChannel.setPlatFormIndex(platform.getId());
+            redisCatchStorage.sendPlatformStartPlayMsg(messageForPushChannel);
+        }
+    }
+}

+ 6 - 6
src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java

@@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.media.zlm;
 
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.utils.SSLSocketClientUtil;
 import okhttp3.*;
 import okhttp3.logging.HttpLoggingInterceptor;
@@ -70,7 +70,7 @@ public class AssistRESTfulUtils {
     }
 
 
-    public JSONObject sendGet(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
+    public JSONObject sendGet(MediaServer mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
         OkHttpClient client = getClient();
 
         if (mediaServerItem == null) {
@@ -155,7 +155,7 @@ public class AssistRESTfulUtils {
         return responseJSON;
     }
 
-    public JSONObject sendPost(MediaServerItem mediaServerItem, String url,
+    public JSONObject sendPost(MediaServer mediaServerItem, String url,
                                JSONObject param, ZLMRESTfulUtils.RequestCallback callback,
                                Integer readTimeOut) {
         OkHttpClient client = getClient(readTimeOut);
@@ -244,12 +244,12 @@ public class AssistRESTfulUtils {
         return responseJSON;
     }
 
-    public JSONObject getInfo(MediaServerItem mediaServerItem, RequestCallback callback){
+    public JSONObject getInfo(MediaServer mediaServerItem, RequestCallback callback){
         Map<String, Object> param = new HashMap<>();
         return sendGet(mediaServerItem, "api/record/info",param, callback);
     }
 
-    public JSONObject addTask(MediaServerItem mediaServerItem, String app, String stream, String startTime,
+    public JSONObject addTask(MediaServer mediaServerItem, String app, String stream, String startTime,
                               String endTime, String callId, List<String> filePathList, String remoteHost) {
 
         JSONObject videoTaskInfoJSON = new JSONObject();
@@ -266,7 +266,7 @@ public class AssistRESTfulUtils {
         return sendPost(mediaServerItem, urlStr, videoTaskInfoJSON, null, 30);
     }
 
-    public JSONObject queryTaskList(MediaServerItem mediaServerItem, String app, String stream, String callId,
+    public JSONObject queryTaskList(MediaServer mediaServerItem, String app, String stream, String callId,
                                     String taskId, Boolean isEnd, String scheme) {
         Map<String, Object> param = new HashMap<>();
         if (!ObjectUtils.isEmpty(app)) {

+ 3 - 2
src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java

@@ -3,7 +3,7 @@ package com.genersoft.iot.vmp.media.zlm;
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 import org.apache.commons.lang3.math.NumberUtils;
 import org.slf4j.Logger;
@@ -30,7 +30,7 @@ public class SendRtpPortManager {
 
     private final String KEY = "VM_MEDIA_SEND_RTP_PORT_";
 
-    public synchronized int getNextPort(MediaServerItem mediaServer) {
+    public synchronized int getNextPort(MediaServer mediaServer) {
         if (mediaServer == null) {
             logger.warn("[发送端口管理] 参数错误,mediaServer为NULL");
             return -1;
@@ -83,6 +83,7 @@ public class SendRtpPortManager {
     }
 
     private synchronized int getSendPort(int startPort, int endPort, String sendIndexKey, Map<Integer, SendRtpItem> sendRtpItemMap){
+        // TODO 这里改为只取偶数端口
         RedisAtomicInteger redisAtomicInteger = new RedisAtomicInteger(sendIndexKey , redisTemplate.getConnectionFactory());
         if (redisAtomicInteger.get() < startPort) {
             redisAtomicInteger.set(startPort);

+ 100 - 647
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java

@@ -2,55 +2,40 @@ package com.genersoft.iot.vmp.media.zlm;
 
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.common.InviteInfo;
-import com.genersoft.iot.vmp.common.InviteSessionType;
-import com.genersoft.iot.vmp.common.StreamInfo;
-import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.conf.UserSetting;
-import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
-import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
-import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
 import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
-import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
-import com.genersoft.iot.vmp.media.zlm.dto.HookType;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
-import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
+import com.genersoft.iot.vmp.media.bean.ResultForOnPublish;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.event.media.*;
+import com.genersoft.iot.vmp.media.event.mediaServer.MediaSendRtpStoppedEvent;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig;
 import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
+import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerKeepaliveEvent;
+import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerStartEvent;
 import com.genersoft.iot.vmp.service.*;
-import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
-import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
-import com.genersoft.iot.vmp.utils.DateUtil;
-import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
-import com.genersoft.iot.vmp.vmanager.bean.OtherPsSendInfo;
-import com.genersoft.iot.vmp.vmanager.bean.OtherRtpSendInfo;
-import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.util.ObjectUtils;
 import org.springframework.web.bind.annotation.*;
-import org.springframework.web.context.request.async.DeferredResult;
 
 import javax.servlet.http.HttpServletRequest;
-import javax.sip.InvalidArgumentException;
-import javax.sip.SipException;
-import java.text.ParseException;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 
 /**
  * @description:针对 ZLMediaServer的hook事件监听
@@ -106,7 +91,7 @@ public class ZLMHttpHookListener {
     private ZLMMediaListManager zlmMediaListManager;
 
     @Autowired
-    private ZlmHttpHookSubscribe subscribe;
+    private HookSubscribe subscribe;
 
     @Autowired
     private UserSetting userSetting;
@@ -130,25 +115,25 @@ public class ZLMHttpHookListener {
     @Autowired
     private RedisTemplate<Object, Object> redisTemplate;
 
+    @Autowired
+    private ApplicationEventPublisher applicationEventPublisher;
+
     /**
      * 服务器定时上报时间,上报间隔可配置,默认10s上报一次
      */
     @ResponseBody
-
     @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
     public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) {
-
-
-        taskExecutor.execute(() -> {
-            List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
-            if (subscribes != null && !subscribes.isEmpty()) {
-                for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
-                    subscribe.response(null, param);
-                }
+        try {
+            HookZlmServerKeepaliveEvent event = new HookZlmServerKeepaliveEvent(this);
+            MediaServer mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
+            if (mediaServerItem != null) {
+                event.setMediaServerItem(mediaServerItem);
+                applicationEventPublisher.publishEvent(event);
             }
-        });
-        mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData());
-
+        }catch (Exception e) {
+            logger.info("[ZLM-HOOK-心跳] 发送通知失败 ", e);
+        }
         return HookResult.SUCCESS();
     }
 
@@ -156,32 +141,17 @@ public class ZLMHttpHookListener {
      * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
      */
     @ResponseBody
-
     @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
     public HookResult onPlay(@RequestBody OnPlayHookParam param) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("[ZLM HOOK] 播放鉴权:{}->{}", param.getMediaServerId(), param);
-        }
-        String mediaServerId = param.getMediaServerId();
-
-        taskExecutor.execute(() -> {
-            JSONObject json = (JSONObject) JSON.toJSON(param);
-            ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);
-            if (subscribe != null) {
-                MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
-                if (mediaInfo != null) {
-                    subscribe.response(mediaInfo, param);
-                }
-            }
-        });
-        if (!"rtp".equals(param.getApp())) {
-            Map<String, String> paramMap = urlParamToMap(param.getParams());
-            StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
-            if (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(paramMap.get("callId"))) {
-                return new HookResult(401, "Unauthorized");
-            }
-        }
 
+        Map<String, String> paramMap = urlParamToMap(param.getParams());
+        // 对于播放流进行鉴权
+        boolean authenticateResult = mediaService.authenticatePlay(param.getApp(), param.getStream(), paramMap.get("callId"));
+        if (!authenticateResult) {
+            logger.info("[ZLM HOOK] 播放鉴权 失败:{}->{}", param.getMediaServerId(), param);
+            return new HookResult(401, "Unauthorized");
+        }
+        logger.info("[ZLM HOOK] 播放鉴权成功:{}->{}", param.getMediaServerId(), param);
         return HookResult.SUCCESS();
     }
 
@@ -195,136 +165,24 @@ public class ZLMHttpHookListener {
         JSONObject json = (JSONObject) JSON.toJSON(param);
 
         logger.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param);
+        // TODO 加快处理速度
 
         String mediaServerId = json.getString("mediaServerId");
-        MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
-        if (mediaInfo == null) {
-            return new HookResultForOnPublish(200, "success");
+        MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
+        if (mediaServer == null) {
+            return new HookResultForOnPublish(0, "success");
         }
-        // 推流鉴权的处理
-        if (!"rtp".equals(param.getApp())) {
-            StreamProxyItem stream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
-            if (stream != null) {
-                HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
-                result.setEnable_audio(stream.isEnableAudio());
-                result.setEnable_mp4(stream.isEnableMp4());
-                return result;
-            }
-            if (userSetting.getPushAuthority()) {
-                // 推流鉴权
-                if (param.getParams() == null) {
-                    logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)");
-                    return new HookResultForOnPublish(401, "Unauthorized");
-                }
-                Map<String, String> paramMap = urlParamToMap(param.getParams());
-                String sign = paramMap.get("sign");
-                if (sign == null) {
-                    logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)");
-                    return new HookResultForOnPublish(401, "Unauthorized");
-                }
-                // 推流自定义播放鉴权码
-                String callId = paramMap.get("callId");
-                // 鉴权配置
-                boolean hasAuthority = userService.checkPushAuthority(callId, sign);
-                if (!hasAuthority) {
-                    logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign);
-                    return new HookResultForOnPublish(401, "Unauthorized");
-                }
-                StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
-                streamAuthorityInfo.setCallId(callId);
-                streamAuthorityInfo.setSign(sign);
-                // 鉴权通过
-                redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
-            }
-        } else {
-            zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
-        }
-
 
-        HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
-        result.setEnable_audio(true);
-        taskExecutor.execute(() -> {
-            ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
-            if (subscribe != null) {
-                subscribe.response(mediaInfo, param);
-            }
-        });
-
-        // 是否录像
-        if ("rtp".equals(param.getApp())) {
-            result.setEnable_mp4(userSetting.getRecordSip());
-        } else {
-            result.setEnable_mp4(userSetting.isRecordPushLive());
+        ResultForOnPublish resultForOnPublish = mediaService.authenticatePublish(mediaServer, param.getApp(), param.getStream(), param.getParams());
+        if (resultForOnPublish != null) {
+            HookResultForOnPublish successResult = HookResultForOnPublish.getInstance(resultForOnPublish);
+            logger.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, successResult);
+            return successResult;
+        }else {
+            HookResultForOnPublish fail = HookResultForOnPublish.Fail();
+            logger.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, fail);
+            return fail;
         }
-        // 国标流
-        if ("rtp".equals(param.getApp())) {
-
-            InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
-
-            // 单端口模式下修改流 ID
-            if (!mediaInfo.isRtpEnable() && inviteInfo == null) {
-                String ssrc = String.format("%010d", Long.parseLong(param.getStream(), 16));
-                inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc);
-                if (inviteInfo != null) {
-                    result.setStream_replace(inviteInfo.getStream());
-                    logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream());
-                }
-            }
-
-            // 设置音频信息及录制信息
-            List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
-            if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
-
-                // 为录制国标模拟一个鉴权信息, 方便后续写入录像文件时使用
-                StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
-                streamAuthorityInfo.setApp(param.getApp());
-                streamAuthorityInfo.setStream(ssrcTransactionForAll.get(0).getStream());
-                streamAuthorityInfo.setCallId(ssrcTransactionForAll.get(0).getSipTransactionInfo().getCallId());
-
-                redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), ssrcTransactionForAll.get(0).getStream(), streamAuthorityInfo);
-
-                String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
-                String channelId = ssrcTransactionForAll.get(0).getChannelId();
-                DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
-                if (deviceChannel != null) {
-                    result.setEnable_audio(deviceChannel.isHasAudio());
-                }
-                // 如果是录像下载就设置视频间隔十秒
-                if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
-                    // 获取录像的总时长,然后设置为这个视频的时长
-                    InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, param.getStream());
-                    if (inviteInfoForDownload != null && inviteInfoForDownload.getStreamInfo() != null) {
-                        String startTime = inviteInfoForDownload.getStreamInfo().getStartTime();
-                        String endTime = inviteInfoForDownload.getStreamInfo().getEndTime();
-                        long difference = DateUtil.getDifference(startTime, endTime) / 1000;
-                        result.setMp4_max_second((int) difference);
-                        result.setEnable_mp4(true);
-                        // 设置为2保证得到的mp4的时长是正常的
-                        result.setModify_stamp(2);
-                    }
-                }
-                // 如果是talk对讲,则默认获取声音
-                if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) {
-                    result.setEnable_audio(true);
-                }
-            }
-        } else if (param.getApp().equals("broadcast")) {
-            result.setEnable_audio(true);
-        } else if (param.getApp().equals("talk")) {
-            result.setEnable_audio(true);
-        }
-        if (param.getApp().equalsIgnoreCase("rtp")) {
-            String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "_" + param.getStream();
-            OtherRtpSendInfo otherRtpSendInfo = (OtherRtpSendInfo) redisTemplate.opsForValue().get(receiveKey);
-
-            String receiveKeyForPS = VideoManagerConstants.WVP_OTHER_RECEIVE_PS_INFO + userSetting.getServerId() + "_" + param.getStream();
-            OtherPsSendInfo otherPsSendInfo = (OtherPsSendInfo) redisTemplate.opsForValue().get(receiveKeyForPS);
-            if (otherRtpSendInfo != null || otherPsSendInfo != null) {
-                result.setEnable_mp4(true);
-            }
-        }
-        logger.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, result);
-        return result;
     }
 
 
@@ -335,226 +193,20 @@ public class ZLMHttpHookListener {
     @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
     public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) {
 
+        MediaServer mediaServer = mediaServerService.getOne(param.getMediaServerId());
+        if (mediaServer == null) {
+            return HookResult.SUCCESS();
+        }
+
         if (param.isRegist()) {
             logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
+            MediaArrivalEvent mediaArrivalEvent = MediaArrivalEvent.getInstance(this, param, mediaServer);
+            applicationEventPublisher.publishEvent(mediaArrivalEvent);
         } else {
             logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
+            MediaDepartureEvent mediaDepartureEvent = MediaDepartureEvent.getInstance(this, param, mediaServer);
+            applicationEventPublisher.publishEvent(mediaDepartureEvent);
         }
-
-        JSONObject json = (JSONObject) JSON.toJSON(param);
-        taskExecutor.execute(() -> {
-            ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
-            MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
-            if (mediaInfo == null) {
-                logger.info("[ZLM HOOK] 流变化未找到ZLM, {}", param.getMediaServerId());
-                return;
-            }
-            if (subscribe != null) {
-                subscribe.response(mediaInfo, param);
-            }
-
-            List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
-            // TODO 重构此处逻辑
-            if (param.isRegist()) {
-                // 处理流注册的鉴权信息, 流注销这里不再删除鉴权信息,下次来了新的鉴权信息会对就的进行覆盖
-                if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
-                        || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
-                        || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
-                    StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
-                    if (streamAuthorityInfo == null) {
-                        streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
-                    } else {
-                        streamAuthorityInfo.setOriginType(param.getOriginType());
-                        streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
-                    }
-                    redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
-                }
-            }
-            if ("rtsp".equals(param.getSchema())) {
-                logger.info("流变化:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
-                if (param.isRegist()) {
-                    mediaServerService.addCount(param.getMediaServerId());
-                } else {
-                    mediaServerService.removeCount(param.getMediaServerId());
-                }
-
-                int updateStatusResult = streamProxyService.updateStatus(param.isRegist(), param.getApp(), param.getStream());
-                if (updateStatusResult > 0) {
-
-                }
-
-                if ("rtp".equals(param.getApp()) && !param.isRegist()) {
-                    InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
-                    if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) {
-                        inviteStreamService.removeInviteInfo(inviteInfo);
-                        storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
-                    }
-                } else if ("broadcast".equals(param.getApp())) {
-                    // 语音对讲推流  stream需要满足格式deviceId_channelId
-                    if (param.getStream().indexOf("_") > 0) {
-                        String[] streamArray = param.getStream().split("_");
-                        if (streamArray.length == 2) {
-                            String deviceId = streamArray[0];
-                            String channelId = streamArray[1];
-                            Device device = deviceService.getDevice(deviceId);
-                            if (device != null) {
-                                if (param.isRegist()) {
-                                    if (audioBroadcastManager.exit(deviceId, channelId)) {
-                                        playService.stopAudioBroadcast(deviceId, channelId);
-                                    }
-                                    // 开启语音对讲通道
-                                    try {
-                                        playService.audioBroadcastCmd(device, channelId, mediaInfo, param.getApp(), param.getStream(), 60, false, (msg) -> {
-                                            logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId);
-                                        });
-                                    } catch (InvalidArgumentException | ParseException | SipException e) {
-                                        logger.error("[命令发送失败] 语音对讲: {}", e.getMessage());
-                                    }
-                                } else {
-                                    // 流注销
-                                    playService.stopAudioBroadcast(deviceId, channelId);
-                                }
-                            } else {
-                                logger.info("[语音对讲] 未找到设备:{}", deviceId);
-                            }
-                        }
-                    }
-                } else if ("talk".equals(param.getApp())) {
-                    // 语音对讲推流  stream需要满足格式deviceId_channelId
-                    if (param.getStream().indexOf("_") > 0) {
-                        String[] streamArray = param.getStream().split("_");
-                        if (streamArray.length == 2) {
-                            String deviceId = streamArray[0];
-                            String channelId = streamArray[1];
-                            Device device = deviceService.getDevice(deviceId);
-                            if (device != null) {
-                                if (param.isRegist()) {
-                                    if (audioBroadcastManager.exit(deviceId, channelId)) {
-                                        playService.stopAudioBroadcast(deviceId, channelId);
-                                    }
-                                    // 开启语音对讲通道
-                                    playService.talkCmd(device, channelId, mediaInfo, param.getStream(), (msg) -> {
-                                        logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId);
-                                    });
-                                } else {
-                                    // 流注销
-                                    playService.stopTalk(device, channelId, param.isRegist());
-                                }
-                            } else {
-                                logger.info("[语音对讲] 未找到设备:{}", deviceId);
-                            }
-                        }
-                    }
-
-                } else {
-                    if (!"rtp".equals(param.getApp())) {
-                        String type = OriginType.values()[param.getOriginType()].getType();
-                        if (param.isRegist()) {
-                            StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(
-                                    param.getApp(), param.getStream());
-                            String callId = null;
-                            if (streamAuthorityInfo != null) {
-                                callId = streamAuthorityInfo.getCallId();
-                            }
-                            StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaInfo,
-                                    param.getApp(), param.getStream(), tracks, callId);
-                            param.setStreamInfo(new StreamContent(streamInfoByAppAndStream));
-                            redisCatchStorage.addStream(mediaInfo, type, param.getApp(), param.getStream(), param);
-                            if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
-                                    || param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
-                                    || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
-                                param.setSeverId(userSetting.getServerId());
-                                zlmMediaListManager.addPush(param);
-
-                                // 冗余数据,自己系统中自用
-                                redisCatchStorage.addPushListItem(param.getApp(), param.getStream(), param);
-                            }
-                        } else {
-                            // 兼容流注销时类型从redis记录获取
-                            OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
-                                    param.getApp(), param.getStream(), param.getMediaServerId());
-                            if (onStreamChangedHookParam != null) {
-                                type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
-                                redisCatchStorage.removeStream(mediaInfo.getId(), type, param.getApp(), param.getStream());
-                                if ("PUSH".equalsIgnoreCase(type)) {
-                                    // 冗余数据,自己系统中自用
-                                    redisCatchStorage.removePushListItem(param.getApp(), param.getStream(), param.getMediaServerId());
-                                }
-                            }
-                            GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
-                            if (gbStream != null) {
-//									eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
-                            }
-                            zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
-                        }
-                        GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
-                        if (gbStream != null) {
-                            if (userSetting.isUsePushingAsStatus()) {
-                                eventPublisher.catalogEventPublishForStream(null, gbStream, param.isRegist() ? CatalogEvent.ON : CatalogEvent.OFF);
-                            }
-                        }
-                        if (type != null) {
-                            // 发送流变化redis消息
-                            JSONObject jsonObject = new JSONObject();
-                            jsonObject.put("serverId", userSetting.getServerId());
-                            jsonObject.put("app", param.getApp());
-                            jsonObject.put("stream", param.getStream());
-                            jsonObject.put("register", param.isRegist());
-                            jsonObject.put("mediaServerId", param.getMediaServerId());
-                            redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
-                        }
-                    }
-                }
-                if (!param.isRegist()) {
-                    List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
-                    if (!sendRtpItems.isEmpty()) {
-                        for (SendRtpItem sendRtpItem : sendRtpItems) {
-                            if (sendRtpItem == null) {
-                                continue;
-                            }
-
-                            if (sendRtpItem.getApp().equals(param.getApp())) {
-                                logger.info(sendRtpItem.toString());
-                                if (userSetting.getServerId().equals(sendRtpItem.getServerId())) {
-                                    MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
-                                            sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
-                                            sendRtpItem.getPlatformId(), null, userSetting.getServerId(), param.getMediaServerId());
-                                    // 通知其他wvp停止发流
-                                    redisCatchStorage.sendPushStreamClose(messageForPushChannel);
-                                }else {
-                                    String platformId = sendRtpItem.getPlatformId();
-                                    ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
-                                    Device device = deviceService.getDevice(platformId);
-
-                                    try {
-                                        if (platform != null) {
-                                            commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
-                                            redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(),
-                                                    sendRtpItem.getCallId(), sendRtpItem.getStream());
-                                        } else {
-                                            cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId());
-                                            if (sendRtpItem.getPlayType().equals(InviteStreamType.BROADCAST)
-                                                    || sendRtpItem.getPlayType().equals(InviteStreamType.TALK)) {
-                                                AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
-                                                if (audioBroadcastCatch != null) {
-                                                    // 来自上级平台的停止对讲
-                                                    logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
-                                                    audioBroadcastManager.del(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
-                                                }
-                                            }
-                                        }
-                                    } catch (SipException | InvalidArgumentException | ParseException |
-                                             SsrcTransactionNotFoundException e) {
-                                        logger.error("[命令发送失败] 发送BYE: {}", e.getMessage());
-                                    }
-                                }
-
-                            }
-                        }
-                    }
-                }
-            }
-        });
         return HookResult.SUCCESS();
     }
 
@@ -568,104 +220,9 @@ public class ZLMHttpHookListener {
         logger.info("[ZLM HOOK]流无人观看:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(),
                 param.getApp(), param.getStream());
         JSONObject ret = new JSONObject();
-        ret.put("code", 0);
-        // 国标类型的流
-        if ("rtp".equals(param.getApp())) {
-            ret.put("close", userSetting.getStreamOnDemand());
-            // 国标流, 点播/录像回放/录像下载
-            InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
-            // 点播
-            if (inviteInfo != null) {
-                // 录像下载
-                if (inviteInfo.getType() == InviteSessionType.DOWNLOAD) {
-                    ret.put("close", false);
-                    return ret;
-                }
-                // 收到无人观看说明流也没有在往上级推送
-                if (redisCatchStorage.isChannelSendingRTP(inviteInfo.getChannelId())) {
-                    List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChannelId(
-                            inviteInfo.getChannelId());
-                    if (!sendRtpItems.isEmpty()) {
-                        for (SendRtpItem sendRtpItem : sendRtpItems) {
-                            ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
-                            try {
-                                commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
-                            } catch (SipException | InvalidArgumentException | ParseException e) {
-                                logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
-                            }
-                            redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
-                                    sendRtpItem.getCallId(), sendRtpItem.getStream());
-                            if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) {
-                                MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
-                                        sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
-                                        sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
-                                messageForPushChannel.setPlatFormIndex(parentPlatform.getId());
-                                redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
-                            }
-                        }
-                    }
-                }
-                Device device = deviceService.getDevice(inviteInfo.getDeviceId());
-                if (device != null) {
-                    try {
-                        // 多查询一次防止已经被处理了
-                        InviteInfo info = inviteStreamService.getInviteInfo(inviteInfo.getType(),
-                                inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
-                        if (info != null) {
-                            cmder.streamByeCmd(device, inviteInfo.getChannelId(),
-                                    inviteInfo.getStream(), null);
-                        } else {
-                            logger.info("[无人观看] 未找到设备的点播信息: {}, 流:{}", inviteInfo.getDeviceId(), param.getStream());
-                        }
-                    } catch (InvalidArgumentException | ParseException | SipException |
-                             SsrcTransactionNotFoundException e) {
-                        logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage());
-                    }
-                } else {
-                    logger.info("[无人观看] 未找到设备: {},流:{}", inviteInfo.getDeviceId(), param.getStream());
-                }
-
-                inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
-                        inviteInfo.getChannelId(), inviteInfo.getStream());
-                storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
-                return ret;
-            }
-            SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, param.getStream(), null);
-            if (sendRtpItem != null && "talk".equals(sendRtpItem.getApp())) {
-                ret.put("close", false);
-                return ret;
-            }
-        } else if ("talk".equals(param.getApp()) || "broadcast".equals(param.getApp())) {
-            ret.put("close", false);
-        } else {
-            // 非国标流 推流/拉流代理
-            // 拉流代理
-            StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
-            if (streamProxyItem != null) {
-                if (streamProxyItem.isEnableRemoveNoneReader()) {
-                    // 无人观看自动移除
-                    ret.put("close", true);
-                    streamProxyService.del(param.getApp(), param.getStream());
-                    String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrcUrl();
-                    logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url);
-                } else if (streamProxyItem.isEnableDisableNoneReader()) {
-                    // 无人观看停用
-                    ret.put("close", true);
-                    // 修改数据
-                    streamProxyService.stop(param.getApp(), param.getStream());
-                } else {
-                    // 无人观看不做处理
-                    ret.put("close", false);
-                }
-                return ret;
-            }
-            // TODO 推流具有主动性,暂时不做处理
-//			StreamPushItem streamPushItem = streamPushService.getPush(app, streamId);
-//			if (streamPushItem != null) {
-//				// TODO 发送停止
-//
-//			}
-        }
+
+        boolean close = mediaService.closeStreamOnNoneReader(param.getMediaServerId(), param.getApp(), param.getStream(), param.getSchema());
+        ret.put("code", close);
         return ret;
     }
 
@@ -674,119 +231,17 @@ public class ZLMHttpHookListener {
      */
     @ResponseBody
     @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
-    public DeferredResult<HookResult> onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) {
+    public HookResult onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) {
         logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
 
-        DeferredResult<HookResult> defaultResult = new DeferredResult<>();
-
-        MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
-        if (!userSetting.isAutoApplyPlay() || mediaInfo == null) {
-            defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
-            return defaultResult;
-        }
-
-        if ("rtp".equals(param.getApp())) {
-            String[] s = param.getStream().split("_");
-            if ((s.length != 2 && s.length != 4)) {
-                defaultResult.setResult(HookResult.SUCCESS());
-                return defaultResult;
-            }
-            String deviceId = s[0];
-            String channelId = s[1];
-            Device device = redisCatchStorage.getDevice(deviceId);
-            if (device == null || !device.isOnLine()) {
-                defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
-                return defaultResult;
-            }
-            DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
-            if (deviceChannel == null) {
-                defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
-                return defaultResult;
-            }
-            if (s.length == 2) {
-                logger.info("[ZLM HOOK] 预览流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
-
-                RequestMessage msg = new RequestMessage();
-                String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
-                boolean exist = resultHolder.exist(key, null);
-                msg.setKey(key);
-                String uuid = UUID.randomUUID().toString();
-                msg.setId(uuid);
-                DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
-
-                result.onTimeout(() -> {
-                    logger.info("[ZLM HOOK] 预览流自动点播, 等待超时");
-                    msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
-                    resultHolder.invokeAllResult(msg);
-                    inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
-                    storager.stopPlay(deviceId, channelId);
-                });
-
-                resultHolder.put(key, uuid, result);
-
-                if (!exist) {
-                    playService.play(mediaInfo, deviceId, channelId, null, (code, message, data) -> {
-                        msg.setData(new HookResult(code, message));
-                        resultHolder.invokeResult(msg);
-                    });
-                }
-                return result;
-            } else if (s.length == 4) {
-                // 此时为录像回放, 录像回放格式为> 设备ID_通道ID_开始时间_结束时间
-                String startTimeStr = s[2];
-                String endTimeStr = s[3];
-                if (startTimeStr == null || endTimeStr == null || startTimeStr.length() != 14 || endTimeStr.length() != 14) {
-                    defaultResult.setResult(HookResult.SUCCESS());
-                    return defaultResult;
-                }
-                String startTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(startTimeStr);
-                String endTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(endTimeStr);
-                logger.info("[ZLM HOOK] 回放流未找到, 发起自动点播:{}->{}->{}/{}-{}-{}",
-                        param.getMediaServerId(), param.getSchema(),
-                        param.getApp(), param.getStream(),
-                        startTime, endTime
-                );
-                RequestMessage msg = new RequestMessage();
-                String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId;
-                boolean exist = resultHolder.exist(key, null);
-                msg.setKey(key);
-                String uuid = UUID.randomUUID().toString();
-                msg.setId(uuid);
-                DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
-
-                result.onTimeout(() -> {
-                    logger.info("[ZLM HOOK] 回放流自动点播, 等待超时");
-                    // 释放rtpserver
-                    msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
-                    resultHolder.invokeResult(msg);
-                });
-
-                resultHolder.put(key, uuid, result);
-
-                if (!exist) {
-                    SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaInfo, param.getStream(), null,
-                            device.isSsrcCheck(), true, 0, false, false, device.getStreamModeForParam());
-                    playService.playBack(mediaInfo, ssrcInfo, deviceId, channelId, startTime, endTime, (code, message, data) -> {
-                        msg.setData(new HookResult(code, message));
-                        resultHolder.invokeResult(msg);
-                    });
-                }
-                return result;
-            } else {
-                defaultResult.setResult(HookResult.SUCCESS());
-                return defaultResult;
-            }
 
-        } else {
-            // 拉流代理
-            StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
-            if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnableDisableNoneReader()) {
-                streamProxyService.start(param.getApp(), param.getStream());
-            }
-            DeferredResult<HookResult> result = new DeferredResult<>();
-            result.setResult(HookResult.SUCCESS());
-            return result;
+        MediaServer mediaServer = mediaServerService.getOne(param.getMediaServerId());
+        if (!userSetting.isAutoApplyPlay() || mediaServer == null) {
+            return HookResult.SUCCESS();
         }
+        MediaNotFoundEvent mediaNotFoundEvent = MediaNotFoundEvent.getInstance(this, param, mediaServer);
+        applicationEventPublisher.publishEvent(mediaNotFoundEvent);
+        return HookResult.SUCCESS();
     }
 
     /**
@@ -800,15 +255,16 @@ public class ZLMHttpHookListener {
         ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
         zlmServerConfig.setIp(request.getRemoteAddr());
         logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
-        taskExecutor.execute(() -> {
-            List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
-            if (subscribes != null && !subscribes.isEmpty()) {
-                for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
-                    subscribe.response(null, zlmServerConfig);
-                }
+        try {
+            HookZlmServerStartEvent event = new HookZlmServerStartEvent(this);
+            MediaServer mediaServerItem = mediaServerService.getOne(zlmServerConfig.getMediaServerId());
+            if (mediaServerItem != null) {
+                event.setMediaServerItem(mediaServerItem);
+                applicationEventPublisher.publishEvent(event);
             }
-            mediaServerService.zlmServerOnline(zlmServerConfig);
-        });
+        }catch (Exception e) {
+            logger.info("[ZLM-HOOK-ZLM启动] 发送通知失败 ", e);
+        }
 
         return HookResult.SUCCESS();
     }
@@ -826,22 +282,16 @@ public class ZLMHttpHookListener {
         if (!"rtp".equals(param.getApp())) {
             return HookResult.SUCCESS();
         }
-        taskExecutor.execute(() -> {
-            List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
-            if (sendRtpItems.size() > 0) {
-                for (SendRtpItem sendRtpItem : sendRtpItems) {
-                    ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
-                    ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
-                    try {
-                        commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
-                    } catch (SipException | InvalidArgumentException | ParseException e) {
-                        logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
-                    }
-                    redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
-                            sendRtpItem.getCallId(), sendRtpItem.getStream());
-                }
+        try {
+            MediaSendRtpStoppedEvent event = new MediaSendRtpStoppedEvent(this);
+            MediaServer mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
+            if (mediaServerItem != null) {
+                event.setMediaServer(mediaServerItem);
+                applicationEventPublisher.publishEvent(event);
             }
-        });
+        }catch (Exception e) {
+            logger.info("[ZLM-HOOK-rtp发送关闭] 发送通知失败 ", e);
+        }
 
         return HookResult.SUCCESS();
     }
@@ -855,14 +305,17 @@ public class ZLMHttpHookListener {
             param) {
         logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
 
-        taskExecutor.execute(() -> {
-            List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
-            if (subscribes != null && !subscribes.isEmpty()) {
-                for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
-                    subscribe.response(null, param);
-                }
+        try {
+            MediaRtpServerTimeoutEvent event = new MediaRtpServerTimeoutEvent(this);
+            MediaServer mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
+            if (mediaServerItem != null) {
+                event.setMediaServer(mediaServerItem);
+                event.setApp("rtp");
+                applicationEventPublisher.publishEvent(event);
             }
-        });
+        }catch (Exception e) {
+            logger.info("[ZLM-HOOK-rtpServer收流超时] 发送通知失败 ", e);
+        }
 
         return HookResult.SUCCESS();
     }
@@ -875,16 +328,16 @@ public class ZLMHttpHookListener {
     public HookResult onRecordMp4(HttpServletRequest request, @RequestBody OnRecordMp4HookParam param) {
         logger.info("[ZLM HOOK] 录像完成事件:{}->{}", param.getMediaServerId(), param.getFile_path());
 
-        taskExecutor.execute(() -> {
-            List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_record_mp4);
-            if (subscribes != null && !subscribes.isEmpty()) {
-                for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
-                    subscribe.response(null, param);
-                }
+        try {
+            MediaServer mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
+            if (mediaServerItem != null) {
+                MediaRecordMp4Event event = MediaRecordMp4Event.getInstance(this, param, mediaServerItem);
+                event.setMediaServer(mediaServerItem);
+                applicationEventPublisher.publishEvent(event);
             }
-            cloudRecordService.addRecord(param);
-
-        });
+        }catch (Exception e) {
+            logger.info("[ZLM-HOOK-rtpServer收流超时] 发送通知失败 ", e);
+        }
 
         return HookResult.SUCCESS();
     }

+ 6 - 11
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java

@@ -2,12 +2,13 @@ package com.genersoft.iot.vmp.media.zlm;
 
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.dto.*;
 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
-import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IStreamProxyService;
 import com.genersoft.iot.vmp.service.IStreamPushService;
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
 import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper;
@@ -30,12 +31,6 @@ public class ZLMMediaListManager {
 
     private Logger logger = LoggerFactory.getLogger("ZLMMediaListManager");
 
-    @Autowired
-    private ZLMRESTfulUtils zlmresTfulUtils;
-
-    @Autowired
-    private IRedisCatchStorage redisCatchStorage;
-
     @Autowired
     private IVideoManagerStorage storager;
 
@@ -55,7 +50,7 @@ public class ZLMMediaListManager {
     private StreamPushMapper streamPushMapper;
 
     @Autowired
-    private ZlmHttpHookSubscribe subscribe;
+    private HookSubscribe subscribe;
 
     @Autowired
     private UserSetting userSetting;
@@ -95,9 +90,9 @@ public class ZLMMediaListManager {
     }
 
     public void sendStreamEvent(String app, String stream, String mediaServerId) {
-        MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
+        MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId);
         // 查看推流状态
-        Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, app, stream);
+        Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, app, stream);
         if (streamReady != null && streamReady) {
             ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(app, stream);
             if (channelOnlineEventLister != null)  {

+ 349 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java

@@ -0,0 +1,349 @@
+package com.genersoft.iot.vmp.media.zlm;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.genersoft.iot.vmp.common.CommonCallback;
+import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.conf.exception.ControllerException;
+import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.media.bean.MediaInfo;
+import com.genersoft.iot.vmp.media.service.IMediaNodeServerService;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig;
+import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.util.ObjectUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service("zlm")
+public class ZLMMediaNodeServerService implements IMediaNodeServerService {
+
+    private final static Logger logger = LoggerFactory.getLogger(ZLMMediaNodeServerService.class);
+
+    @Autowired
+    private ZLMRESTfulUtils zlmresTfulUtils;
+
+    @Autowired
+    private ZLMServerFactory zlmServerFactory;
+
+    @Value("${sip.ip}")
+    private String sipIp;
+
+    @Override
+    public int createRTPServer(MediaServer mediaServer, String streamId, long ssrc, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode) {
+        return zlmServerFactory.createRTPServer(mediaServer, streamId, ssrc, port, onlyAuto, reUsePort, tcpMode);
+    }
+
+    @Override
+    public void closeRtpServer(MediaServer mediaServer, String streamId) {
+        zlmServerFactory.closeRtpServer(mediaServer, streamId);
+    }
+
+    @Override
+    public void closeRtpServer(MediaServer mediaServer, String streamId, CommonCallback<Boolean> callback) {
+        zlmServerFactory.closeRtpServer(mediaServer, streamId, callback);
+    }
+
+    @Override
+    public void closeStreams(MediaServer mediaServer, String app, String stream) {
+        zlmresTfulUtils.closeStreams(mediaServer, app, stream);
+    }
+
+    @Override
+    public Boolean updateRtpServerSSRC(MediaServer mediaServer, String streamId, String ssrc) {
+        return zlmServerFactory.updateRtpServerSSRC(mediaServer, streamId, ssrc);
+    }
+
+    @Override
+    public boolean checkNodeId(MediaServer mediaServer) {
+        if (mediaServer == null) {
+            return false;
+        }
+        JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServer);
+        if (responseJSON != null) {
+            JSONArray data = responseJSON.getJSONArray("data");
+            if (data != null && !data.isEmpty()) {
+                ZLMServerConfig zlmServerConfig= JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
+                return zlmServerConfig.getGeneralMediaServerId().equals(mediaServer.getId());
+            }else {
+                return false;
+            }
+
+        }else {
+            return false;
+        }
+    }
+
+    @Override
+    public void online(MediaServer mediaServer) {
+
+    }
+
+    @Override
+    public MediaServer checkMediaServer(String ip, int port, String secret) {
+        MediaServer mediaServer = new MediaServer();
+        mediaServer.setIp(ip);
+        mediaServer.setHttpPort(port);
+        mediaServer.setSecret(secret);
+        JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServer);
+        if (responseJSON == null) {
+            throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接失败");
+        }
+        JSONArray data = responseJSON.getJSONArray("data");
+        if (data == null) {
+            throw new ControllerException(ErrorCode.ERROR100.getCode(), "读取配置失败");
+        }
+        ZLMServerConfig zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
+        if (zlmServerConfig == null) {
+            throw new ControllerException(ErrorCode.ERROR100.getCode(), "读取配置失败");
+        }
+        mediaServer.setId(zlmServerConfig.getGeneralMediaServerId());
+        mediaServer.setHttpSSlPort(zlmServerConfig.getHttpPort());
+        mediaServer.setRtmpPort(zlmServerConfig.getRtmpPort());
+        mediaServer.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
+        mediaServer.setRtspPort(zlmServerConfig.getRtspPort());
+        mediaServer.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
+        mediaServer.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
+        mediaServer.setStreamIp(ip);
+        mediaServer.setHookIp(sipIp.split(",")[0]);
+        mediaServer.setSdpIp(ip);
+        mediaServer.setType("zlm");
+        return mediaServer;
+    }
+
+    @Override
+    public boolean stopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc) {
+        Map<String, Object> param = new HashMap<>();
+        param.put("vhost", "__defaultVhost__");
+        param.put("app", app);
+        param.put("stream", stream);
+        if (!ObjectUtils.isEmpty(ssrc)) {
+            param.put("ssrc", ssrc);
+        }
+        JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaInfo, param);
+        return (jsonObject != null && jsonObject.getInteger("code") == 0);
+
+    }
+
+    @Override
+    public boolean deleteRecordDirectory(MediaServer mediaServer, String app, String stream, String date, String fileName) {
+        logger.info("[zlm-deleteRecordDirectory] 删除磁盘文件, server: {} {}:{}->{}/{}", mediaServer.getId(), app, stream, date, fileName);
+        JSONObject jsonObject = zlmresTfulUtils.deleteRecordDirectory(mediaServer, app,
+                stream, date, fileName);
+        if (jsonObject.getInteger("code") == 0) {
+            return true;
+        }else {
+            logger.info("[zlm-deleteRecordDirectory] 删除磁盘文件错误, server: {} {}:{}->{}/{}, 结果: {}", mediaServer.getId(), app, stream, date, fileName, jsonObject);
+            return false;
+        }
+    }
+
+    @Override
+    public List<StreamInfo> getMediaList(MediaServer mediaServer, String app, String stream, String callId) {
+        List<StreamInfo> streamInfoList = new ArrayList<>();
+        JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaServer, app, stream);
+        if (mediaList != null) {
+            if (mediaList.getInteger("code") == 0) {
+                JSONArray data = mediaList.getJSONArray("data");
+                if (data == null) {
+                    return null;
+                }
+                JSONObject mediaJSON = data.getJSONObject(0);
+                MediaInfo mediaInfo = MediaInfo.getInstance(mediaJSON, mediaServer);
+                StreamInfo streamInfo = getStreamInfoByAppAndStream(mediaServer, app, stream, mediaInfo, callId, true);
+                if (streamInfo != null) {
+                    streamInfoList.add(streamInfo);
+                }
+            }
+        }
+        return streamInfoList;
+    }
+
+    public StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServer, String app, String stream, MediaInfo mediaInfo, String callId, boolean isPlay) {
+        StreamInfo streamInfoResult = new StreamInfo();
+        streamInfoResult.setStream(stream);
+        streamInfoResult.setApp(app);
+        String addr = mediaServer.getStreamIp();
+        streamInfoResult.setIp(addr);
+        streamInfoResult.setMediaServerId(mediaServer.getId());
+        String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId;
+        streamInfoResult.setRtmp(addr, mediaServer.getRtmpPort(),mediaServer.getRtmpSSlPort(), app,  stream, callIdParam);
+        streamInfoResult.setRtsp(addr, mediaServer.getRtspPort(),mediaServer.getRtspSSLPort(), app,  stream, callIdParam);
+        String flvFile = String.format("%s/%s.live.flv%s", app, stream, callIdParam);
+        streamInfoResult.setFlv(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), flvFile);
+        streamInfoResult.setWsFlv(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), flvFile);
+        streamInfoResult.setFmp4(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app,  stream, callIdParam);
+        streamInfoResult.setHls(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app,  stream, callIdParam);
+        streamInfoResult.setTs(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app,  stream, callIdParam);
+        streamInfoResult.setRtc(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app,  stream, callIdParam, isPlay);
+
+        streamInfoResult.setMediaInfo(mediaInfo);
+        streamInfoResult.setOriginType(mediaInfo.getOriginType());
+        return streamInfoResult;
+    }
+
+    @Override
+    public Boolean connectRtpServer(MediaServer mediaServer, String address, int port, String stream) {
+        JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServer, address, port, stream);
+        logger.info("[TCP主动连接对方] 结果: {}", jsonObject);
+        return jsonObject.getInteger("code") == 0;
+    }
+
+    @Override
+    public void getSnap(MediaServer mediaServer, String streamUrl, int timeoutSec, int expireSec, String path, String fileName) {
+        zlmresTfulUtils.getSnap(mediaServer, streamUrl, timeoutSec, expireSec, path, fileName);
+    }
+
+    @Override
+    public MediaInfo getMediaInfo(MediaServer mediaServer, String app, String stream) {
+        JSONObject jsonObject = zlmresTfulUtils.getMediaInfo(mediaServer, app, "rtsp", stream);
+        if (jsonObject.getInteger("code") != 0) {
+            return null;
+        }
+        return MediaInfo.getInstance(jsonObject, mediaServer);
+    }
+
+    @Override
+    public Boolean pauseRtpCheck(MediaServer mediaServer, String streamKey) {
+        JSONObject jsonObject = zlmresTfulUtils.pauseRtpCheck(mediaServer, streamKey);
+        return jsonObject.getInteger("code") == 0;
+    }
+
+    @Override
+    public Boolean resumeRtpCheck(MediaServer mediaServer, String streamKey) {
+        JSONObject jsonObject = zlmresTfulUtils.resumeRtpCheck(mediaServer, streamKey);
+        return jsonObject.getInteger("code") == 0;
+    }
+
+    @Override
+    public String getFfmpegCmd(MediaServer mediaServer, String cmdKey) {
+        JSONObject jsonObject = zlmresTfulUtils.getMediaServerConfig(mediaServer);
+        if (jsonObject.getInteger("code") != 0) {
+            logger.warn("[getFfmpegCmd] 获取流媒体配置失败");
+            throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取流媒体配置失败");
+        }
+        JSONArray dataArray = jsonObject.getJSONArray("data");
+        JSONObject mediaServerConfig = dataArray.getJSONObject(0);
+        if (ObjectUtils.isEmpty(cmdKey)) {
+            cmdKey = "ffmpeg.cmd";
+        }
+       return mediaServerConfig.getString(cmdKey);
+    }
+
+    @Override
+    public WVPResult<String> addFFmpegSource(MediaServer mediaServer, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey) {
+        JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(mediaServer, srcUrl, dstUrl, timeoutMs, enableAudio, enableMp4, ffmpegCmdKey);
+        if (jsonObject.getInteger("code") != 0) {
+            logger.warn("[getFfmpegCmd] 添加FFMPEG代理失败");
+            return WVPResult.fail(ErrorCode.ERROR100.getCode(), "添加FFMPEG代理失败");
+        }else {
+            JSONObject data = jsonObject.getJSONObject("data");
+            if (data == null) {
+                return WVPResult.fail(ErrorCode.ERROR100.getCode(), "代理结果异常: " + jsonObject);
+            }else {
+                return WVPResult.success(data.getString("key"));
+            }
+        }
+    }
+
+    @Override
+    public WVPResult<String> addStreamProxy(MediaServer mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType) {
+        JSONObject jsonObject = zlmresTfulUtils.addStreamProxy(mediaServer, app, stream, url, enableAudio, enableMp4, rtpType);
+        if (jsonObject.getInteger("code") != 0) {
+            return WVPResult.fail(ErrorCode.ERROR100.getCode(), "添加代理失败");
+        }else {
+            JSONObject data = jsonObject.getJSONObject("data");
+            if (data == null) {
+                return WVPResult.fail(ErrorCode.ERROR100.getCode(), "代理结果异常: " + jsonObject);
+            }else {
+                return WVPResult.success(data.getString("key"));
+            }
+        }
+    }
+
+    @Override
+    public Boolean delFFmpegSource(MediaServer mediaServer, String streamKey) {
+        JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(mediaServer, streamKey);
+        return jsonObject.getInteger("code") == 0;
+    }
+
+    @Override
+    public Boolean delStreamProxy(MediaServer mediaServer, String streamKey) {
+        JSONObject jsonObject = zlmresTfulUtils.delStreamProxy(mediaServer, streamKey);
+        return jsonObject.getInteger("code") == 0;
+    }
+
+    @Override
+    public Map<String, String> getFFmpegCMDs(MediaServer mediaServer) {
+        Map<String, String> result = new HashMap<>();
+        JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(mediaServer);
+        if (mediaServerConfigResuly != null && mediaServerConfigResuly.getInteger("code") == 0
+                && mediaServerConfigResuly.getJSONArray("data").size() > 0){
+            JSONObject mediaServerConfig = mediaServerConfigResuly.getJSONArray("data").getJSONObject(0);
+
+            for (String key : mediaServerConfig.keySet()) {
+                if (key.startsWith("ffmpeg.cmd")){
+                    result.put(key, mediaServerConfig.getString(key));
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public void startSendRtpPassive(MediaServer mediaServer, SendRtpItem sendRtpItem, Integer timeout) {
+        Map<String, Object> param = new HashMap<>(12);
+        param.put("vhost","__defaultVhost__");
+        param.put("app", sendRtpItem.getApp());
+        param.put("stream", sendRtpItem.getStream());
+        param.put("ssrc", sendRtpItem.getSsrc());
+        param.put("src_port", sendRtpItem.getLocalPort());
+        param.put("pt", sendRtpItem.getPt());
+        param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0");
+        param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0");
+        param.put("is_udp", sendRtpItem.isTcp() ? "0" : "1");
+        param.put("recv_stream_id", sendRtpItem.getReceiveStream());
+        if (timeout  != null) {
+            param.put("close_delay_ms", timeout);
+        }
+
+        JSONObject jsonObject = zlmServerFactory.startSendRtpPassive(mediaServer, param, null);
+        if (jsonObject == null || jsonObject.getInteger("code") != 0 ) {
+            throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg"));
+        }
+    }
+
+    @Override
+    public void startSendRtpStream(MediaServer mediaServer, SendRtpItem sendRtpItem) {
+        Map<String, Object> param = new HashMap<>(12);
+        param.put("vhost", "__defaultVhost__");
+        param.put("app", sendRtpItem.getApp());
+        param.put("stream", sendRtpItem.getStream());
+        param.put("ssrc", sendRtpItem.getSsrc());
+        param.put("src_port", sendRtpItem.getLocalPort());
+        param.put("pt", sendRtpItem.getPt());
+        param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0");
+        param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0");
+        param.put("is_udp", sendRtpItem.isTcp() ? "0" : "1");
+        if (!sendRtpItem.isTcp()) {
+            // udp模式下开启rtcp保活
+            param.put("udp_rtcp_timeout", sendRtpItem.isRtcp() ? "1" : "0");
+        }
+        param.put("dst_url", sendRtpItem.getIp());
+        param.put("dst_port", sendRtpItem.getPort());
+        JSONObject jsonObject = zlmServerFactory.startSendRtpStream(mediaServer, param);
+        if (jsonObject == null || jsonObject.getInteger("code") != 0 ) {
+            throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg"));
+        }
+    }
+}

+ 306 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaServerStatusManger.java

@@ -0,0 +1,306 @@
+package com.genersoft.iot.vmp.media.zlm;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.genersoft.iot.vmp.conf.DynamicTask;
+import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent;
+import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerDeleteEvent;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig;
+import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerKeepaliveEvent;
+import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerStartEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 管理zlm流媒体节点的状态
+ */
+@Component
+public class ZLMMediaServerStatusManger {
+
+    private final static Logger logger = LoggerFactory.getLogger(ZLMMediaServerStatusManger.class);
+
+    private final Map<Object, MediaServer> offlineZlmPrimaryMap = new ConcurrentHashMap<>();
+    private final Map<Object, MediaServer> offlineZlmsecondaryMap = new ConcurrentHashMap<>();
+    private final Map<Object, Long> offlineZlmTimeMap = new ConcurrentHashMap<>();
+
+    @Autowired
+    private ZLMRESTfulUtils zlmresTfulUtils;
+
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+    @Autowired
+    private DynamicTask dynamicTask;
+
+    @Value("${server.ssl.enabled:false}")
+    private boolean sslEnabled;
+
+    @Value("${server.port}")
+    private Integer serverPort;
+
+    @Value("${server.servlet.context-path:}")
+    private String serverServletContextPath;
+
+    @Autowired
+    private UserSetting userSetting;
+
+    private final String type = "zlm";
+
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaServerChangeEvent event) {
+        if (event.getMediaServerItemList() == null
+                || event.getMediaServerItemList().isEmpty()) {
+            return;
+        }
+        for (MediaServer mediaServerItem : event.getMediaServerItemList()) {
+            if (!type.equals(mediaServerItem.getType())) {
+                continue;
+            }
+            logger.info("[ZLM-添加待上线节点] ID:" + mediaServerItem.getId());
+            offlineZlmPrimaryMap.put(mediaServerItem.getId(), mediaServerItem);
+            offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis());
+            execute();
+        }
+    }
+
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(HookZlmServerStartEvent event) {
+        if (event.getMediaServerItem() == null
+                || !type.equals(event.getMediaServerItem().getType())
+                || event.getMediaServerItem().isStatus()) {
+            return;
+        }
+        MediaServer serverItem = mediaServerService.getOne(event.getMediaServerItem().getId());
+        if (serverItem == null) {
+            return;
+        }
+        logger.info("[ZLM-HOOK事件-服务启动] ID:" + event.getMediaServerItem().getId());
+        online(serverItem, null);
+    }
+
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(HookZlmServerKeepaliveEvent event) {
+        if (event.getMediaServerItem() == null) {
+            return;
+        }
+        MediaServer serverItem = mediaServerService.getOne(event.getMediaServerItem().getId());
+        if (serverItem == null) {
+            return;
+        }
+        logger.info("[ZLM-HOOK事件-心跳] ID:" + event.getMediaServerItem().getId());
+        online(serverItem, null);
+    }
+
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaServerDeleteEvent event) {
+        if (event.getMediaServerId() == null) {
+            return;
+        }
+        logger.info("[ZLM-节点被移除] ID:" + event.getMediaServerId());
+        offlineZlmPrimaryMap.remove(event.getMediaServerId());
+        offlineZlmsecondaryMap.remove(event.getMediaServerId());
+        offlineZlmTimeMap.remove(event.getMediaServerId());
+    }
+
+    @Scheduled(fixedDelay = 10*1000)   //每隔10秒检查一次
+    public void execute(){
+        // 初次加入的离线节点会在30分钟内,每间隔十秒尝试一次,30分钟后如果仍然没有上线,则每隔30分钟尝试一次连接
+        if (offlineZlmPrimaryMap.isEmpty() && offlineZlmsecondaryMap.isEmpty()) {
+            return;
+        }
+        if (!offlineZlmPrimaryMap.isEmpty()) {
+            for (MediaServer mediaServerItem : offlineZlmPrimaryMap.values()) {
+                if (offlineZlmTimeMap.get(mediaServerItem.getId()) <  System.currentTimeMillis() - 30*60*1000) {
+                    offlineZlmsecondaryMap.put(mediaServerItem.getId(), mediaServerItem);
+                    offlineZlmPrimaryMap.remove(mediaServerItem.getId());
+                    continue;
+                }
+                logger.info("[ZLM-尝试连接] ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
+                JSONObject responseJson = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
+                ZLMServerConfig zlmServerConfig = null;
+                if (responseJson == null) {
+                    logger.info("[ZLM-尝试连接]失败, ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
+                    continue;
+                }
+                JSONArray data = responseJson.getJSONArray("data");
+                if (data == null || data.isEmpty()) {
+                    logger.info("[ZLM-尝试连接]失败, ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
+                }else {
+                    zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
+                    initPort(mediaServerItem, zlmServerConfig);
+                    online(mediaServerItem, zlmServerConfig);
+                }
+            }
+        }
+        if (!offlineZlmsecondaryMap.isEmpty()) {
+            for (MediaServer mediaServerItem : offlineZlmsecondaryMap.values()) {
+                if (offlineZlmTimeMap.get(mediaServerItem.getId()) <  System.currentTimeMillis() - 30*60*1000) {
+                    continue;
+                }
+                logger.info("[ZLM-尝试连接] ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
+                JSONObject responseJson = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
+                ZLMServerConfig zlmServerConfig = null;
+                if (responseJson == null) {
+                    logger.info("[ZLM-尝试连接]失败, ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
+                    offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis());
+                    continue;
+                }
+                JSONArray data = responseJson.getJSONArray("data");
+                if (data == null || data.isEmpty()) {
+                    logger.info("[ZLM-尝试连接]失败, ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
+                    offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis());
+                }else {
+                    zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
+                    initPort(mediaServerItem, zlmServerConfig);
+                    online(mediaServerItem, zlmServerConfig);
+                }
+            }
+        }
+    }
+
+    private void online(MediaServer mediaServerItem, ZLMServerConfig config) {
+        offlineZlmPrimaryMap.remove(mediaServerItem.getId());
+        offlineZlmsecondaryMap.remove(mediaServerItem.getId());
+        offlineZlmTimeMap.remove(mediaServerItem.getId());
+        if (!mediaServerItem.isStatus()) {
+            logger.info("[ZLM-连接成功] ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
+            mediaServerItem.setStatus(true);
+            mediaServerItem.setHookAliveInterval(10F);
+            mediaServerService.update(mediaServerItem);
+            if(mediaServerItem.isAutoConfig()) {
+                if (config == null) {
+                    JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
+                    JSONArray data = responseJSON.getJSONArray("data");
+                    if (data != null && !data.isEmpty()) {
+                        config = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
+                    }
+                }
+                if (config != null) {
+                    initPort(mediaServerItem, config);
+                    setZLMConfig(mediaServerItem, "0".equals(config.getHookEnable())
+                            || !Objects.equals(mediaServerItem.getHookAliveInterval(), config.getHookAliveInterval()));
+                }
+            }
+            mediaServerService.update(mediaServerItem);
+        }
+        // 设置两次心跳未收到则认为zlm离线
+        String key = "zlm-keepalive-" + mediaServerItem.getId();
+        dynamicTask.startDelay(key, ()->{
+            logger.warn("[ZLM-心跳超时] ID:{}", mediaServerItem.getId());
+            mediaServerItem.setStatus(false);
+            offlineZlmPrimaryMap.put(mediaServerItem.getId(), mediaServerItem);
+            offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis());
+            // TODO 发送离线通知
+            mediaServerService.update(mediaServerItem);
+        }, (int)(mediaServerItem.getHookAliveInterval() * 2 * 1000));
+    }
+    private void initPort(MediaServer mediaServerItem, ZLMServerConfig zlmServerConfig) {
+        // 端口只会从配置中读取一次,一旦自己配置或者读取过了将不在配置
+        if (mediaServerItem.getHttpSSlPort() == 0) {
+            mediaServerItem.setHttpSSlPort(zlmServerConfig.getHttpSSLport());
+        }
+        if (mediaServerItem.getRtmpPort() == 0) {
+            mediaServerItem.setRtmpPort(zlmServerConfig.getRtmpPort());
+        }
+        if (mediaServerItem.getRtmpSSlPort() == 0) {
+            mediaServerItem.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
+        }
+        if (mediaServerItem.getRtspPort() == 0) {
+            mediaServerItem.setRtspPort(zlmServerConfig.getRtspPort());
+        }
+        if (mediaServerItem.getRtspSSLPort() == 0) {
+            mediaServerItem.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
+        }
+        if (mediaServerItem.getRtpProxyPort() == 0) {
+            mediaServerItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
+        }
+        mediaServerItem.setHookAliveInterval(10F);
+    }
+
+    public void setZLMConfig(MediaServer mediaServerItem, boolean restart) {
+        logger.info("[媒体服务节点] 正在设置 :{} -> {}:{}",
+                mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
+        String protocol = sslEnabled ? "https" : "http";
+        String hookPrefix = String.format("%s://%s:%s%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort, (serverServletContextPath == null || "/".equals(serverServletContextPath)) ? "" : serverServletContextPath);
+
+        Map<String, Object> param = new HashMap<>();
+        param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
+        if (mediaServerItem.getRtspPort() != 0) {
+            param.put("ffmpeg.snap", "%s -rtsp_transport tcp -i %s -y -f mjpeg -frames:v 1 %s");
+        }
+        param.put("hook.enable","1");
+        param.put("hook.on_flow_report","");
+        param.put("hook.on_play",String.format("%s/on_play", hookPrefix));
+        param.put("hook.on_http_access","");
+        param.put("hook.on_publish", String.format("%s/on_publish", hookPrefix));
+        param.put("hook.on_record_ts","");
+        param.put("hook.on_rtsp_auth","");
+        param.put("hook.on_rtsp_realm","");
+        param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrefix));
+        param.put("hook.on_shell_login","");
+        param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrefix));
+        param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrefix));
+        param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrefix));
+        param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrefix));
+        param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrefix));
+        param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrefix));
+        param.put("hook.on_record_mp4",String.format("%s/on_record_mp4", hookPrefix));
+        param.put("hook.timeoutSec","30");
+        param.put("hook.alive_interval", mediaServerItem.getHookAliveInterval());
+        // 推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。
+        // 置0关闭此特性(推流断开会导致立即断开播放器)
+        // 此参数不应大于播放器超时时间
+        // 优化此消息以更快的收到流注销事件
+        param.put("protocol.continue_push_ms", "3000" );
+        // 最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流,
+        // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项
+        if (mediaServerItem.isRtpEnable() && !ObjectUtils.isEmpty(mediaServerItem.getRtpPortRange())) {
+            param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-"));
+        }
+
+        if (!ObjectUtils.isEmpty(mediaServerItem.getRecordPath())) {
+            File recordPathFile = new File(mediaServerItem.getRecordPath());
+            param.put("protocol.mp4_save_path", recordPathFile.getParentFile().getPath());
+            param.put("protocol.downloadRoot", recordPathFile.getParentFile().getPath());
+            param.put("record.appName", recordPathFile.getName());
+        }
+
+        JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param);
+
+        if (responseJSON != null && responseJSON.getInteger("code") == 0) {
+            if (restart) {
+                logger.info("[媒体服务节点] 设置成功,开始重启以保证配置生效 {} -> {}:{}",
+                        mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
+                zlmresTfulUtils.restartServer(mediaServerItem);
+            }else {
+                logger.info("[媒体服务节点] 设置成功 {} -> {}:{}",
+                        mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
+            }
+        }else {
+            logger.info("[媒体服务节点] 设置媒体服务节点失败 {} -> {}:{}",
+                    mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
+        }
+    }
+
+}

+ 34 - 36
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java

@@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.media.zlm;
 
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import okhttp3.*;
 import okhttp3.logging.HttpLoggingInterceptor;
 import org.jetbrains.annotations.NotNull;
@@ -60,12 +60,12 @@ public class ZLMRESTfulUtils {
 
     }
 
-    public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
+    public JSONObject sendPost(MediaServer mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
         return sendPost(mediaServerItem, api, param, callback, null);
     }
 
 
-    public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback, Integer readTimeOut) {
+    public JSONObject sendPost(MediaServer mediaServerItem, String api, Map<String, Object> param, RequestCallback callback, Integer readTimeOut) {
         OkHttpClient client = getClient(readTimeOut);
 
         if (mediaServerItem == null) {
@@ -104,8 +104,6 @@ public class ZLMRESTfulUtils {
                             responseJSON = JSON.parseObject(responseStr);
                         }
                     }else {
-                        System.out.println( 2222);
-                        System.out.println( response.code());
                         response.close();
                         Objects.requireNonNull(response.body()).close();
                     }
@@ -164,7 +162,7 @@ public class ZLMRESTfulUtils {
         return responseJSON;
     }
 
-    public void sendGetForImg(MediaServerItem mediaServerItem, String api, Map<String, Object> params, String targetPath, String fileName) {
+    public void sendGetForImg(MediaServer mediaServerItem, String api, Map<String, Object> params, String targetPath, String fileName) {
         String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
         HttpUrl parseUrl = HttpUrl.parse(url);
         if (parseUrl == null) {
@@ -216,7 +214,7 @@ public class ZLMRESTfulUtils {
         }
     }
 
-    public JSONObject isMediaOnline(MediaServerItem mediaServerItem, String app, String stream, String schema){
+    public JSONObject isMediaOnline(MediaServer mediaServerItem, String app, String stream, String schema){
         Map<String, Object> param = new HashMap<>();
         if (app != null) {
             param.put("app",app);
@@ -231,7 +229,7 @@ public class ZLMRESTfulUtils {
         return sendPost(mediaServerItem, "isMediaOnline", param, null);
     }
 
-    public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream, String schema, RequestCallback callback){
+    public JSONObject getMediaList(MediaServer mediaServerItem, String app, String stream, String schema, RequestCallback callback){
         Map<String, Object> param = new HashMap<>();
         if (app != null) {
             param.put("app",app);
@@ -246,15 +244,15 @@ public class ZLMRESTfulUtils {
         return sendPost(mediaServerItem, "getMediaList",param, callback);
     }
 
-    public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream){
+    public JSONObject getMediaList(MediaServer mediaServerItem, String app, String stream){
         return getMediaList(mediaServerItem, app, stream,null,  null);
     }
 
-    public JSONObject getMediaList(MediaServerItem mediaServerItem, RequestCallback callback){
+    public JSONObject getMediaList(MediaServer mediaServerItem, RequestCallback callback){
         return sendPost(mediaServerItem, "getMediaList",null, callback);
     }
 
-    public JSONObject getMediaInfo(MediaServerItem mediaServerItem, String app, String schema, String stream){
+    public JSONObject getMediaInfo(MediaServer mediaServerItem, String app, String schema, String stream){
         Map<String, Object> param = new HashMap<>();
         param.put("app",app);
         param.put("schema",schema);
@@ -263,13 +261,13 @@ public class ZLMRESTfulUtils {
         return sendPost(mediaServerItem, "getMediaInfo",param, null);
     }
 
-    public JSONObject getRtpInfo(MediaServerItem mediaServerItem, String stream_id){
+    public JSONObject getRtpInfo(MediaServer mediaServerItem, String stream_id){
         Map<String, Object> param = new HashMap<>();
         param.put("stream_id",stream_id);
         return sendPost(mediaServerItem, "getRtpInfo",param, null);
     }
 
-    public JSONObject addFFmpegSource(MediaServerItem mediaServerItem, String src_url, String dst_url, String timeout_ms,
+    public JSONObject addFFmpegSource(MediaServer mediaServerItem, String src_url, String dst_url, Integer timeout_ms,
                                       boolean enable_audio, boolean enable_mp4, String ffmpeg_cmd_key){
         logger.info(src_url);
         logger.info(dst_url);
@@ -282,63 +280,63 @@ public class ZLMRESTfulUtils {
         return sendPost(mediaServerItem, "addFFmpegSource",param, null);
     }
 
-    public JSONObject delFFmpegSource(MediaServerItem mediaServerItem, String key){
+    public JSONObject delFFmpegSource(MediaServer mediaServerItem, String key){
         Map<String, Object> param = new HashMap<>();
         param.put("key", key);
         return sendPost(mediaServerItem, "delFFmpegSource",param, null);
     }
 
-    public JSONObject delStreamProxy(MediaServerItem mediaServerItem, String key){
+    public JSONObject delStreamProxy(MediaServer mediaServerItem, String key){
         Map<String, Object> param = new HashMap<>();
         param.put("key", key);
         return sendPost(mediaServerItem, "delStreamProxy",param, null);
     }
 
-    public JSONObject getMediaServerConfig(MediaServerItem mediaServerItem){
+    public JSONObject getMediaServerConfig(MediaServer mediaServerItem){
         return sendPost(mediaServerItem, "getServerConfig",null, null);
     }
 
-    public JSONObject setServerConfig(MediaServerItem mediaServerItem, Map<String, Object> param){
+    public JSONObject setServerConfig(MediaServer mediaServerItem, Map<String, Object> param){
         return sendPost(mediaServerItem,"setServerConfig",param, null);
     }
 
-    public JSONObject openRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param){
+    public JSONObject openRtpServer(MediaServer mediaServerItem, Map<String, Object> param){
         return sendPost(mediaServerItem, "openRtpServer",param, null);
     }
 
-    public JSONObject closeRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param) {
+    public JSONObject closeRtpServer(MediaServer mediaServerItem, Map<String, Object> param) {
         return sendPost(mediaServerItem, "closeRtpServer",param, null);
     }
 
-    public void closeRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param, RequestCallback callback) {
+    public void closeRtpServer(MediaServer mediaServerItem, Map<String, Object> param, RequestCallback callback) {
         sendPost(mediaServerItem, "closeRtpServer",param, callback);
     }
 
-    public JSONObject listRtpServer(MediaServerItem mediaServerItem) {
+    public JSONObject listRtpServer(MediaServer mediaServerItem) {
         return sendPost(mediaServerItem, "listRtpServer",null, null);
     }
 
-    public JSONObject startSendRtp(MediaServerItem mediaServerItem, Map<String, Object> param) {
+    public JSONObject startSendRtp(MediaServer mediaServerItem, Map<String, Object> param) {
         return sendPost(mediaServerItem, "startSendRtp",param, null);
     }
 
-    public JSONObject startSendRtpPassive(MediaServerItem mediaServerItem, Map<String, Object> param) {
+    public JSONObject startSendRtpPassive(MediaServer mediaServerItem, Map<String, Object> param) {
         return sendPost(mediaServerItem, "startSendRtpPassive",param, null);
     }
 
-    public JSONObject startSendRtpPassive(MediaServerItem mediaServerItem, Map<String, Object> param, RequestCallback callback) {
+    public JSONObject startSendRtpPassive(MediaServer mediaServerItem, Map<String, Object> param, RequestCallback callback) {
         return sendPost(mediaServerItem, "startSendRtpPassive",param, callback);
     }
 
-    public JSONObject stopSendRtp(MediaServerItem mediaServerItem, Map<String, Object> param) {
+    public JSONObject stopSendRtp(MediaServer mediaServerItem, Map<String, Object> param) {
         return sendPost(mediaServerItem, "stopSendRtp",param, null);
     }
 
-    public JSONObject restartServer(MediaServerItem mediaServerItem) {
+    public JSONObject restartServer(MediaServer mediaServerItem) {
         return sendPost(mediaServerItem, "restartServer",null, null);
     }
 
-    public JSONObject addStreamProxy(MediaServerItem mediaServerItem, String app, String stream, String url, boolean enable_audio, boolean enable_mp4, String rtp_type) {
+    public JSONObject addStreamProxy(MediaServer mediaServerItem, String app, String stream, String url, boolean enable_audio, boolean enable_mp4, String rtp_type) {
         Map<String, Object> param = new HashMap<>();
         param.put("vhost", "__defaultVhost__");
         param.put("app", app);
@@ -350,7 +348,7 @@ public class ZLMRESTfulUtils {
         return sendPost(mediaServerItem, "addStreamProxy",param, null, 20);
     }
 
-    public JSONObject closeStreams(MediaServerItem mediaServerItem, String app, String stream) {
+    public JSONObject closeStreams(MediaServer mediaServerItem, String app, String stream) {
         Map<String, Object> param = new HashMap<>();
         param.put("vhost", "__defaultVhost__");
         param.put("app", app);
@@ -359,17 +357,17 @@ public class ZLMRESTfulUtils {
         return sendPost(mediaServerItem, "close_streams",param, null);
     }
 
-    public JSONObject getAllSession(MediaServerItem mediaServerItem) {
+    public JSONObject getAllSession(MediaServer mediaServerItem) {
         return sendPost(mediaServerItem, "getAllSession",null, null);
     }
 
-    public void kickSessions(MediaServerItem mediaServerItem, String localPortSStr) {
+    public void kickSessions(MediaServer mediaServerItem, String localPortSStr) {
         Map<String, Object> param = new HashMap<>();
         param.put("local_port", localPortSStr);
         sendPost(mediaServerItem, "kick_sessions",param, null);
     }
 
-    public void getSnap(MediaServerItem mediaServerItem, String streamUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
+    public void getSnap(MediaServer mediaServerItem, String streamUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
         Map<String, Object> param = new HashMap<>(3);
         param.put("url", streamUrl);
         param.put("timeout_sec", timeout_sec);
@@ -377,19 +375,19 @@ public class ZLMRESTfulUtils {
         sendGetForImg(mediaServerItem, "getSnap", param, targetPath, fileName);
     }
 
-    public JSONObject pauseRtpCheck(MediaServerItem mediaServerItem, String streamId) {
+    public JSONObject pauseRtpCheck(MediaServer mediaServerItem, String streamId) {
         Map<String, Object> param = new HashMap<>(1);
         param.put("stream_id", streamId);
         return sendPost(mediaServerItem, "pauseRtpCheck",param, null);
     }
 
-    public JSONObject resumeRtpCheck(MediaServerItem mediaServerItem, String streamId) {
+    public JSONObject resumeRtpCheck(MediaServer mediaServerItem, String streamId) {
         Map<String, Object> param = new HashMap<>(1);
         param.put("stream_id", streamId);
         return sendPost(mediaServerItem, "resumeRtpCheck",param, null);
     }
 
-    public JSONObject connectRtpServer(MediaServerItem mediaServerItem, String dst_url, int dst_port, String stream_id) {
+    public JSONObject connectRtpServer(MediaServer mediaServerItem, String dst_url, int dst_port, String stream_id) {
         Map<String, Object> param = new HashMap<>(1);
         param.put("dst_url", dst_url);
         param.put("dst_port", dst_port);
@@ -397,14 +395,14 @@ public class ZLMRESTfulUtils {
         return sendPost(mediaServerItem, "connectRtpServer",param, null);
     }
 
-    public JSONObject updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) {
+    public JSONObject updateRtpServerSSRC(MediaServer mediaServerItem, String streamId, String ssrc) {
         Map<String, Object> param = new HashMap<>(1);
         param.put("ssrc", ssrc);
         param.put("stream_id", streamId);
         return sendPost(mediaServerItem, "updateRtpServerSSRC",param, null);
     }
 
-    public JSONObject deleteRecordDirectory(MediaServerItem mediaServerItem, String app, String stream, String date, String fileName) {
+    public JSONObject deleteRecordDirectory(MediaServer mediaServerItem, String app, String stream, String date, String fileName) {
         Map<String, Object> param = new HashMap<>(1);
         param.put("vhost", "__defaultVhost__");
         param.put("app", app);

+ 0 - 173
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java

@@ -1,173 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm;
-
-import com.alibaba.fastjson2.JSON;
-import com.alibaba.fastjson2.JSONArray;
-import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.conf.DynamicTask;
-import com.genersoft.iot.vmp.conf.MediaConfig;
-import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForServerStarted;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
-import com.genersoft.iot.vmp.service.IMediaServerService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.CommandLineRunner;
-import org.springframework.core.annotation.Order;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.stereotype.Component;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-@Component
-@Order(value=12)
-public class ZLMRunner implements CommandLineRunner {
-
-    private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class);
-
-    private Map<String, Boolean> startGetMedia;
-
-    @Autowired
-    private ZLMRESTfulUtils zlmresTfulUtils;
-
-    @Autowired
-    private ZlmHttpHookSubscribe hookSubscribe;
-
-    @Autowired
-    private EventPublisher publisher;
-
-    @Autowired
-    private IMediaServerService mediaServerService;
-
-    @Autowired
-    private MediaConfig mediaConfig;
-
-    @Autowired
-    private DynamicTask dynamicTask;
-
-
-    @Override
-    public void run(String... strings) throws Exception {
-        mediaServerService.clearMediaServerForOnline();
-        MediaServerItem defaultMediaServer = mediaServerService.getDefaultMediaServer();
-        if (defaultMediaServer == null) {
-            mediaServerService.addToDatabase(mediaConfig.getMediaSerItem());
-        }else {
-            MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem();
-            mediaServerService.updateToDatabase(mediaSerItem);
-        }
-        mediaServerService.syncCatchFromDatabase();
-        HookSubscribeForServerStarted hookSubscribeForServerStarted = HookSubscribeFactory.on_server_started();
-        // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
-        hookSubscribe.addSubscribe(hookSubscribeForServerStarted,
-                (mediaServerItem, hookParam)->{
-            ZLMServerConfig zlmServerConfig = (ZLMServerConfig)hookParam;
-            if (zlmServerConfig !=null ) {
-                if (startGetMedia != null) {
-                    startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId());
-                    if (startGetMedia.size() == 0) {
-                        hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started());
-                    }
-                }
-            }
-        });
-
-        // 获取zlm信息
-        logger.info("[zlm] 等待默认zlm中...");
-
-        // 获取所有的zlm, 并开启主动连接
-        List<MediaServerItem> all = mediaServerService.getAllFromDatabase();
-        Map<String, MediaServerItem> allMap = new HashMap<>();
-        mediaServerService.updateVmServer(all);
-        if (all.size() == 0) {
-            all.add(mediaConfig.getMediaSerItem());
-        }
-        for (MediaServerItem mediaServerItem : all) {
-            if (startGetMedia == null) {
-                startGetMedia = new ConcurrentHashMap<>();
-            }
-            startGetMedia.put(mediaServerItem.getId(), true);
-            connectZlmServer(mediaServerItem);
-            allMap.put(mediaServerItem.getId(), mediaServerItem);
-        }
-        String taskKey = "zlm-connect-timeout";
-        dynamicTask.startDelay(taskKey, ()->{
-            if (startGetMedia != null && startGetMedia.size() > 0) {
-                Set<String> allZlmId = startGetMedia.keySet();
-                for (String id : allZlmId) {
-                    logger.error("[ {} ]]主动连接失败,不再尝试连接", id);
-                }
-                startGetMedia = null;
-            }
-            // 获取redis中所有的zlm
-            List<MediaServerItem> allInRedis = mediaServerService.getAll();
-            for (MediaServerItem mediaServerItem : allInRedis) {
-                if (!allMap.containsKey(mediaServerItem.getId())) {
-                    mediaServerService.delete(mediaServerItem.getId());
-                }
-            }
-        }, 60 * 1000 );
-    }
-
-    @Async("taskExecutor")
-    public void connectZlmServer(MediaServerItem mediaServerItem){
-        String connectZlmServerTaskKey = "connect-zlm-" + mediaServerItem.getId();
-        ZLMServerConfig zlmServerConfigFirst = getMediaServerConfig(mediaServerItem);
-        if (zlmServerConfigFirst != null) {
-            zlmServerConfigFirst.setIp(mediaServerItem.getIp());
-            zlmServerConfigFirst.setHttpPort(mediaServerItem.getHttpPort());
-            startGetMedia.remove(mediaServerItem.getId());
-            if (startGetMedia.size() == 0) {
-                hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started());
-            }
-            mediaServerService.zlmServerOnline(zlmServerConfigFirst);
-        }else {
-            logger.info("[ {} ]-[ {}:{} ]主动连接失败, 清理相关资源, 开始尝试重试连接",
-                    mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
-            publisher.zlmOfflineEventPublish(mediaServerItem.getId());
-        }
-
-        dynamicTask.startCron(connectZlmServerTaskKey, ()->{
-            ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem);
-            if (zlmServerConfig != null) {
-                dynamicTask.stop(connectZlmServerTaskKey);
-                zlmServerConfig.setIp(mediaServerItem.getIp());
-                zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort());
-                startGetMedia.remove(mediaServerItem.getId());
-                if (startGetMedia.size() == 0) {
-                    hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started());
-                }
-                mediaServerService.zlmServerOnline(zlmServerConfig);
-            }
-        }, 2000);
-    }
-
-    public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem) {
-        if (startGetMedia == null) { return null;}
-        if (!mediaServerItem.isDefaultServer() && mediaServerService.getOne(mediaServerItem.getId()) == null) {
-            return null;
-        }
-        if ( startGetMedia.get(mediaServerItem.getId()) == null || !startGetMedia.get(mediaServerItem.getId())) {
-            return null;
-        }
-        JSONObject responseJson = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
-        ZLMServerConfig zlmServerConfig = null;
-        if (responseJson != null) {
-            JSONArray data = responseJson.getJSONArray("data");
-            if (data != null && data.size() > 0) {
-                zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
-            }
-        } else {
-            logger.error("[ {} ]-[ {}:{} ]主动连接失败, 2s后重试",
-                    mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
-        }
-        return zlmServerConfig;
-
-    }
-}

+ 18 - 17
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java

@@ -5,7 +5,8 @@ import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.common.CommonCallback;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -17,7 +18,7 @@ import java.util.Map;
 @Component
 public class ZLMServerFactory {
 
-    private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory");
+    private Logger logger = LoggerFactory.getLogger("ZLMServerFactory");
 
     @Autowired
     private ZLMRESTfulUtils zlmresTfulUtils;
@@ -26,7 +27,7 @@ public class ZLMServerFactory {
     private UserSetting userSetting;
 
     @Autowired
-    private ZlmHttpHookSubscribe hookSubscribe;
+    private HookSubscribe hookSubscribe;
 
     @Autowired
     private SendRtpPortManager sendRtpPortManager;
@@ -42,7 +43,7 @@ public class ZLMServerFactory {
      * @param tcpMode 0/null udp 模式,1 tcp 被动模式, 2 tcp 主动模式。
      * @return
      */
-    public int createRTPServer(MediaServerItem mediaServerItem, String streamId, long ssrc, Integer port, Boolean onlyAuto, Boolean reUsePort, Integer tcpMode) {
+    public int createRTPServer(MediaServer mediaServerItem, String streamId, long ssrc, Integer port, Boolean onlyAuto, Boolean reUsePort, Integer tcpMode) {
         int result = -1;
         // 查询此rtp server 是否已经存在
         JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId);
@@ -108,7 +109,7 @@ public class ZLMServerFactory {
         return result;
     }
 
-    public boolean closeRtpServer(MediaServerItem serverItem, String streamId) {
+    public boolean closeRtpServer(MediaServer serverItem, String streamId) {
         boolean result = false;
         if (serverItem !=null){
             Map<String, Object> param = new HashMap<>();
@@ -129,7 +130,7 @@ public class ZLMServerFactory {
         return result;
     }
 
-    public void closeRtpServer(MediaServerItem serverItem, String streamId, CommonCallback<Boolean> callback) {
+    public void closeRtpServer(MediaServer serverItem, String streamId, CommonCallback<Boolean> callback) {
         if (serverItem == null) {
             callback.run(false);
             return;
@@ -165,7 +166,7 @@ public class ZLMServerFactory {
      * @param tcp 是否为tcp
      * @return SendRtpItem
      */
-    public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId,
+    public SendRtpItem createSendRtpItem(MediaServer serverItem, String ip, int port, String ssrc, String platformId,
                                          String deviceId, String channelId, boolean tcp, boolean rtcp){
 
         int localPort = sendRtpPortManager.getNextPort(serverItem);
@@ -198,7 +199,7 @@ public class ZLMServerFactory {
      * @param tcp 是否为tcp
      * @return SendRtpItem
      */
-    public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId,
+    public SendRtpItem createSendRtpItem(MediaServer serverItem, String ip, int port, String ssrc, String platformId,
                                          String app, String stream, String channelId, boolean tcp, boolean rtcp){
 
         int localPort = sendRtpPortManager.getNextPort(serverItem);
@@ -224,25 +225,25 @@ public class ZLMServerFactory {
     /**
      * 调用zlm RESTFUL API —— startSendRtp
      */
-    public JSONObject startSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
+    public JSONObject startSendRtpStream(MediaServer mediaServerItem, Map<String, Object>param) {
         return zlmresTfulUtils.startSendRtp(mediaServerItem, param);
     }
 
     /**
      * 调用zlm RESTFUL API —— startSendRtpPassive
      */
-    public JSONObject startSendRtpPassive(MediaServerItem mediaServerItem, Map<String, Object>param) {
+    public JSONObject startSendRtpPassive(MediaServer mediaServerItem, Map<String, Object>param) {
         return zlmresTfulUtils.startSendRtpPassive(mediaServerItem, param);
     }
 
-    public JSONObject startSendRtpPassive(MediaServerItem mediaServerItem, Map<String, Object>param, ZLMRESTfulUtils.RequestCallback callback) {
+    public JSONObject startSendRtpPassive(MediaServer mediaServerItem, Map<String, Object>param, ZLMRESTfulUtils.RequestCallback callback) {
         return zlmresTfulUtils.startSendRtpPassive(mediaServerItem, param, callback);
     }
 
     /**
      * 查询待转推的流是否就绪
      */
-    public Boolean isRtpReady(MediaServerItem mediaServerItem, String streamId) {
+    public Boolean isRtpReady(MediaServer mediaServerItem, String streamId) {
         JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem,"rtp", "rtsp", streamId);
         if (mediaInfo.getInteger("code") == -2) {
             return null;
@@ -253,7 +254,7 @@ public class ZLMServerFactory {
     /**
      * 查询待转推的流是否就绪
      */
-    public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) {
+    public Boolean isStreamReady(MediaServer mediaServerItem, String app, String streamId) {
         JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId);
         if (mediaInfo == null || (mediaInfo.getInteger("code") == -2)) {
             return null;
@@ -268,7 +269,7 @@ public class ZLMServerFactory {
      * @param streamId
      * @return
      */
-    public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) {
+    public int totalReaderCount(MediaServer mediaServerItem, String app, String streamId) {
         JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtsp", streamId);
         if (mediaInfo == null) {
             return 0;
@@ -288,7 +289,7 @@ public class ZLMServerFactory {
     /**
      * 调用zlm RESTful API —— stopSendRtp
      */
-    public Boolean stopSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
+    public Boolean stopSendRtpStream(MediaServer mediaServerItem, Map<String, Object>param) {
         if (mediaServerItem == null) {
             logger.error("[停止RTP推流] 失败: 媒体节点为NULL");
             return false;
@@ -306,7 +307,7 @@ public class ZLMServerFactory {
         return result;
     }
 
-    public JSONObject startSendRtp(MediaServerItem mediaInfo, SendRtpItem sendRtpItem) {
+    public JSONObject startSendRtp(MediaServer mediaInfo, SendRtpItem sendRtpItem) {
         String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
         logger.info("rtp/{}开始推流, 目标={}:{},SSRC={}", sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc());
         Map<String, Object> param = new HashMap<>(12);
@@ -351,7 +352,7 @@ public class ZLMServerFactory {
         return startSendRtpStreamResult;
     }
 
-    public Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) {
+    public Boolean updateRtpServerSSRC(MediaServer mediaServerItem, String streamId, String ssrc) {
         boolean result = false;
         JSONObject jsonObject = zlmresTfulUtils.updateRtpServerSSRC(mediaServerItem, streamId, ssrc);
         if (jsonObject == null) {

+ 0 - 161
src/main/java/com/genersoft/iot/vmp/media/zlm/ZlmHttpHookSubscribe.java

@@ -1,161 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm;
-
-import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.media.zlm.dto.HookType;
-import com.genersoft.iot.vmp.media.zlm.dto.IHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-import org.springframework.util.CollectionUtils;
-
-import java.time.Instant;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-
-/**
- * ZLMediaServer的hook事件订阅
- * @author lin
- */
-@Component
-public class ZlmHttpHookSubscribe {
-
-    private final static Logger logger = LoggerFactory.getLogger(ZlmHttpHookSubscribe.class);
-
-    @FunctionalInterface
-    public interface Event{
-        void response(MediaServerItem mediaServerItem, HookParam hookParam);
-    }
-
-    private Map<HookType, Map<IHookSubscribe, ZlmHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>();
-
-    public void addSubscribe(IHookSubscribe hookSubscribe, ZlmHttpHookSubscribe.Event event) {
-        if (hookSubscribe.getExpires() == null) {
-            // 默认5分钟过期
-            Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(5));
-            hookSubscribe.setExpires(expiresInstant);
-        }
-        allSubscribes.computeIfAbsent(hookSubscribe.getHookType(), k -> new ConcurrentHashMap<>()).put(hookSubscribe, event);
-        System.out.println(allSubscribes);
-    }
-
-    public ZlmHttpHookSubscribe.Event sendNotify(HookType type, JSONObject hookResponse) {
-        ZlmHttpHookSubscribe.Event event= null;
-        Map<IHookSubscribe, Event> eventMap = allSubscribes.get(type);
-        if (eventMap == null) {
-            return null;
-        }
-        for (IHookSubscribe key : eventMap.keySet()) {
-            Boolean result = null;
-
-            for (String s : key.getContent().keySet()) {
-                if (result == null) {
-                    result = key.getContent().getString(s).equals(hookResponse.getString(s));
-                }else {
-                    if (key.getContent().getString(s) == null) {
-                        continue;
-                    }
-                    result = result && key.getContent().getString(s).equals(hookResponse.getString(s));
-                }
-            }
-            if (null != result && result) {
-                event = eventMap.get(key);
-            }
-        }
-        return event;
-    }
-
-    public void removeSubscribe(IHookSubscribe hookSubscribe) {
-        Map<IHookSubscribe, Event> eventMap = allSubscribes.get(hookSubscribe.getHookType());
-        if (eventMap == null) {
-            return;
-        }
-
-        Set<Map.Entry<IHookSubscribe, Event>> entries = eventMap.entrySet();
-        if (entries.size() > 0) {
-            List<Map.Entry<IHookSubscribe, ZlmHttpHookSubscribe.Event>> entriesToRemove = new ArrayList<>();
-            for (Map.Entry<IHookSubscribe, ZlmHttpHookSubscribe.Event> entry : entries) {
-                JSONObject content = entry.getKey().getContent();
-                if (content == null || content.size() == 0) {
-                    entriesToRemove.add(entry);
-                    continue;
-                }
-                Boolean result = null;
-                for (String s : content.keySet()) {
-                    if (result == null) {
-                        result = content.getString(s).equals(hookSubscribe.getContent().getString(s));
-                    }else {
-                        if (content.getString(s) == null) {
-                            continue;
-                        }
-                        result = result && content.getString(s).equals(hookSubscribe.getContent().getString(s));
-                    }
-                }
-                if (result){
-                    entriesToRemove.add(entry);
-                }
-            }
-
-            if (!CollectionUtils.isEmpty(entriesToRemove)) {
-                for (Map.Entry<IHookSubscribe, ZlmHttpHookSubscribe.Event> entry : entriesToRemove) {
-                    eventMap.remove(entry.getKey());
-                }
-                if (eventMap.size() == 0) {
-                    allSubscribes.remove(hookSubscribe.getHookType());
-                }
-            }
-
-        }
-    }
-
-    /**
-     * 获取某个类型的所有的订阅
-     * @param type
-     * @return
-     */
-    public List<ZlmHttpHookSubscribe.Event> getSubscribes(HookType type) {
-        Map<IHookSubscribe, Event> eventMap = allSubscribes.get(type);
-        if (eventMap == null) {
-            return null;
-        }
-        List<ZlmHttpHookSubscribe.Event> result = new ArrayList<>();
-        for (IHookSubscribe key : eventMap.keySet()) {
-            result.add(eventMap.get(key));
-        }
-        return result;
-    }
-
-    public List<IHookSubscribe> getAll(){
-        ArrayList<IHookSubscribe> result = new ArrayList<>();
-        Collection<Map<IHookSubscribe, Event>> values = allSubscribes.values();
-        for (Map<IHookSubscribe, Event> value : values) {
-            result.addAll(value.keySet());
-        }
-        return result;
-    }
-
-    /**
-     * 对订阅数据进行过期清理
-     */
-//    @Scheduled(cron="0 0/5 * * * ?")   //每5分钟执行一次
-    @Scheduled(fixedRate = 2 * 1000)
-    public void execute(){
-        Instant instant = Instant.now().minusMillis(TimeUnit.MINUTES.toMillis(5));
-        int total = 0;
-        for (HookType hookType : allSubscribes.keySet()) {
-            Map<IHookSubscribe, Event> hookSubscribeEventMap = allSubscribes.get(hookType);
-            if (hookSubscribeEventMap.size() > 0) {
-                for (IHookSubscribe hookSubscribe : hookSubscribeEventMap.keySet()) {
-                    if (hookSubscribe.getExpires().isBefore(instant)) {
-                        // 过期的
-                        hookSubscribeEventMap.remove(hookSubscribe);
-                        total ++;
-                    }
-                }
-            }
-        }
-    }
-}

+ 0 - 71
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java

@@ -1,71 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
-
-
-import com.alibaba.fastjson2.JSONObject;
-
-/**
- * hook 订阅工厂
- * @author lin
- */
-public class HookSubscribeFactory {
-
-    public static HookSubscribeForStreamChange on_stream_changed(String app, String stream, boolean regist, String scheam, String mediaServerId) {
-        HookSubscribeForStreamChange hookSubscribe = new HookSubscribeForStreamChange();
-        JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
-        subscribeKey.put("app", app);
-        subscribeKey.put("stream", stream);
-        subscribeKey.put("regist", regist);
-        if (scheam != null) {
-            subscribeKey.put("schema", scheam);
-        }
-        subscribeKey.put("mediaServerId", mediaServerId);
-        hookSubscribe.setContent(subscribeKey);
-
-        return hookSubscribe;
-    }
-
-    public static HookSubscribeForRtpServerTimeout on_rtp_server_timeout(String stream, String ssrc, String mediaServerId) {
-        HookSubscribeForRtpServerTimeout hookSubscribe = new HookSubscribeForRtpServerTimeout();
-        JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
-        subscribeKey.put("stream_id", stream);
-        subscribeKey.put("ssrc", ssrc);
-        subscribeKey.put("mediaServerId", mediaServerId);
-        hookSubscribe.setContent(subscribeKey);
-
-        return hookSubscribe;
-    }
-
-    public static HookSubscribeForStreamPush on_publish(String app, String stream, String scheam, String mediaServerId) {
-        HookSubscribeForStreamPush hookSubscribe = new HookSubscribeForStreamPush();
-        JSONObject subscribeKey = new JSONObject();
-        subscribeKey.put("app", app);
-        subscribeKey.put("stream", stream);
-        if (scheam != null) {
-            subscribeKey.put("schema", scheam);
-        }
-        subscribeKey.put("mediaServerId", mediaServerId);
-        hookSubscribe.setContent(subscribeKey);
-
-        return hookSubscribe;
-    }
-
-
-    public static HookSubscribeForServerStarted on_server_started() {
-        HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted();
-        hookSubscribe.setContent(new JSONObject());
-
-        return hookSubscribe;
-    }
-
-    public static HookSubscribeForRecordMp4 on_record_mp4(String mediaServerId, String app, String stream) {
-        HookSubscribeForRecordMp4 hookSubscribe = new HookSubscribeForRecordMp4();
-        JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
-        subscribeKey.put("app", app);
-        subscribeKey.put("stream", stream);
-        subscribeKey.put("mediaServerId", mediaServerId);
-        hookSubscribe.setContent(subscribeKey);
-
-        return hookSubscribe;
-    }
-
-}

+ 0 - 44
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForRecordMp4.java

@@ -1,44 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
-
-import com.alibaba.fastjson2.JSONObject;
-import com.alibaba.fastjson2.annotation.JSONField;
-
-import java.time.Instant;
-
-/**
- * hook订阅-录像完成
- * @author lin
- */
-public class HookSubscribeForRecordMp4 implements IHookSubscribe{
-
-    private HookType hookType = HookType.on_record_mp4;
-
-    private JSONObject content;
-
-    @JSONField(format="yyyy-MM-dd HH:mm:ss")
-    private Instant expires;
-
-    @Override
-    public HookType getHookType() {
-        return hookType;
-    }
-
-    @Override
-    public JSONObject getContent() {
-        return content;
-    }
-
-    public void setContent(JSONObject content) {
-        this.content = content;
-    }
-
-    @Override
-    public Instant getExpires() {
-        return expires;
-    }
-
-    @Override
-    public void setExpires(Instant expires) {
-        this.expires = expires;
-    }
-}

+ 0 - 44
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForRtpServerTimeout.java

@@ -1,44 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
-
-import com.alibaba.fastjson2.JSONObject;
-import com.alibaba.fastjson2.annotation.JSONField;
-
-import java.time.Instant;
-
-/**
- * hook订阅-收流超时
- * @author lin
- */
-public class HookSubscribeForRtpServerTimeout implements IHookSubscribe{
-
-    private HookType hookType = HookType.on_rtp_server_timeout;
-
-    private JSONObject content;
-
-    @JSONField(format="yyyy-MM-dd HH:mm:ss")
-    private Instant expires;
-
-    @Override
-    public HookType getHookType() {
-        return hookType;
-    }
-
-    @Override
-    public JSONObject getContent() {
-        return content;
-    }
-
-    public void setContent(JSONObject content) {
-        this.content = content;
-    }
-
-    @Override
-    public Instant getExpires() {
-        return expires;
-    }
-
-    @Override
-    public void setExpires(Instant expires) {
-        this.expires = expires;
-    }
-}

+ 0 - 44
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForServerStarted.java

@@ -1,44 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
-
-import com.alibaba.fastjson2.JSONObject;
-import com.alibaba.fastjson2.annotation.JSONField;
-
-import java.time.Instant;
-
-/**
- * hook订阅-流变化
- * @author lin
- */
-public class HookSubscribeForServerStarted implements IHookSubscribe{
-
-    private HookType hookType = HookType.on_server_started;
-
-    private JSONObject content;
-
-    @JSONField(format="yyyy-MM-dd HH:mm:ss")
-    private Instant expires;
-
-    @Override
-    public HookType getHookType() {
-        return hookType;
-    }
-
-    @Override
-    public JSONObject getContent() {
-        return content;
-    }
-
-    public void setContent(JSONObject content) {
-        this.content = content;
-    }
-
-    @Override
-    public Instant getExpires() {
-        return expires;
-    }
-
-    @Override
-    public void setExpires(Instant expires) {
-        this.expires = expires;
-    }
-}

+ 0 - 44
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java

@@ -1,44 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
-
-import com.alibaba.fastjson2.JSONObject;
-import com.alibaba.fastjson2.annotation.JSONField;
-
-import java.time.Instant;
-
-/**
- * hook订阅-流变化
- * @author lin
- */
-public class HookSubscribeForStreamChange implements IHookSubscribe{
-
-    private HookType hookType = HookType.on_stream_changed;
-
-    private JSONObject content;
-
-    @JSONField(format="yyyy-MM-dd HH:mm:ss")
-    private Instant expires;
-
-    @Override
-    public HookType getHookType() {
-        return hookType;
-    }
-
-    @Override
-    public JSONObject getContent() {
-        return content;
-    }
-
-    public void setContent(JSONObject content) {
-        this.content = content;
-    }
-
-    @Override
-    public Instant getExpires() {
-        return expires;
-    }
-
-    @Override
-    public void setExpires(Instant expires) {
-        this.expires = expires;
-    }
-}

+ 0 - 43
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamPush.java

@@ -1,43 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
-
-
-import com.alibaba.fastjson2.JSONObject;
-
-import java.time.Instant;
-
-/**
- * hook订阅-开始推流
- * @author lin
- */
-public class HookSubscribeForStreamPush implements IHookSubscribe{
-
-    private HookType hookType = HookType.on_publish;
-
-    private JSONObject content;
-
-    private Instant expires;
-
-    @Override
-    public HookType getHookType() {
-        return hookType;
-    }
-
-    @Override
-    public JSONObject getContent() {
-        return content;
-    }
-
-    public void setContent(JSONObject content) {
-        this.content = content;
-    }
-
-    @Override
-    public Instant getExpires() {
-        return expires;
-    }
-
-    @Override
-    public void setExpires(Instant expires) {
-        this.expires = expires;
-    }
-}

+ 0 - 26
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java

@@ -1,26 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
-
-/**
- * hook类型
- * @author lin
- */
-
-public enum HookType {
-
-    on_flow_report,
-    on_http_access,
-    on_play,
-    on_publish,
-    on_record_mp4,
-    on_rtsp_auth,
-    on_rtsp_realm,
-    on_shell_login,
-    on_stream_changed,
-    on_stream_none_reader,
-    on_stream_not_found,
-    on_server_started,
-
-    on_rtp_server_timeout,
-    on_server_keepalive,
-    on_send_rtp_stopped
-}

+ 0 - 36
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IHookSubscribe.java

@@ -1,36 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
-
-import com.alibaba.fastjson2.JSONObject;
-
-import java.time.Instant;
-
-/**
- * zlm hook事件的参数
- * @author lin
- */
-public interface IHookSubscribe {
-
-    /**
-     * 获取hook类型
-     * @return hook类型
-     */
-    HookType getHookType();
-
-    /**
-     * 获取hook的具体内容
-     * @return hook的具体内容
-     */
-    JSONObject getContent();
-
-    /**
-     * 设置过期时间
-     * @param instant 过期时间
-     */
-    void setExpires(Instant instant);
-
-    /**
-     * 获取过期时间
-     * @return 过期时间
-     */
-    Instant getExpires();
-}

+ 0 - 168
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItemLite.java

@@ -1,168 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
-
-
-/**
- * 精简的MediaServerItem信息,方便给前端返回数据
- */
-public class MediaServerItemLite {
-
-    private String id;
-
-    private String ip;
-
-    private String hookIp;
-
-    private String sdpIp;
-
-    private String streamIp;
-
-    private int httpPort;
-
-    private int httpSSlPort;
-
-    private int rtmpPort;
-
-    private int rtmpSSlPort;
-
-    private int rtpProxyPort;
-
-    private int rtspPort;
-
-    private int rtspSSLPort;
-
-    private String secret;
-
-    private int recordAssistPort;
-
-
-
-    public MediaServerItemLite(MediaServerItem mediaServerItem) {
-        this.id = mediaServerItem.getId();
-        this.ip = mediaServerItem.getIp();
-        this.hookIp = mediaServerItem.getHookIp();
-        this.sdpIp = mediaServerItem.getSdpIp();
-        this.streamIp = mediaServerItem.getStreamIp();
-        this.httpPort = mediaServerItem.getHttpPort();
-        this.httpSSlPort = mediaServerItem.getHttpSSlPort();
-        this.rtmpPort = mediaServerItem.getRtmpPort();
-        this.rtmpSSlPort = mediaServerItem.getRtmpSSlPort();
-        this.rtpProxyPort = mediaServerItem.getRtpProxyPort();
-        this.rtspPort = mediaServerItem.getRtspPort();
-        this.rtspSSLPort = mediaServerItem.getRtspSSLPort();
-        this.secret = mediaServerItem.getSecret();
-        this.recordAssistPort = mediaServerItem.getRecordAssistPort();
-    }
-
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
-    public String getIp() {
-        return ip;
-    }
-
-    public void setIp(String ip) {
-        this.ip = ip;
-    }
-
-    public String getHookIp() {
-        return hookIp;
-    }
-
-    public void setHookIp(String hookIp) {
-        this.hookIp = hookIp;
-    }
-
-    public String getSdpIp() {
-        return sdpIp;
-    }
-
-    public void setSdpIp(String sdpIp) {
-        this.sdpIp = sdpIp;
-    }
-
-    public String getStreamIp() {
-        return streamIp;
-    }
-
-    public void setStreamIp(String streamIp) {
-        this.streamIp = streamIp;
-    }
-
-    public int getHttpPort() {
-        return httpPort;
-    }
-
-    public void setHttpPort(int httpPort) {
-        this.httpPort = httpPort;
-    }
-
-    public int getHttpSSlPort() {
-        return httpSSlPort;
-    }
-
-    public void setHttpSSlPort(int httpSSlPort) {
-        this.httpSSlPort = httpSSlPort;
-    }
-
-    public int getRtmpPort() {
-        return rtmpPort;
-    }
-
-    public void setRtmpPort(int rtmpPort) {
-        this.rtmpPort = rtmpPort;
-    }
-
-    public int getRtmpSSlPort() {
-        return rtmpSSlPort;
-    }
-
-    public void setRtmpSSlPort(int rtmpSSlPort) {
-        this.rtmpSSlPort = rtmpSSlPort;
-    }
-
-    public int getRtpProxyPort() {
-        return rtpProxyPort;
-    }
-
-    public void setRtpProxyPort(int rtpProxyPort) {
-        this.rtpProxyPort = rtpProxyPort;
-    }
-
-    public int getRtspPort() {
-        return rtspPort;
-    }
-
-    public void setRtspPort(int rtspPort) {
-        this.rtspPort = rtspPort;
-    }
-
-    public int getRtspSSLPort() {
-        return rtspSSLPort;
-    }
-
-    public void setRtspSSLPort(int rtspSSLPort) {
-        this.rtspSSLPort = rtspSSLPort;
-    }
-
-
-    public String getSecret() {
-        return secret;
-    }
-
-    public void setSecret(String secret) {
-        this.secret = secret;
-    }
-
-    public int getRecordAssistPort() {
-        return recordAssistPort;
-    }
-
-    public void setRecordAssistPort(int recordAssistPort) {
-        this.recordAssistPort = recordAssistPort;
-    }
-}

+ 13 - 12
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamAuthorityInfo.java

@@ -1,7 +1,6 @@
 package com.genersoft.iot.vmp.media.zlm.dto;
 
-import com.genersoft.iot.vmp.media.zlm.dto.hook.OnPublishHookParam;
-import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
+import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
 
 /**
  * 流的鉴权信息
@@ -97,21 +96,23 @@ public class StreamAuthorityInfo {
         this.sign = sign;
     }
 
-    public static StreamAuthorityInfo getInstanceByHook(OnPublishHookParam hookParam) {
+    public static StreamAuthorityInfo getInstanceByHook(String app, String stream, String id) {
         StreamAuthorityInfo streamAuthorityInfo = new StreamAuthorityInfo();
-        streamAuthorityInfo.setApp(hookParam.getApp());
-        streamAuthorityInfo.setStream(hookParam.getStream());
-        streamAuthorityInfo.setId(hookParam.getId());
+        streamAuthorityInfo.setApp(app);
+        streamAuthorityInfo.setStream(stream);
+        streamAuthorityInfo.setId(id);
         return streamAuthorityInfo;
     }
 
-    public static StreamAuthorityInfo getInstanceByHook(OnStreamChangedHookParam onStreamChangedHookParam) {
+    public static StreamAuthorityInfo getInstanceByHook(MediaArrivalEvent event) {
         StreamAuthorityInfo streamAuthorityInfo = new StreamAuthorityInfo();
-        streamAuthorityInfo.setApp(onStreamChangedHookParam.getApp());
-        streamAuthorityInfo.setStream(onStreamChangedHookParam.getStream());
-        streamAuthorityInfo.setId(onStreamChangedHookParam.getMediaServerId());
-        streamAuthorityInfo.setOriginType(onStreamChangedHookParam.getOriginType());
-        streamAuthorityInfo.setOriginTypeStr(onStreamChangedHookParam.getOriginTypeStr());
+        streamAuthorityInfo.setApp(event.getApp());
+        streamAuthorityInfo.setStream(event.getStream());
+        streamAuthorityInfo.setId(event.getMediaServer().getId());
+        if (event.getMediaInfo() != null) {
+            streamAuthorityInfo.setOriginType(event.getMediaInfo().getOriginType());
+        }
+
         return streamAuthorityInfo;
     }
 }

+ 43 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java

@@ -1,6 +1,8 @@
 package com.genersoft.iot.vmp.media.zlm.dto;
 
+import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
+import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -150,6 +152,47 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
                 - DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(streamPushItem.getCreateTime())).intValue();
     }
 
+    public StreamPushItem getInstance(StreamInfo streamInfo) {
+        StreamPushItem streamPushItem = new StreamPushItem();
+        streamPushItem.setApp(streamInfo.getApp());
+        streamPushItem.setMediaServerId(streamInfo.getMediaServerId());
+        streamPushItem.setStream(streamInfo.getStream());
+        streamPushItem.setAliveSecond(streamInfo.getMediaInfo().getAliveSecond());
+//        streamPushItem.setOriginSock(streamInfo.getMediaInfo().getOriginSock());
+        streamPushItem.setTotalReaderCount(streamInfo.getMediaInfo().getReaderCount() + "");
+        streamPushItem.setOriginType(streamInfo.getOriginType());
+//        streamPushItem.setOriginTypeStr(streamInfo.getMediaInfo().getOriginTypeStr());
+//        streamPushItem.setOriginUrl(streamInfo.getMediaInfo().getOriginUrl());
+        streamPushItem.setCreateTime(DateUtil.getNow());
+        streamPushItem.setAliveSecond(streamInfo.getMediaInfo().getAliveSecond());
+        streamPushItem.setStatus(true);
+        streamPushItem.setStreamType("push");
+//        streamPushItem.setVhost(streamInfo.getVhost());
+        streamPushItem.setServerId(streamInfo.getMediaServerId());
+        return streamPushItem;
+
+    }
+
+    public static StreamPushItem getInstance(MediaArrivalEvent event, String serverId){
+        StreamPushItem streamPushItem = new StreamPushItem();
+        streamPushItem.setApp(event.getApp());
+        streamPushItem.setMediaServerId(event.getMediaServer().getId());
+        streamPushItem.setStream(event.getStream());
+        streamPushItem.setAliveSecond(event.getMediaInfo().getAliveSecond());
+//        streamPushItem.setOriginSock(streamInfo.getMediaInfo().getOriginSock());
+        streamPushItem.setTotalReaderCount(event.getMediaInfo().getReaderCount() + "");
+        streamPushItem.setOriginType(event.getMediaInfo().getOriginType());
+//        streamPushItem.setOriginTypeStr(streamInfo.getMediaInfo().getOriginTypeStr());
+//        streamPushItem.setOriginUrl(streamInfo.getMediaInfo().getOriginUrl());
+        streamPushItem.setCreateTime(DateUtil.getNow());
+        streamPushItem.setAliveSecond(event.getMediaInfo().getAliveSecond());
+        streamPushItem.setStatus(true);
+        streamPushItem.setStreamType("push");
+//        streamPushItem.setVhost(streamInfo.getVhost());
+        streamPushItem.setServerId(serverId);
+        return streamPushItem;
+    }
+
     public static class MediaSchema {
         private String schema;
         private Long bytesSpeed;

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java

@@ -1,4 +1,4 @@
-package com.genersoft.iot.vmp.media.zlm;
+package com.genersoft.iot.vmp.media.zlm.dto;
 
 import com.alibaba.fastjson2.annotation.JSONField;
 import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;

+ 2 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookParam.java

@@ -7,6 +7,8 @@ package com.genersoft.iot.vmp.media.zlm.dto.hook;
 public class HookParam {
     private String mediaServerId;
 
+
+
     public String getMediaServerId() {
         return mediaServerId;
     }

+ 2 - 2
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResult.java

@@ -18,8 +18,8 @@ public class HookResult {
         return new HookResult(0, "success");
     }
 
-    public static HookResult Fail(){
-        return new HookResult(-1, "fail");
+    public static HookResultForOnPublish Fail(){
+        return new HookResultForOnPublish(-1, "fail");
     }
 
     public int getCode() {

+ 13 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java

@@ -1,5 +1,7 @@
 package com.genersoft.iot.vmp.media.zlm.dto.hook;
 
+import com.genersoft.iot.vmp.media.bean.ResultForOnPublish;
+
 public class HookResultForOnPublish extends HookResult{
 
     private boolean enable_audio;
@@ -16,6 +18,17 @@ public class HookResultForOnPublish extends HookResult{
         return new HookResultForOnPublish(0, "success");
     }
 
+    public static HookResultForOnPublish getInstance(ResultForOnPublish resultForOnPublish){
+        HookResultForOnPublish successResult = new HookResultForOnPublish(0, "success");
+        successResult.setEnable_audio(resultForOnPublish.isEnable_audio());
+        successResult.setEnable_mp4(resultForOnPublish.isEnable_mp4());
+        successResult.setModify_stamp(resultForOnPublish.getModify_stamp());
+        successResult.setStream_replace(resultForOnPublish.getStream_replace());
+        successResult.setMp4_max_second(resultForOnPublish.getMp4_max_second());
+        successResult.setMp4_save_path(resultForOnPublish.getMp4_save_path());
+        return successResult;
+    }
+
     public HookResultForOnPublish(int code, String msg) {
         setCode(code);
         setMsg(msg);

+ 3 - 3
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamChangedHookParam.java

@@ -32,7 +32,7 @@ public class OnStreamChangedHookParam extends HookParam{
     /**
      * 观看总人数,包括hls/rtsp/rtmp/http-flv/ws-flv
      */
-    private String totalReaderCount;
+    private int totalReaderCount;
 
     /**
      * 协议 包括hls/rtsp/rtmp/http-flv/ws-flv
@@ -374,11 +374,11 @@ public class OnStreamChangedHookParam extends HookParam{
         this.stream = stream;
     }
 
-    public String getTotalReaderCount() {
+    public int getTotalReaderCount() {
         return totalReaderCount;
     }
 
-    public void setTotalReaderCount(String totalReaderCount) {
+    public void setTotalReaderCount(int totalReaderCount) {
         this.totalReaderCount = totalReaderCount;
     }
 

+ 24 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/event/HookZlmServerKeepaliveEvent.java

@@ -0,0 +1,24 @@
+package com.genersoft.iot.vmp.media.zlm.event;
+
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * zlm 心跳事件
+ */
+public class HookZlmServerKeepaliveEvent extends ApplicationEvent {
+
+    public HookZlmServerKeepaliveEvent(Object source) {
+        super(source);
+    }
+
+    private MediaServer mediaServerItem;
+
+    public MediaServer getMediaServerItem() {
+        return mediaServerItem;
+    }
+
+    public void setMediaServerItem(MediaServer mediaServerItem) {
+        this.mediaServerItem = mediaServerItem;
+    }
+}

+ 24 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/event/HookZlmServerStartEvent.java

@@ -0,0 +1,24 @@
+package com.genersoft.iot.vmp.media.zlm.event;
+
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * zlm server_start事件
+ */
+public class HookZlmServerStartEvent extends ApplicationEvent {
+
+    public HookZlmServerStartEvent(Object source) {
+        super(source);
+    }
+
+    private MediaServer mediaServerItem;
+
+    public MediaServer getMediaServerItem() {
+        return mediaServerItem;
+    }
+
+    public void setMediaServerItem(MediaServer mediaServerItem) {
+        this.mediaServerItem = mediaServerItem;
+    }
+}

+ 0 - 11
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOfflineEvent.java

@@ -1,11 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm.event;
-
-/**
- * zlm离线事件类
- */
-public class ZLMOfflineEvent extends ZLMEventAbstract {
-
-	public ZLMOfflineEvent(Object source) {
-		super(source);
-	}
-}

+ 0 - 11
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOnlineEvent.java

@@ -1,11 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm.event;
-
-/**
- * zlm在线事件
- */
-public class ZLMOnlineEvent extends ZLMEventAbstract {
-
-	public ZLMOnlineEvent(Object source) {
-		super(source);
-	}
-}

+ 4 - 10
src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java

@@ -1,8 +1,7 @@
 package com.genersoft.iot.vmp.service;
 
 import com.alibaba.fastjson2.JSONArray;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
 import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
 import com.github.pagehelper.PageInfo;
@@ -18,22 +17,17 @@ public interface ICloudRecordService {
     /**
      * 分页回去云端录像列表
      */
-    PageInfo<CloudRecordItem> getList(int page, int count, String query,  String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems);
-
-    /**
-     * 根据hook消息增加一条记录
-     */
-    void addRecord(OnRecordMp4HookParam param);
+    PageInfo<CloudRecordItem> getList(int page, int count, String query,  String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems);
 
     /**
      * 获取所有的日期
      */
-    List<String> getDateList(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems);
+    List<String> getDateList(String app, String stream, int year, int month, List<MediaServer> mediaServerItems);
 
     /**
      * 添加合并任务
      */
-    String addTask(String app, String stream, MediaServerItem mediaServerItem, String startTime,
+    String addTask(String app, String stream, MediaServer mediaServerItem, String startTime,
                    String endTime, String callId, String remoteHost, boolean filterMediaServer);
 
 

+ 0 - 100
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java

@@ -1,100 +0,0 @@
-package com.genersoft.iot.vmp.service;
-
-import com.genersoft.iot.vmp.common.CommonCallback;
-import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData;
-import com.genersoft.iot.vmp.service.bean.MediaServerLoad;
-import com.genersoft.iot.vmp.service.bean.SSRCInfo;
-import com.genersoft.iot.vmp.vmanager.bean.RecordFile;
-
-import java.util.List;
-
-/**
- * 媒体服务节点
- */
-public interface IMediaServerService {
-
-    List<MediaServerItem> getAll();
-
-    List<MediaServerItem> getAllFromDatabase();
-
-    List<MediaServerItem> getAllOnline();
-
-    MediaServerItem getOne(String generalMediaServerId);
-
-    void syncCatchFromDatabase();
-
-    /**
-     * 新的节点加入
-     * @param zlmServerConfig
-     * @return
-     */
-    void zlmServerOnline(ZLMServerConfig zlmServerConfig);
-
-    /**
-     * 节点离线
-     * @param mediaServerId
-     * @return
-     */
-    void zlmServerOffline(String mediaServerId);
-
-    MediaServerItem getMediaServerForMinimumLoad(Boolean hasAssist);
-
-    void setZLMConfig(MediaServerItem mediaServerItem, boolean restart);
-
-    void updateVmServer(List<MediaServerItem>  mediaServerItemList);
-
-    SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck,
-                           boolean isPlayback, Integer port, Boolean onlyAuto, Boolean reUsePort, Integer tcpMode);
-
-    SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback, Integer port, Boolean onlyAuto);
-
-    void closeRTPServer(MediaServerItem mediaServerItem, String streamId);
-
-    void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback<Boolean> callback);
-    Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc);
-
-    void closeRTPServer(String mediaServerId, String streamId);
-
-    void clearRTPServer(MediaServerItem mediaServerItem);
-
-    void update(MediaServerItem mediaSerItem);
-
-    void addCount(String mediaServerId);
-
-    void removeCount(String mediaServerId);
-
-    void releaseSsrc(String mediaServerItemId, String ssrc);
-
-    void clearMediaServerForOnline();
-
-    void add(MediaServerItem mediaSerItem);
-
-    int addToDatabase(MediaServerItem mediaSerItem);
-
-    int updateToDatabase(MediaServerItem mediaSerItem);
-
-    void resetOnlineServerItem(MediaServerItem serverItem);
-
-    MediaServerItem checkMediaServer(String ip, int port, String secret);
-
-    boolean checkMediaRecordServer(String ip, int port);
-
-    void delete(String id);
-
-    void deleteDb(String id);
-
-    MediaServerItem getDefaultMediaServer();
-
-    void updateMediaServerKeepalive(String mediaServerId, ServerKeepaliveData data);
-
-    /**
-     * 获取负载信息
-     * @return
-     */
-    MediaServerLoad getLoad(MediaServerItem mediaServerItem);
-
-    List<MediaServerItem> getAllWithAssistPort();
-
-}

+ 6 - 31
src/main/java/com/genersoft/iot/vmp/service/IMediaService.java

@@ -1,8 +1,7 @@
 package com.genersoft.iot.vmp.service;
 
-import com.alibaba.fastjson2.JSONArray;
-import com.genersoft.iot.vmp.common.StreamInfo;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.ResultForOnPublish;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 
 /**
  * 媒体信息业务
@@ -10,35 +9,11 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 public interface IMediaService {
 
     /**
-     * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在
-     * @param app
-     * @param stream
-     * @return
+     * 播放鉴权
      */
-    StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId,String addr, boolean authority);
+    boolean authenticatePlay(String app, String stream, String callId);
 
+    ResultForOnPublish authenticatePublish(MediaServer mediaServer, String app, String stream, String params);
 
-    /**
-     * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在, 返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况
-     * @param app
-     * @param stream
-     * @return
-     */
-    StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority);
-
-    /**
-     * 根据应用名和流ID获取播放地址, 只是地址拼接
-     * @param app
-     * @param stream
-     * @return
-     */
-    StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, Object tracks, String callId);
-
-    /**
-     * 根据应用名和流ID获取播放地址, 只是地址拼接,返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况
-     * @param app
-     * @param stream
-     * @return
-     */
-    StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr, String callId, boolean isPlay);
+    boolean closeStreamOnNoneReader(String mediaServerId, String app, String stream, String schema);
 }

+ 4 - 4
src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java

@@ -3,8 +3,8 @@ package com.genersoft.iot.vmp.service;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
 import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
 import com.github.pagehelper.PageInfo;
@@ -73,13 +73,13 @@ public interface IPlatformService {
      * @param errorEvent 信令错误事件
      * @param timeoutCallback 超时事件
      */
-    void broadcastInvite(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem,  ZlmHttpHookSubscribe.Event hookEvent,
+    void broadcastInvite(ParentPlatform platform, String channelId, MediaServer mediaServerItem, HookSubscribe.Event hookEvent,
                          SipSubscribe.Event errorEvent, InviteTimeOutCallback timeoutCallback) throws InvalidArgumentException, ParseException, SipException;
 
     /**
      * 语音喊话回复BYE
      */
-    void stopBroadcast(ParentPlatform platform, DeviceChannel channel, String stream,boolean sendBye, MediaServerItem mediaServerItem);
+    void stopBroadcast(ParentPlatform platform, DeviceChannel channel, String stream,boolean sendBye, MediaServer mediaServerItem);
 
     void addSimulatedSubscribeInfo(ParentPlatform parentPlatform);
 }

+ 11 - 12
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java

@@ -7,8 +7,8 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
+import com.genersoft.iot.vmp.media.bean.MediaInfo;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.service.bean.ErrorCallback;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
@@ -26,20 +26,20 @@ import java.util.Map;
  */
 public interface IPlayService {
 
-    void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channelId,
+    void play(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channelId,
               ErrorCallback<Object> callback);
-    SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback<Object> callback);
+    SSRCInfo play(MediaServer mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback<Object> callback);
 
-    StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, HookParam hookParam, String deviceId, String channelId);
+    StreamInfo onPublishHandlerForPlay(MediaServer mediaServerItem, MediaInfo mediaInfo, String deviceId, String channelId);
 
-    MediaServerItem getNewMediaServerItem(Device device);
+    MediaServer getNewMediaServerItem(Device device);
 
     void playBack(String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
-    void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
+    void playBack(MediaServer mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
     void zlmServerOffline(String mediaServerId);
 
     void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback);
-    void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,String deviceId,  String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback);
+    void download(MediaServer mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback);
 
     StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream);
 
@@ -47,7 +47,7 @@ public interface IPlayService {
 
     AudioBroadcastResult audioBroadcast(Device device, String channelId, Boolean broadcastMode);
 
-    boolean audioBroadcastCmd(Device device, String channelId, MediaServerItem mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException;
+    boolean audioBroadcastCmd(Device device, String channelId, MediaServer mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException;
 
     boolean audioBroadcastInUse(Device device, String channelId);
 
@@ -59,10 +59,9 @@ public interface IPlayService {
 
     void startPushStream(SendRtpItem sendRtpItem, SIPResponse sipResponse, ParentPlatform platform, CallIdHeader callIdHeader);
 
-    void startSendRtpStreamHand(SendRtpItem sendRtpItem, Object correlationInfo,
-                                JSONObject jsonObject, Map<String, Object> param, CallIdHeader callIdHeader);
+    void startSendRtpStreamFailHand(SendRtpItem sendRtpItem,ParentPlatform platform, CallIdHeader callIdHeader);
 
-    void talkCmd(Device device, String channelId, MediaServerItem mediaServerItem, String stream, AudioBroadcastEvent event);
+    void talkCmd(Device device, String channelId, MediaServer mediaServerItem, String stream, AudioBroadcastEvent event);
 
     void stopTalk(Device device, String channelId, Boolean streamIsReady);
 

+ 10 - 5
src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java

@@ -1,13 +1,15 @@
 package com.genersoft.iot.vmp.service;
 
-import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.common.GeneralCallback;
 import com.genersoft.iot.vmp.common.StreamInfo;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
 import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import com.github.pagehelper.PageInfo;
 
+import java.util.Map;
+
 public interface IStreamProxyService {
 
     /**
@@ -18,17 +20,19 @@ public interface IStreamProxyService {
 
     /**
      * 添加视频代理到zlm
+     *
      * @param param
      * @return
      */
-    JSONObject addStreamProxyToZlm(StreamProxyItem param);
+    WVPResult<String> addStreamProxyToZlm(StreamProxyItem param);
 
     /**
      * 从zlm移除视频代理
+     *
      * @param param
      * @return
      */
-    JSONObject removeStreamProxyFromZlm(StreamProxyItem param);
+    Boolean removeStreamProxyFromZlm(StreamProxyItem param);
 
     /**
      * 分页查询
@@ -73,9 +77,10 @@ public interface IStreamProxyService {
 
     /**
      * 获取ffmpeg.cmd模板
+     *
      * @return
      */
-    JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem);
+    Map<String, String> getFFmpegCMDs(MediaServer mediaServerItem);
 
     /**
      * 根据app与stream获取streamProxy

+ 1 - 4
src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java

@@ -1,9 +1,8 @@
 package com.genersoft.iot.vmp.service;
 
 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
-import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
 import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis;
 import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo;
 import com.github.pagehelper.PageInfo;
@@ -16,8 +15,6 @@ import java.util.Map;
  */
 public interface IStreamPushService {
 
-    List<StreamPushItem> handleJSON(String json, MediaServerItem mediaServerItem);
-
     /**
      * 将应用名和流ID加入国标关联
      * @param stream

+ 10 - 9
src/main/java/com/genersoft/iot/vmp/service/bean/CloudRecordItem.java

@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp.service.bean;
 
+import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event;
 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
 
 /**
@@ -76,18 +77,18 @@ public class CloudRecordItem {
      */
     private long timeLen;
 
-    public static CloudRecordItem getInstance(OnRecordMp4HookParam param) {
+    public static CloudRecordItem getInstance(MediaRecordMp4Event param) {
         CloudRecordItem cloudRecordItem = new CloudRecordItem();
         cloudRecordItem.setApp(param.getApp());
         cloudRecordItem.setStream(param.getStream());
-        cloudRecordItem.setStartTime(param.getStart_time()*1000);
-        cloudRecordItem.setFileName(param.getFile_name());
-        cloudRecordItem.setFolder(param.getFolder());
-        cloudRecordItem.setFileSize(param.getFile_size());
-        cloudRecordItem.setFilePath(param.getFile_path());
-        cloudRecordItem.setMediaServerId(param.getMediaServerId());
-        cloudRecordItem.setTimeLen((long) param.getTime_len() * 1000);
-        cloudRecordItem.setEndTime((param.getStart_time() + (long)param.getTime_len()) * 1000);
+        cloudRecordItem.setStartTime(param.getRecordInfo().getStartTime()*1000);
+        cloudRecordItem.setFileName(param.getRecordInfo().getFileName());
+        cloudRecordItem.setFolder(param.getRecordInfo().getFolder());
+        cloudRecordItem.setFileSize(param.getRecordInfo().getFileSize());
+        cloudRecordItem.setFilePath(param.getRecordInfo().getFilePath());
+        cloudRecordItem.setMediaServerId(param.getMediaServer().getId());
+        cloudRecordItem.setTimeLen((long) param.getRecordInfo().getTimeLen() * 1000);
+        cloudRecordItem.setEndTime((param.getRecordInfo().getStartTime() + (long)param.getRecordInfo().getTimeLen()) * 1000);
         return cloudRecordItem;
     }
 

+ 4 - 4
src/main/java/com/genersoft/iot/vmp/service/bean/PlayBackResult.java

@@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.service.bean;
 
 import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 
 import java.util.EventObject;
 
@@ -15,7 +15,7 @@ public class PlayBackResult<T> {
 
     private String msg;
     private T data;
-    private MediaServerItem mediaServerItem;
+    private MediaServer mediaServerItem;
     private JSONObject response;
     private SipSubscribe.EventResult<EventObject> event;
 
@@ -35,11 +35,11 @@ public class PlayBackResult<T> {
         this.data = data;
     }
 
-    public MediaServerItem getMediaServerItem() {
+    public MediaServer getMediaServerItem() {
         return mediaServerItem;
     }
 
-    public void setMediaServerItem(MediaServerItem mediaServerItem) {
+    public void setMediaServerItem(MediaServer mediaServerItem) {
         this.mediaServerItem = mediaServerItem;
     }
 

+ 4 - 4
src/main/java/com/genersoft/iot/vmp/service/bean/ResponseSendItemMsg.java

@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.service.bean;
 
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 
 /**
  * redis消息:下级回复推送信息
@@ -11,7 +11,7 @@ public class ResponseSendItemMsg {
 
     private SendRtpItem sendRtpItem;
 
-    private MediaServerItem mediaServerItem;
+    private MediaServer mediaServerItem;
 
     public SendRtpItem getSendRtpItem() {
         return sendRtpItem;
@@ -21,11 +21,11 @@ public class ResponseSendItemMsg {
         this.sendRtpItem = sendRtpItem;
     }
 
-    public MediaServerItem getMediaServerItem() {
+    public MediaServer getMediaServerItem() {
         return mediaServerItem;
     }
 
-    public void setMediaServerItem(MediaServerItem mediaServerItem) {
+    public void setMediaServerItem(MediaServer mediaServerItem) {
         this.mediaServerItem = mediaServerItem;
     }
 }

+ 19 - 16
src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java

@@ -5,12 +5,12 @@ import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
+import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event;
 import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
-import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
 import com.genersoft.iot.vmp.service.ICloudRecordService;
-import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
 import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -24,6 +24,8 @@ import org.apache.commons.lang3.ObjectUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
 import java.time.*;
@@ -51,7 +53,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
     private VideoStreamSessionManager streamSession;
 
     @Override
-    public PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems) {
+    public PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems) {
         // 开始时间和结束时间在数据库中都是以秒为单位的
         Long startTimeStamp = null;
         Long endTimeStamp = null;
@@ -76,7 +78,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
     }
 
     @Override
-    public List<String> getDateList(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems) {
+    public List<String> getDateList(String app, String stream, int year, int month, List<MediaServer> mediaServerItems) {
         LocalDate startDate = LocalDate.of(year, month, 1);
         LocalDate endDate;
         if (month == 12) {
@@ -99,19 +101,20 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
         return new ArrayList<>(resultSet);
     }
 
-    @Override
-    public void addRecord(OnRecordMp4HookParam param) {
-        CloudRecordItem cloudRecordItem = CloudRecordItem.getInstance(param);
-        StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaRecordMp4Event event) {
+        CloudRecordItem cloudRecordItem = CloudRecordItem.getInstance(event);
+        StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(event.getApp(), event.getStream());
         if (streamAuthorityInfo != null) {
             cloudRecordItem.setCallId(streamAuthorityInfo.getCallId());
         }
-        logger.info("[添加录像记录] {}/{} 文件大小:{}, 时长: {}秒", param.getApp(), param.getStream(), param.getFile_size(),param.getTime_len());
+        logger.info("[添加录像记录] {}/{} 内容:{}", event.getApp(), event.getStream(), event.getRecordInfo());
         cloudRecordServiceMapper.add(cloudRecordItem);
     }
 
     @Override
-    public String addTask(String app, String stream, MediaServerItem mediaServerItem, String startTime, String endTime,
+    public String addTask(String app, String stream, MediaServer mediaServerItem, String startTime, String endTime,
                           String callId, String remoteHost, boolean filterMediaServer) {
         // 参数校验
         assert app != null;
@@ -128,7 +131,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
             endTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
         }
 
-        List<MediaServerItem> mediaServers = new ArrayList<>();
+        List<MediaServer> mediaServers = new ArrayList<>();
         mediaServers.add(mediaServerItem);
         // 检索相关的录像文件
         List<String> filePathList = cloudRecordServiceMapper.queryRecordFilePathList(app, stream, startTimeStamp,
@@ -146,7 +149,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
     @Override
     public JSONArray queryTask(String app, String stream, String callId, String taskId, String mediaServerId,
                                Boolean isEnd, String scheme) {
-        MediaServerItem mediaServerItem = null;
+        MediaServer mediaServerItem = null;
         if (mediaServerId == null) {
             mediaServerItem = mediaServerService.getDefaultMediaServer();
         }else {
@@ -183,10 +186,10 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
 
         }
 
-        List<MediaServerItem> mediaServerItems;
+        List<MediaServer> mediaServerItems;
         if (!ObjectUtils.isEmpty(mediaServerId)) {
             mediaServerItems = new ArrayList<>();
-            MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
+            MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId);
             if (mediaServerItem == null) {
                 throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到流媒体: " + mediaServerId);
             }
@@ -229,7 +232,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
             throw new ControllerException(ErrorCode.ERROR400.getCode(), "资源不存在");
         }
         String filePath = recordItem.getFilePath();
-        MediaServerItem mediaServerItem = mediaServerService.getOne(recordItem.getMediaServerId());
+        MediaServer mediaServerItem = mediaServerService.getOne(recordItem.getMediaServerId());
         return CloudRecordUtils.getDownloadFilePath(mediaServerItem, filePath);
     }
 }

+ 5 - 12
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java

@@ -14,12 +14,11 @@ import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler;
-import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.service.IDeviceChannelService;
 import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.service.IInviteStreamService;
-import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
 import com.genersoft.iot.vmp.storager.dao.DeviceMapper;
@@ -54,6 +53,7 @@ public class DeviceServiceImpl implements IDeviceService {
 
     @Autowired
     private SIPCommander cmder;
+
     @Autowired
     private DynamicTask dynamicTask;
 
@@ -102,9 +102,6 @@ public class DeviceServiceImpl implements IDeviceService {
     @Autowired
     private AudioBroadcastManager audioBroadcastManager;
 
-    @Autowired
-    private ZLMRESTfulUtils zlmresTfulUtils;
-
     @Override
     public void online(Device device, SipTransactionInfo sipTransactionInfo) {
         logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort());
@@ -244,12 +241,8 @@ public class DeviceServiceImpl implements IDeviceService {
                 SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(deviceId, audioBroadcastCatch.getChannelId(), null, null);
                 if (sendRtpItem != null) {
                     redisCatchStorage.deleteSendRTPServer(deviceId, sendRtpItem.getChannelId(), null, null);
-                    MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
-                    Map<String, Object> param = new HashMap<>();
-                    param.put("vhost", "__defaultVhost__");
-                    param.put("app", sendRtpItem.getApp());
-                    param.put("stream", sendRtpItem.getStream());
-                    zlmresTfulUtils.stopSendRtp(mediaInfo, param);
+                    MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+                    mediaServerService.stopSendRtp(mediaInfo, sendRtpItem.getApp(), sendRtpItem.getStream(), null);
                 }
 
                 audioBroadcastManager.del(deviceId, audioBroadcastCatch.getChannelId());

+ 34 - 0
src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java

@@ -6,13 +6,18 @@ import com.genersoft.iot.vmp.common.InviteInfo;
 import com.genersoft.iot.vmp.common.InviteSessionStatus;
 import com.genersoft.iot.vmp.common.InviteSessionType;
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
+import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
 import com.genersoft.iot.vmp.service.IInviteStreamService;
 import com.genersoft.iot.vmp.service.bean.ErrorCallback;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
@@ -31,6 +36,35 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
     @Autowired
     private RedisTemplate<Object, Object> redisTemplate;
 
+    @Autowired
+    private IVideoManagerStorage storage;
+
+    /**
+     * 流到来的处理
+     */
+    @Async("taskExecutor")
+    @org.springframework.context.event.EventListener
+    public void onApplicationEvent(MediaArrivalEvent event) {
+//        if ("rtsp".equals(event.getSchema()) && "rtp".equals(event.getApp())) {
+//
+//        }
+    }
+
+    /**
+     * 流离开的处理
+     */
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaDepartureEvent event) {
+        if ("rtsp".equals(event.getSchema()) && "rtp".equals(event.getApp())) {
+            InviteInfo inviteInfo = getInviteInfoByStream(null, event.getStream());
+            if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) {
+                removeInviteInfo(inviteInfo);
+                storage.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
+            }
+        }
+    }
+
     @Override
     public void updateInviteInfo(InviteInfo inviteInfo) {
         if (inviteInfo == null || (inviteInfo.getDeviceId() == null || inviteInfo.getChannelId() == null)) {

+ 0 - 756
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java

@@ -1,756 +0,0 @@
-package com.genersoft.iot.vmp.service.impl;
-
-import com.alibaba.fastjson2.JSON;
-import com.alibaba.fastjson2.JSONArray;
-import com.alibaba.fastjson2.JSONObject;
-import com.baomidou.dynamic.datasource.annotation.DS;
-import com.genersoft.iot.vmp.common.CommonCallback;
-import com.genersoft.iot.vmp.common.VideoManagerConstants;
-import com.genersoft.iot.vmp.conf.DynamicTask;
-import com.genersoft.iot.vmp.conf.SipConfig;
-import com.genersoft.iot.vmp.conf.UserSetting;
-import com.genersoft.iot.vmp.conf.exception.ControllerException;
-import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
-import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
-import com.genersoft.iot.vmp.media.zlm.*;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData;
-import com.genersoft.iot.vmp.service.IInviteStreamService;
-import com.genersoft.iot.vmp.service.IMediaServerService;
-import com.genersoft.iot.vmp.service.bean.MediaServerLoad;
-import com.genersoft.iot.vmp.service.bean.SSRCInfo;
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
-import com.genersoft.iot.vmp.utils.DateUtil;
-import com.genersoft.iot.vmp.utils.JsonUtil;
-import com.genersoft.iot.vmp.utils.redis.RedisUtil;
-import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
-import com.genersoft.iot.vmp.vmanager.bean.RecordFile;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.Response;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.jdbc.datasource.DataSourceTransactionManager;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.TransactionDefinition;
-import org.springframework.transaction.TransactionStatus;
-import org.springframework.util.Assert;
-import org.springframework.util.ObjectUtils;
-
-import java.io.File;
-import java.time.LocalDateTime;
-import java.util.*;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-
-/**
- * 媒体服务器节点管理
- */
-@Service
-@DS("master")
-public class MediaServerServiceImpl implements IMediaServerService {
-
-    private final static Logger logger = LoggerFactory.getLogger(MediaServerServiceImpl.class);
-
-    private final String zlmKeepaliveKeyPrefix = "zlm-keepalive_";
-
-    @Autowired
-    private SipConfig sipConfig;
-
-    @Autowired
-    private SSRCFactory ssrcFactory;
-
-    @Value("${server.ssl.enabled:false}")
-    private boolean sslEnabled;
-
-    @Value("${server.port}")
-    private Integer serverPort;
-
-    @Autowired
-    private UserSetting userSetting;
-
-    @Autowired
-    private SendRtpPortManager sendRtpPortManager;
-
-    @Autowired
-    private AssistRESTfulUtils assistRESTfulUtils;
-
-    @Autowired
-    private ZLMRESTfulUtils zlmresTfulUtils;
-
-    @Autowired
-    private MediaServerMapper mediaServerMapper;
-
-    @Autowired
-    private DataSourceTransactionManager dataSourceTransactionManager;
-
-    @Autowired
-    private TransactionDefinition transactionDefinition;
-
-
-    @Autowired
-    private ZLMServerFactory zlmServerFactory;
-
-    @Autowired
-    private EventPublisher publisher;
-
-    @Autowired
-    private DynamicTask dynamicTask;
-
-    @Autowired
-    private IRedisCatchStorage redisCatchStorage;
-
-    @Autowired
-    private IInviteStreamService inviteStreamService;
-
-    @Autowired
-    private RedisTemplate<Object, Object> redisTemplate;
-
-    @Qualifier("taskExecutor")
-    @Autowired
-    private ThreadPoolTaskExecutor taskExecutor;
-
-
-
-
-
-    /**
-     * 初始化
-     */
-    @Override
-    public void updateVmServer(List<MediaServerItem>  mediaServerItemList) {
-        logger.info("[zlm] 缓存初始化 ");
-        for (MediaServerItem mediaServerItem : mediaServerItemList) {
-            if (ObjectUtils.isEmpty(mediaServerItem.getId())) {
-                continue;
-            }
-            // 更新
-            if (!ssrcFactory.hasMediaServerSSRC(mediaServerItem.getId())) {
-                ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null);
-            }
-            // 查询redis是否存在此mediaServer
-            String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId();
-            Boolean hasKey = redisTemplate.hasKey(key);
-            if (hasKey != null && ! hasKey) {
-                redisTemplate.opsForValue().set(key, mediaServerItem);
-            }
-        }
-    }
-
-
-    @Override
-    public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck,
-                                  boolean isPlayback, Integer port, Boolean onlyAuto, Boolean reUsePort, Integer tcpMode) {
-        if (mediaServerItem == null || mediaServerItem.getId() == null) {
-            logger.info("[openRTPServer] 失败, mediaServerItem == null || mediaServerItem.getId() == null");
-            return null;
-        }
-        // 获取mediaServer可用的ssrc
-        String ssrc;
-        if (presetSsrc != null) {
-            ssrc = presetSsrc;
-        }else {
-            if (isPlayback) {
-                ssrc = ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
-            }else {
-                ssrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId());
-            }
-        }
-
-        if (streamId == null) {
-            streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
-        }
-        if (ssrcCheck && tcpMode > 0) {
-            // 目前zlm不支持 tcp模式更新ssrc,暂时关闭ssrc校验
-            logger.warn("[openRTPServer] 平台对接时下级可能自定义ssrc,但是tcp模式zlm收流目前无法更新ssrc,可能收流超时,此时请使用udp收流或者关闭ssrc校验");
-        }
-        int rtpServerPort;
-        if (mediaServerItem.isRtpEnable()) {
-            rtpServerPort = zlmServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck ? Long.parseLong(ssrc) : 0, port, onlyAuto, reUsePort, tcpMode);
-        } else {
-            rtpServerPort = mediaServerItem.getRtpProxyPort();
-        }
-        return new SSRCInfo(rtpServerPort, ssrc, streamId);
-    }
-
-    @Override
-    public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback, Integer port, Boolean onlyAuto) {
-        return openRTPServer(mediaServerItem, streamId, ssrc, ssrcCheck, isPlayback, port, onlyAuto, null, 0);
-    }
-
-
-    @Override
-    public void closeRTPServer(MediaServerItem mediaServerItem, String streamId) {
-        if (mediaServerItem == null) {
-            return;
-        }
-        zlmServerFactory.closeRtpServer(mediaServerItem, streamId);
-    }
-
-    @Override
-    public void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback<Boolean> callback) {
-        if (mediaServerItem == null) {
-            callback.run(false);
-            return;
-        }
-        zlmServerFactory.closeRtpServer(mediaServerItem, streamId, callback);
-    }
-
-    @Override
-    public void closeRTPServer(String mediaServerId, String streamId) {
-        MediaServerItem mediaServerItem = this.getOne(mediaServerId);
-        if (mediaServerItem != null && mediaServerItem.isRtpEnable()) {
-            closeRTPServer(mediaServerItem, streamId);
-        }
-        zlmresTfulUtils.closeStreams(mediaServerItem, "rtp", streamId);
-    }
-
-    @Override
-    public Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) {
-        return zlmServerFactory.updateRtpServerSSRC(mediaServerItem, streamId, ssrc);
-    }
-
-    @Override
-    public void releaseSsrc(String mediaServerItemId, String ssrc) {
-        MediaServerItem mediaServerItem = getOne(mediaServerItemId);
-        if (mediaServerItem == null || ssrc == null) {
-            return;
-        }
-        ssrcFactory.releaseSsrc(mediaServerItemId, ssrc);
-    }
-
-    /**
-     * zlm 重启后重置他的推流信息, TODO 给正在使用的设备发送停止命令
-     */
-    @Override
-    public void clearRTPServer(MediaServerItem mediaServerItem) {
-        ssrcFactory.reset(mediaServerItem.getId());
-
-    }
-
-
-    @Override
-    public void update(MediaServerItem mediaSerItem) {
-        mediaServerMapper.update(mediaSerItem);
-        MediaServerItem mediaServerItemInRedis = getOne(mediaSerItem.getId());
-        MediaServerItem mediaServerItemInDataBase = mediaServerMapper.queryOne(mediaSerItem.getId());
-        if (mediaServerItemInRedis == null || !ssrcFactory.hasMediaServerSSRC(mediaSerItem.getId())) {
-            ssrcFactory.initMediaServerSSRC(mediaServerItemInDataBase.getId(),null);
-        }
-        String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItemInDataBase.getId();
-        redisTemplate.opsForValue().set(key, mediaServerItemInDataBase);
-    }
-
-    @Override
-    public List<MediaServerItem> getAll() {
-        List<MediaServerItem> result = new ArrayList<>();
-        List<Object> mediaServerKeys = RedisUtil.scan(redisTemplate, String.format("%S*", VideoManagerConstants.MEDIA_SERVER_PREFIX+ userSetting.getServerId() + "_" ));
-        String onlineKey = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
-        for (Object mediaServerKey : mediaServerKeys) {
-            String key = (String) mediaServerKey;
-            MediaServerItem mediaServerItem = JsonUtil.redisJsonToObject(redisTemplate, key, MediaServerItem.class);
-            if (Objects.isNull(mediaServerItem)) {
-                continue;
-            }
-            // 检查状态
-            Double aDouble = redisTemplate.opsForZSet().score(onlineKey, mediaServerItem.getId());
-            if (aDouble != null) {
-                mediaServerItem.setStatus(true);
-            }
-            result.add(mediaServerItem);
-        }
-        result.sort((serverItem1, serverItem2)->{
-            int sortResult = 0;
-            LocalDateTime localDateTime1 = LocalDateTime.parse(serverItem1.getCreateTime(), DateUtil.formatter);
-            LocalDateTime localDateTime2 = LocalDateTime.parse(serverItem2.getCreateTime(), DateUtil.formatter);
-
-            sortResult = localDateTime1.compareTo(localDateTime2);
-            return  sortResult;
-        });
-        return result;
-    }
-
-
-    @Override
-    public List<MediaServerItem> getAllFromDatabase() {
-        return mediaServerMapper.queryAll();
-    }
-
-    @Override
-    public List<MediaServerItem> getAllOnline() {
-        String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
-        Set<Object> mediaServerIdSet = redisTemplate.opsForZSet().reverseRange(key, 0, -1);
-
-        List<MediaServerItem> result = new ArrayList<>();
-        if (mediaServerIdSet != null && mediaServerIdSet.size() > 0) {
-            for (Object mediaServerId : mediaServerIdSet) {
-                String mediaServerIdStr = (String) mediaServerId;
-                String serverKey = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerIdStr;
-                result.add((MediaServerItem) redisTemplate.opsForValue().get(serverKey));
-            }
-        }
-        Collections.reverse(result);
-        return result;
-    }
-
-    /**
-     * 获取单个zlm服务器
-     * @param mediaServerId 服务id
-     * @return MediaServerItem
-     */
-    @Override
-    public MediaServerItem getOne(String mediaServerId) {
-        if (mediaServerId == null) {
-            return null;
-        }
-        String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerId;
-        return JsonUtil.redisJsonToObject(redisTemplate, key, MediaServerItem.class);
-    }
-
-
-    @Override
-    public MediaServerItem getDefaultMediaServer() {
-        return mediaServerMapper.queryDefault();
-    }
-
-    @Override
-    public void clearMediaServerForOnline() {
-        String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
-        redisTemplate.delete(key);
-    }
-
-    @Override
-    public void add(MediaServerItem mediaServerItem) {
-        mediaServerItem.setCreateTime(DateUtil.getNow());
-        mediaServerItem.setUpdateTime(DateUtil.getNow());
-        mediaServerItem.setHookAliveInterval(30f);
-        JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
-        if (responseJSON != null) {
-            JSONArray data = responseJSON.getJSONArray("data");
-            if (data != null && data.size() > 0) {
-                ZLMServerConfig zlmServerConfig= JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
-                if (mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()) != null) {
-                    throw new ControllerException(ErrorCode.ERROR100.getCode(),"保存失败,媒体服务ID [ " + zlmServerConfig.getGeneralMediaServerId() + " ] 已存在,请修改媒体服务器配置");
-                }
-                mediaServerItem.setId(zlmServerConfig.getGeneralMediaServerId());
-                zlmServerConfig.setIp(mediaServerItem.getIp());
-                mediaServerMapper.add(mediaServerItem);
-                zlmServerOnline(zlmServerConfig);
-            }else {
-                throw new ControllerException(ErrorCode.ERROR100.getCode(),"连接失败");
-            }
-
-        }else {
-            throw new ControllerException(ErrorCode.ERROR100.getCode(),"连接失败");
-        }
-    }
-
-    @Override
-    public int addToDatabase(MediaServerItem mediaSerItem) {
-        return mediaServerMapper.add(mediaSerItem);
-    }
-
-    @Override
-    public int updateToDatabase(MediaServerItem mediaSerItem) {
-        int result = 0;
-        if (mediaSerItem.isDefaultServer()) {
-            TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
-            int delResult = mediaServerMapper.delDefault();
-            if (delResult == 0) {
-                logger.error("移除数据库默认zlm节点失败");
-                //事务回滚
-                dataSourceTransactionManager.rollback(transactionStatus);
-                return 0;
-            }
-            result = mediaServerMapper.add(mediaSerItem);
-            dataSourceTransactionManager.commit(transactionStatus);     //手动提交
-        }else {
-            result = mediaServerMapper.update(mediaSerItem);
-        }
-        return result;
-    }
-
-    /**
-     * 处理zlm上线
-     * @param zlmServerConfig zlm上线携带的参数
-     */
-    @Override
-    public void zlmServerOnline(ZLMServerConfig zlmServerConfig) {
-
-        MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId());
-        if (serverItem == null) {
-            logger.warn("[未注册的zlm] 拒接接入:{}来自{}:{}", zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() );
-            logger.warn("请检查ZLM的<general.mediaServerId>配置是否与WVP的<media.id>一致");
-            return;
-        }else {
-            logger.info("[ZLM] 正在连接 : {} -> {}:{}",
-                    zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
-        }
-        serverItem.setHookAliveInterval(zlmServerConfig.getHookAliveInterval());
-        if (serverItem.getHttpPort() == 0) {
-            serverItem.setHttpPort(zlmServerConfig.getHttpPort());
-        }
-        if (serverItem.getHttpSSlPort() == 0) {
-            serverItem.setHttpSSlPort(zlmServerConfig.getHttpSSLport());
-        }
-        if (serverItem.getRtmpPort() == 0) {
-            serverItem.setRtmpPort(zlmServerConfig.getRtmpPort());
-        }
-        if (serverItem.getRtmpSSlPort() == 0) {
-            serverItem.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
-        }
-        if (serverItem.getRtspPort() == 0) {
-            serverItem.setRtspPort(zlmServerConfig.getRtspPort());
-        }
-        if (serverItem.getRtspSSLPort() == 0) {
-            serverItem.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
-        }
-        if (serverItem.getRtpProxyPort() == 0) {
-            serverItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
-        }
-        serverItem.setStatus(true);
-
-        if (ObjectUtils.isEmpty(serverItem.getId())) {
-            logger.warn("[未注册的zlm] serverItem缺少ID, 无法接入:{}:{}", zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() );
-            return;
-        }
-        mediaServerMapper.update(serverItem);
-        String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + zlmServerConfig.getGeneralMediaServerId();
-        if (!ssrcFactory.hasMediaServerSSRC(serverItem.getId())) {
-            ssrcFactory.initMediaServerSSRC(zlmServerConfig.getGeneralMediaServerId(), null);
-        }
-        redisTemplate.opsForValue().set(key, serverItem);
-        resetOnlineServerItem(serverItem);
-
-
-        if (serverItem.isAutoConfig()) {
-            setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable()));
-        }
-        final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId();
-        dynamicTask.stop(zlmKeepaliveKey);
-        dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (serverItem.getHookAliveInterval().intValue() + 5) * 1000);
-        publisher.zlmOnlineEventPublish(serverItem.getId());
-
-        logger.info("[ZLM] 连接成功 {} - {}:{} ",
-                zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
-    }
-
-    class KeepAliveTimeoutRunnable implements Runnable{
-
-        private MediaServerItem serverItem;
-
-        public KeepAliveTimeoutRunnable(MediaServerItem serverItem) {
-            this.serverItem = serverItem;
-        }
-
-        @Override
-        public void run() {
-            logger.info("[zlm心跳到期]:" + serverItem.getId());
-            // 发起http请求验证zlm是否确实无法连接,如果确实无法连接则发送离线事件,否则不作处理
-            JSONObject mediaServerConfig = zlmresTfulUtils.getMediaServerConfig(serverItem);
-            if (mediaServerConfig != null && mediaServerConfig.getInteger("code") == 0) {
-                logger.info("[zlm心跳到期]:{}验证后zlm仍在线,恢复心跳信息,请检查zlm是否可以正常向wvp发送心跳", serverItem.getId());
-                // 添加zlm信息
-                updateMediaServerKeepalive(serverItem.getId(), null);
-            }else {
-                publisher.zlmOfflineEventPublish(serverItem.getId());
-            }
-        }
-    }
-
-
-    @Override
-    public void zlmServerOffline(String mediaServerId) {
-        delete(mediaServerId);
-        final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerId;
-        dynamicTask.stop(zlmKeepaliveKey);
-    }
-
-    @Override
-    public void resetOnlineServerItem(MediaServerItem serverItem) {
-        // 更新缓存
-        String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
-        // 使用zset的分数作为当前并发量, 默认值设置为0
-        if (redisTemplate.opsForZSet().score(key, serverItem.getId()) == null) {  // 不存在则设置默认值 已存在则重置
-            redisTemplate.opsForZSet().add(key, serverItem.getId(), 0L);
-            // 查询服务流数量
-            zlmresTfulUtils.getMediaList(serverItem, null, null, "rtsp",(mediaList ->{
-                Integer code = mediaList.getInteger("code");
-                if (code == 0) {
-                    JSONArray data = mediaList.getJSONArray("data");
-                    if (data != null) {
-                        redisTemplate.opsForZSet().add(key, serverItem.getId(), data.size());
-                    }
-                }
-            }));
-        }else {
-            clearRTPServer(serverItem);
-        }
-    }
-
-
-    @Override
-    public void addCount(String mediaServerId) {
-        if (mediaServerId == null) {
-            return;
-        }
-        String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
-        redisTemplate.opsForZSet().incrementScore(key, mediaServerId, 1);
-
-    }
-
-    @Override
-    public void removeCount(String mediaServerId) {
-        String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
-        redisTemplate.opsForZSet().incrementScore(key, mediaServerId, - 1);
-    }
-
-    /**
-     * 获取负载最低的节点
-     * @return MediaServerItem
-     */
-    @Override
-    public MediaServerItem getMediaServerForMinimumLoad(Boolean hasAssist) {
-        String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
-        Long size = redisTemplate.opsForZSet().zCard(key);
-        if (size  == null || size == 0) {
-            logger.info("获取负载最低的节点时无在线节点");
-            return null;
-        }
-
-        // 获取分数最低的,及并发最低的
-        Set<Object> objects = redisTemplate.opsForZSet().range(key, 0, -1);
-        ArrayList<Object> mediaServerObjectS = new ArrayList<>(objects);
-        MediaServerItem mediaServerItem = null;
-        if (hasAssist == null) {
-            String mediaServerId = (String)mediaServerObjectS.get(0);
-            mediaServerItem = getOne(mediaServerId);
-        }else if (hasAssist) {
-            for (Object mediaServerObject : mediaServerObjectS) {
-                String mediaServerId = (String)mediaServerObject;
-                MediaServerItem serverItem = getOne(mediaServerId);
-                if (serverItem.getRecordAssistPort() > 0) {
-                    mediaServerItem = serverItem;
-                    break;
-                }
-            }
-        }else if (!hasAssist) {
-            for (Object mediaServerObject : mediaServerObjectS) {
-                String mediaServerId = (String)mediaServerObject;
-                MediaServerItem serverItem = getOne(mediaServerId);
-                if (serverItem.getRecordAssistPort() == 0) {
-                    mediaServerItem = serverItem;
-                    break;
-                }
-            }
-        }
-
-        return mediaServerItem;
-    }
-
-    /**
-     * 对zlm服务器进行基础配置
-     * @param mediaServerItem 服务ID
-     * @param restart 是否重启zlm
-     */
-    @Override
-    public void setZLMConfig(MediaServerItem mediaServerItem, boolean restart) {
-        logger.info("[ZLM] 正在设置 :{} -> {}:{}",
-                mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
-        String protocol = sslEnabled ? "https" : "http";
-        String hookPrefix = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort);
-
-        Map<String, Object> param = new HashMap<>();
-        param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
-        if (mediaServerItem.getRtspPort() != 0) {
-            param.put("ffmpeg.snap", "%s -rtsp_transport tcp -i %s -y -f mjpeg -frames:v 1 %s");
-        }
-        param.put("hook.enable","1");
-        param.put("hook.on_flow_report","");
-        param.put("hook.on_play",String.format("%s/on_play", hookPrefix));
-        param.put("hook.on_http_access","");
-        param.put("hook.on_publish", String.format("%s/on_publish", hookPrefix));
-        param.put("hook.on_record_ts","");
-        param.put("hook.on_rtsp_auth","");
-        param.put("hook.on_rtsp_realm","");
-        param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrefix));
-        param.put("hook.on_shell_login","");
-        param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrefix));
-        param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrefix));
-        param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrefix));
-        param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrefix));
-        param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrefix));
-        param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrefix));
-        param.put("hook.on_record_mp4",String.format("%s/on_record_mp4", hookPrefix));
-        param.put("hook.timeoutSec","20");
-        // 推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。
-        // 置0关闭此特性(推流断开会导致立即断开播放器)
-        // 此参数不应大于播放器超时时间
-        // 优化此消息以更快的收到流注销事件
-        param.put("protocol.continue_push_ms", "3000" );
-        // 最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流,
-        // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项
-        if (mediaServerItem.isRtpEnable() && !ObjectUtils.isEmpty(mediaServerItem.getRtpPortRange())) {
-            param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-"));
-        }
-
-        if (!ObjectUtils.isEmpty(mediaServerItem.getRecordPath())) {
-            File recordPathFile = new File(mediaServerItem.getRecordPath());
-            param.put("protocol.mp4_save_path", recordPathFile.getParentFile().getPath());
-            param.put("protocol.downloadRoot", recordPathFile.getParentFile().getPath());
-            param.put("record.appName", recordPathFile.getName());
-        }
-
-        JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param);
-
-        if (responseJSON != null && responseJSON.getInteger("code") == 0) {
-            if (restart) {
-                logger.info("[ZLM] 设置成功,开始重启以保证配置生效 {} -> {}:{}",
-                        mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
-                zlmresTfulUtils.restartServer(mediaServerItem);
-            }else {
-                logger.info("[ZLM] 设置成功 {} -> {}:{}",
-                        mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
-            }
-
-
-        }else {
-            logger.info("[ZLM] 设置zlm失败 {} -> {}:{}",
-                    mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
-        }
-
-
-    }
-
-
-    @Override
-    public MediaServerItem checkMediaServer(String ip, int port, String secret) {
-        if (mediaServerMapper.queryOneByHostAndPort(ip, port) != null) {
-            throw new ControllerException(ErrorCode.ERROR100.getCode(), "此连接已存在");
-        }
-        MediaServerItem mediaServerItem = new MediaServerItem();
-        mediaServerItem.setIp(ip);
-        mediaServerItem.setHttpPort(port);
-        mediaServerItem.setSecret(secret);
-        JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
-        if (responseJSON == null) {
-            throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接失败");
-        }
-        JSONArray data = responseJSON.getJSONArray("data");
-        ZLMServerConfig zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
-        if (zlmServerConfig == null) {
-            throw new ControllerException(ErrorCode.ERROR100.getCode(), "读取配置失败");
-        }
-        if (mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()) != null) {
-            throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体服务ID [" + zlmServerConfig.getGeneralMediaServerId() + " ] 已存在,请修改媒体服务器配置");
-        }
-        mediaServerItem.setHttpSSlPort(zlmServerConfig.getHttpPort());
-        mediaServerItem.setRtmpPort(zlmServerConfig.getRtmpPort());
-        mediaServerItem.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
-        mediaServerItem.setRtspPort(zlmServerConfig.getRtspPort());
-        mediaServerItem.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
-        mediaServerItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
-        mediaServerItem.setStreamIp(ip);
-        mediaServerItem.setHookIp(sipConfig.getIp().split(",")[0]);
-        mediaServerItem.setSdpIp(ip);
-        return mediaServerItem;
-    }
-
-    @Override
-    public boolean checkMediaRecordServer(String ip, int port) {
-        boolean result = false;
-        OkHttpClient client = new OkHttpClient();
-        String url = String.format("http://%s:%s/index/api/record",  ip, port);
-        Request request = new Request.Builder()
-                .get()
-                .url(url)
-                .build();
-        try {
-            Response response = client.newCall(request).execute();
-            if (response != null) {
-                result = true;
-            }
-        } catch (Exception e) {}
-
-        return result;
-    }
-
-    @Override
-    public void delete(String id) {
-        redisTemplate.opsForZSet().remove(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(), id);
-        String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + id;
-        redisTemplate.delete(key);
-    }
-    @Override
-    public void deleteDb(String id){
-        //同步删除数据库中的数据
-        mediaServerMapper.delOne(id);
-    }
-
-    @Override
-    public void updateMediaServerKeepalive(String mediaServerId, ServerKeepaliveData data) {
-        MediaServerItem mediaServerItem = getOne(mediaServerId);
-        if (mediaServerItem == null) {
-            // 缓存不存在,从数据库查询,如果数据库不存在则是错误的
-            mediaServerItem = getOneFromDatabase(mediaServerId);
-            if (mediaServerItem == null) {
-                logger.warn("[更新ZLM 保活信息] 流媒体{}尚未加入使用,请检查节点中是否含有此流媒体 ", mediaServerId);
-                return;
-            }
-            // zlm连接重试
-            logger.warn("[更新ZLM 保活信息]尝试链接zml id {}", mediaServerId);
-            ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null);
-            String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId();
-            redisTemplate.opsForValue().set(key, mediaServerItem);
-            resetOnlineServerItem(mediaServerItem);
-            clearRTPServer(mediaServerItem);
-        }
-        final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerItem.getId();
-        dynamicTask.stop(zlmKeepaliveKey);
-        dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(mediaServerItem), (mediaServerItem.getHookAliveInterval().intValue() + 5) * 1000);
-    }
-
-    private MediaServerItem getOneFromDatabase(String mediaServerId) {
-        return mediaServerMapper.queryOne(mediaServerId);
-    }
-
-    @Override
-    public void syncCatchFromDatabase() {
-        List<MediaServerItem> allInCatch = getAll();
-        List<MediaServerItem> allInDatabase = mediaServerMapper.queryAll();
-        Map<String, MediaServerItem> mediaServerItemMap = new HashMap<>();
-
-        for (MediaServerItem mediaServerItem : allInDatabase) {
-            mediaServerItemMap.put(mediaServerItem.getId(), mediaServerItem);
-        }
-        for (MediaServerItem mediaServerItem : allInCatch) {
-            if (!mediaServerItemMap.containsKey(mediaServerItem.getId())) {
-                delete(mediaServerItem.getId());
-            }
-        }
-    }
-
-    @Override
-    public MediaServerLoad getLoad(MediaServerItem mediaServerItem) {
-        MediaServerLoad result = new MediaServerLoad();
-        result.setId(mediaServerItem.getId());
-        result.setPush(redisCatchStorage.getPushStreamCount(mediaServerItem.getId()));
-        result.setProxy(redisCatchStorage.getProxyStreamCount(mediaServerItem.getId()));
-
-        result.setGbReceive(inviteStreamService.getStreamInfoCount(mediaServerItem.getId()));
-        result.setGbSend(redisCatchStorage.getGbSendCount(mediaServerItem.getId()));
-        return result;
-    }
-
-    @Override
-    public List<MediaServerItem> getAllWithAssistPort() {
-        return mediaServerMapper.queryAllWithAssistPort();
-    }
-}

+ 286 - 64
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java

@@ -1,109 +1,331 @@
 package com.genersoft.iot.vmp.service.impl;
 
-import com.alibaba.fastjson2.JSON;
-import com.alibaba.fastjson2.JSONArray;
-import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.common.StreamInfo;
-import com.genersoft.iot.vmp.conf.MediaConfig;
-import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.common.InviteInfo;
+import com.genersoft.iot.vmp.common.InviteSessionType;
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.conf.exception.ControllerException;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
+import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+import com.genersoft.iot.vmp.media.bean.ResultForOnPublish;
+import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
-import com.genersoft.iot.vmp.service.IMediaServerService;
-import com.genersoft.iot.vmp.service.IMediaService;
+import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
+import com.genersoft.iot.vmp.service.*;
+import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import com.genersoft.iot.vmp.utils.DateUtil;
+import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
+import com.genersoft.iot.vmp.vmanager.bean.OtherPsSendInfo;
+import com.genersoft.iot.vmp.vmanager.bean.OtherRtpSendInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.util.ObjectUtils;
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 @Service
 public class MediaServiceImpl implements IMediaService {
 
+    private final static Logger logger = LoggerFactory.getLogger(MediaServiceImpl.class);
+
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
 
     @Autowired
-    private IVideoManagerStorage storager;
+    private IStreamProxyService streamProxyService;
+
+    @Autowired
+    private UserSetting userSetting;
+
+    @Autowired
+    private RedisTemplate<Object, Object> redisTemplate;
 
     @Autowired
-    private IMediaServerService mediaServerService;
+    private IUserService userService;
 
+    @Autowired
+    private IInviteStreamService inviteStreamService;
 
     @Autowired
-    private MediaConfig mediaConfig;
+    private VideoStreamSessionManager sessionManager;
 
     @Autowired
-    private ZLMRESTfulUtils zlmresTfulUtils;
+    private IVideoManagerStorage storager;
 
+    @Autowired
+    private ZLMMediaListManager zlmMediaListManager;
 
+    @Autowired
+    private IDeviceService deviceService;
 
-    @Override
-    public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String callId) {
-        return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null, callId, true);
-    }
+    @Autowired
+    private ISIPCommanderForPlatform commanderForPlatform;
+
+    @Autowired
+    private ISIPCommander commander;
 
     @Override
-    public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr, boolean authority) {
-        StreamInfo streamInfo = null;
-        if (mediaServerId == null) {
-            mediaServerId = mediaConfig.getId();
+    public boolean authenticatePlay(String app, String stream, String callId) {
+        if (app == null || stream == null) {
+            return false;
         }
-        MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
-        if (mediaInfo == null) {
-            return null;
+        if ("rtp".equals(app)) {
+            return true;
         }
-        String calld = null;
         StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream);
-        if (streamAuthorityInfo != null) {
-            calld = streamAuthorityInfo.getCallId();
+        if (streamAuthorityInfo == null || streamAuthorityInfo.getCallId() == null) {
+            return true;
         }
-        JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaInfo, app, stream);
-        if (mediaList != null) {
-            if (mediaList.getInteger("code") == 0) {
-                JSONArray data = mediaList.getJSONArray("data");
-                if (data == null) {
-                    return null;
+        return streamAuthorityInfo.getCallId().equals(callId);
+    }
+
+    @Override
+    public ResultForOnPublish authenticatePublish(MediaServer mediaServer, String app, String stream, String params) {
+        // 推流鉴权的处理
+        if (!"rtp".equals(app)) {
+            StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(app, stream);
+            if (streamProxyItem != null) {
+                ResultForOnPublish result = new ResultForOnPublish();
+                result.setEnable_audio(streamProxyItem.isEnableAudio());
+                result.setEnable_mp4(streamProxyItem.isEnableMp4());
+                return result;
+            }
+            if (userSetting.getPushAuthority()) {
+                // 对于推流进行鉴权
+                Map<String, String> paramMap = urlParamToMap(params);
+                // 推流鉴权
+                if (params == null) {
+                    logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)");
+                    throw new ControllerException(ErrorCode.ERROR401.getCode(), "Unauthorized");
+                }
+
+                String sign = paramMap.get("sign");
+                if (sign == null) {
+                    logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)");
+                    throw new ControllerException(ErrorCode.ERROR401.getCode(), "Unauthorized");
                 }
-                JSONObject mediaJSON = data.getJSONObject(0);
-                JSONArray tracks = mediaJSON.getJSONArray("tracks");
-                if (authority) {
-                    streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr, calld, true);
-                }else {
-                    streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr,null, true);
+                // 推流自定义播放鉴权码
+                String callId = paramMap.get("callId");
+                // 鉴权配置
+                boolean hasAuthority = userService.checkPushAuthority(callId, sign);
+                if (!hasAuthority) {
+                    logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign);
+                    throw new ControllerException(ErrorCode.ERROR401.getCode(), "Unauthorized");
                 }
+                StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(app, stream, mediaServer.getId());
+                streamAuthorityInfo.setCallId(callId);
+                streamAuthorityInfo.setSign(sign);
+                // 鉴权通过
+                redisCatchStorage.updateStreamAuthorityInfo(app, stream, streamAuthorityInfo);
             }
+        } else {
+            zlmMediaListManager.sendStreamEvent(app, stream, mediaServer.getId());
         }
-        return streamInfo;
-    }
 
 
+        ResultForOnPublish result = new ResultForOnPublish();
+        result.setEnable_audio(true);
 
-    @Override
-    public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority) {
-        return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null, authority);
+        // 是否录像
+        if ("rtp".equals(app)) {
+            result.setEnable_mp4(userSetting.getRecordSip());
+        } else {
+            result.setEnable_mp4(userSetting.isRecordPushLive());
+        }
+        // 国标流
+        if ("rtp".equals(app)) {
+
+            InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, stream);
+
+            // 单端口模式下修改流 ID
+            if (!mediaServer.isRtpEnable() && inviteInfo == null) {
+                String ssrc = String.format("%010d", Long.parseLong(stream, 16));
+                inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc);
+                if (inviteInfo != null) {
+                    result.setStream_replace(inviteInfo.getStream());
+                    logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", stream, inviteInfo.getStream());
+                    stream = inviteInfo.getStream();
+                }
+            }
+
+            // 设置音频信息及录制信息
+            List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, stream);
+            if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
+
+                // 为录制国标模拟一个鉴权信息, 方便后续写入录像文件时使用
+                StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(app, stream, mediaServer.getId());
+                streamAuthorityInfo.setApp(app);
+                streamAuthorityInfo.setStream(ssrcTransactionForAll.get(0).getStream());
+                streamAuthorityInfo.setCallId(ssrcTransactionForAll.get(0).getSipTransactionInfo().getCallId());
+
+                redisCatchStorage.updateStreamAuthorityInfo(app, ssrcTransactionForAll.get(0).getStream(), streamAuthorityInfo);
+
+                String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
+                String channelId = ssrcTransactionForAll.get(0).getChannelId();
+                DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
+                if (deviceChannel != null) {
+                    result.setEnable_audio(deviceChannel.isHasAudio());
+                }
+                // 如果是录像下载就设置视频间隔十秒
+                if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
+                    // 获取录像的总时长,然后设置为这个视频的时长
+                    InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, stream);
+                    if (inviteInfoForDownload != null && inviteInfoForDownload.getStreamInfo() != null) {
+                        String startTime = inviteInfoForDownload.getStreamInfo().getStartTime();
+                        String endTime = inviteInfoForDownload.getStreamInfo().getEndTime();
+                        long difference = DateUtil.getDifference(startTime, endTime) / 1000;
+                        result.setMp4_max_second((int) difference);
+                        result.setEnable_mp4(true);
+                        // 设置为2保证得到的mp4的时长是正常的
+                        result.setModify_stamp(2);
+                    }
+                }
+                // 如果是talk对讲,则默认获取声音
+                if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) {
+                    result.setEnable_audio(true);
+                }
+            }
+        } else if (app.equals("broadcast")) {
+            result.setEnable_audio(true);
+        } else if (app.equals("talk")) {
+            result.setEnable_audio(true);
+        }
+        if (app.equalsIgnoreCase("rtp")) {
+            String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "_" + stream;
+            OtherRtpSendInfo otherRtpSendInfo = (OtherRtpSendInfo) redisTemplate.opsForValue().get(receiveKey);
+
+            String receiveKeyForPS = VideoManagerConstants.WVP_OTHER_RECEIVE_PS_INFO + userSetting.getServerId() + "_" + stream;
+            OtherPsSendInfo otherPsSendInfo = (OtherPsSendInfo) redisTemplate.opsForValue().get(receiveKeyForPS);
+            if (otherRtpSendInfo != null || otherPsSendInfo != null) {
+                result.setEnable_mp4(true);
+            }
+        }
+        return result;
     }
 
-    @Override
-    public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr, String callId, boolean isPlay) {
-        StreamInfo streamInfoResult = new StreamInfo();
-        streamInfoResult.setStream(stream);
-        streamInfoResult.setApp(app);
-        if (addr == null) {
-            addr = mediaInfo.getStreamIp();
+    private Map<String, String> urlParamToMap(String params) {
+        HashMap<String, String> map = new HashMap<>();
+        if (ObjectUtils.isEmpty(params)) {
+            return map;
         }
+        String[] paramsArray = params.split("&");
+        if (paramsArray.length == 0) {
+            return map;
+        }
+        for (String param : paramsArray) {
+            String[] paramArray = param.split("=");
+            if (paramArray.length == 2) {
+                map.put(paramArray[0], paramArray[1]);
+            }
+        }
+        return map;
+    }
+
+    @Override
+    public boolean closeStreamOnNoneReader(String mediaServerId, String app, String stream, String schema) {
+        boolean result = false;
+        // 国标类型的流
+        if ("rtp".equals(app)) {
+            result = userSetting.getStreamOnDemand();
+            // 国标流, 点播/录像回放/录像下载
+            InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, stream);
+            // 点播
+            if (inviteInfo != null) {
+                // 录像下载
+                if (inviteInfo.getType() == InviteSessionType.DOWNLOAD) {
+                    return false;
+                }
+                // 收到无人观看说明流也没有在往上级推送
+                if (redisCatchStorage.isChannelSendingRTP(inviteInfo.getChannelId())) {
+                    List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChannelId(
+                            inviteInfo.getChannelId());
+                    if (!sendRtpItems.isEmpty()) {
+                        for (SendRtpItem sendRtpItem : sendRtpItems) {
+                            ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
+                            try {
+                                commanderForPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
+                            } catch (SipException | InvalidArgumentException | ParseException e) {
+                                logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
+                            }
+                            redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
+                                    sendRtpItem.getCallId(), sendRtpItem.getStream());
+                            if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) {
+                                MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
+                                        sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
+                                        sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
+                                messageForPushChannel.setPlatFormIndex(parentPlatform.getId());
+                                redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
+                            }
+                        }
+                    }
+                }
+                Device device = deviceService.getDevice(inviteInfo.getDeviceId());
+                if (device != null) {
+                    try {
+                        // 多查询一次防止已经被处理了
+                        InviteInfo info = inviteStreamService.getInviteInfo(inviteInfo.getType(),
+                                inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
+                        if (info != null) {
+                            commander.streamByeCmd(device, inviteInfo.getChannelId(),
+                                    inviteInfo.getStream(), null);
+                        } else {
+                            logger.info("[无人观看] 未找到设备的点播信息: {}, 流:{}", inviteInfo.getDeviceId(), stream);
+                        }
+                    } catch (InvalidArgumentException | ParseException | SipException |
+                             SsrcTransactionNotFoundException e) {
+                        logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage());
+                    }
+                } else {
+                    logger.info("[无人观看] 未找到设备: {},流:{}", inviteInfo.getDeviceId(), stream);
+                }
 
-        streamInfoResult.setIp(addr);
-        streamInfoResult.setMediaServerId(mediaInfo.getId());
-        String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId;
-        streamInfoResult.setRtmp(addr, mediaInfo.getRtmpPort(),mediaInfo.getRtmpSSlPort(), app,  stream, callIdParam);
-        streamInfoResult.setRtsp(addr, mediaInfo.getRtspPort(),mediaInfo.getRtspSSLPort(), app,  stream, callIdParam);
-        streamInfoResult.setFlv(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app,  stream, callIdParam);
-        streamInfoResult.setFmp4(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app,  stream, callIdParam);
-        streamInfoResult.setHls(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app,  stream, callIdParam);
-        streamInfoResult.setTs(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app,  stream, callIdParam);
-        streamInfoResult.setRtc(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app,  stream, callIdParam, isPlay);
-
-        streamInfoResult.setTracks(tracks);
-        return streamInfoResult;
+                inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
+                        inviteInfo.getChannelId(), inviteInfo.getStream());
+                storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
+                return result;
+            }
+            SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, stream, null);
+            if (sendRtpItem != null && "talk".equals(sendRtpItem.getApp())) {
+                return false;
+            }
+        } else if ("talk".equals(app) || "broadcast".equals(app)) {
+            return false;
+        } else {
+            // 非国标流 推流/拉流代理
+            // 拉流代理
+            StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(app, stream);
+            if (streamProxyItem != null) {
+                if (streamProxyItem.isEnableRemoveNoneReader()) {
+                    // 无人观看自动移除
+                    result = true;
+                    streamProxyService.del(app, stream);
+                    String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrcUrl();
+                    logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", app, stream, url);
+                } else if (streamProxyItem.isEnableDisableNoneReader()) {
+                    // 无人观看停用
+                    result = true;
+                    // 修改数据
+                    streamProxyService.stop(app, stream);
+                } else {
+                    // 无人观看不做处理
+                    result = false;
+                }
+            }
+        }
+        return result;
     }
 }

+ 82 - 47
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java

@@ -1,10 +1,7 @@
 package com.genersoft.iot.vmp.service.impl;
 
-import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.common.InviteInfo;
-import com.genersoft.iot.vmp.common.InviteSessionStatus;
-import com.genersoft.iot.vmp.common.InviteSessionType;
 import com.baomidou.dynamic.datasource.annotation.DS;
+import com.genersoft.iot.vmp.common.*;
 import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
@@ -12,46 +9,38 @@ import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
-import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
+import com.genersoft.iot.vmp.media.event.hook.HookData;
+import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
+import com.genersoft.iot.vmp.media.event.mediaServer.MediaSendRtpStoppedEvent;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.service.IInviteStreamService;
-import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IPlatformService;
 import com.genersoft.iot.vmp.service.IPlayService;
 import com.genersoft.iot.vmp.service.bean.*;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import com.genersoft.iot.vmp.storager.dao.*;
+import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
+import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
-import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
 import javax.sdp.*;
 import javax.sip.InvalidArgumentException;
 import javax.sip.ResponseEvent;
-import javax.sip.PeerUnavailableException;
 import javax.sip.SipException;
 import java.text.ParseException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
 import java.util.*;
 
 /**
@@ -81,7 +70,7 @@ public class PlatformServiceImpl implements IPlatformService {
     private IMediaServerService mediaServerService;
 
     @Autowired
-    private SIPCommanderFroPlatform commanderForPlatform;
+    private ISIPCommanderForPlatform commanderForPlatform;
 
     @Autowired
     private DynamicTask dynamicTask;
@@ -99,20 +88,67 @@ public class PlatformServiceImpl implements IPlatformService {
     private UserSetting userSetting;
 
     @Autowired
-    private ZlmHttpHookSubscribe subscribe;
+    private HookSubscribe subscribe;
 
     @Autowired
     private VideoStreamSessionManager streamSession;
 
-
     @Autowired
     private IPlayService playService;
 
     @Autowired
     private IInviteStreamService inviteStreamService;
 
-    @Autowired
-    private ZLMRESTfulUtils zlmresTfulUtils;
+
+    /**
+     * 流离开的处理
+     */
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaDepartureEvent event) {
+        List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(event.getStream());
+        if (!sendRtpItems.isEmpty()) {
+            for (SendRtpItem sendRtpItem : sendRtpItems) {
+                if (sendRtpItem != null && sendRtpItem.getApp().equals(event.getApp())) {
+                    String platformId = sendRtpItem.getPlatformId();
+                    ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId);
+
+                    try {
+                        if (platform != null) {
+                            commanderForPlatform.streamByeCmd(platform, sendRtpItem);
+                            redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(),
+                                    sendRtpItem.getCallId(), sendRtpItem.getStream());
+                        }
+                    } catch (SipException | InvalidArgumentException | ParseException e) {
+                        logger.error("[命令发送失败] 发送BYE: {}", e.getMessage());
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 发流停止
+     */
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaSendRtpStoppedEvent event) {
+        List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(event.getStream());
+        if (sendRtpItems != null && !sendRtpItems.isEmpty()) {
+            for (SendRtpItem sendRtpItem : sendRtpItems) {
+                ParentPlatform parentPlatform = platformMapper.getParentPlatByServerGBId(sendRtpItem.getPlatformId());
+                ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
+                try {
+                    commanderForPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
+                } catch (SipException | InvalidArgumentException | ParseException e) {
+                    logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
+                }
+                redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
+                        sendRtpItem.getCallId(), sendRtpItem.getStream());
+            }
+        }
+    }
 
 
     @Override
@@ -400,7 +436,7 @@ public class PlatformServiceImpl implements IPlatformService {
             for (SendRtpItem sendRtpItem : sendRtpItems) {
                 ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
                 redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(), null, null);
-                MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+                MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
                 Map<String, Object> param = new HashMap<>(3);
                 param.put("vhost", "__defaultVhost__");
                 param.put("app", sendRtpItem.getApp());
@@ -463,7 +499,7 @@ public class PlatformServiceImpl implements IPlatformService {
     }
 
     @Override
-    public void broadcastInvite(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem, ZlmHttpHookSubscribe.Event hookEvent,
+    public void broadcastInvite(ParentPlatform platform, String channelId, MediaServer mediaServerItem, HookSubscribe.Event hookEvent,
                                 SipSubscribe.Event errorEvent, InviteTimeOutCallback timeoutCallback) throws InvalidArgumentException, ParseException, SipException {
 
         if (mediaServerItem == null) {
@@ -474,19 +510,19 @@ public class PlatformServiceImpl implements IPlatformService {
 
         if (inviteInfoForOld != null && inviteInfoForOld.getStreamInfo() != null) {
             // 如果zlm不存在这个流,则删除数据即可
-            MediaServerItem mediaServerItemForStreamInfo = mediaServerService.getOne(inviteInfoForOld.getStreamInfo().getMediaServerId());
+            MediaServer mediaServerItemForStreamInfo = mediaServerService.getOne(inviteInfoForOld.getStreamInfo().getMediaServerId());
             if (mediaServerItemForStreamInfo != null) {
-                Boolean ready = zlmServerFactory.isStreamReady(mediaServerItemForStreamInfo, inviteInfoForOld.getStreamInfo().getApp(), inviteInfoForOld.getStreamInfo().getStream());
+                Boolean ready = mediaServerService.isStreamReady(mediaServerItemForStreamInfo, inviteInfoForOld.getStreamInfo().getApp(), inviteInfoForOld.getStreamInfo().getStream());
                 if (!ready) {
                     // 错误存在于redis中的数据
                     inviteStreamService.removeInviteInfo(inviteInfoForOld);
                 }else {
                     // 流确实尚在推流,直接回调结果
-                    OnStreamChangedHookParam hookParam = new OnStreamChangedHookParam();
-                    hookParam.setApp(inviteInfoForOld.getStreamInfo().getApp());
-                    hookParam.setStream(inviteInfoForOld.getStreamInfo().getStream());
-
-                    hookEvent.response(mediaServerItemForStreamInfo, hookParam);
+                    HookData hookData = new HookData();
+                    hookData.setApp(inviteInfoForOld.getStreamInfo().getApp());
+                    hookData.setStream(inviteInfoForOld.getStreamInfo().getStream());
+                    hookData.setMediaServer(mediaServerItemForStreamInfo);
+                    hookEvent.response(hookData);
                     return;
                 }
             }
@@ -506,7 +542,7 @@ public class PlatformServiceImpl implements IPlatformService {
         } else {
             tcpMode = 0;
         }
-        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, ssrcCheck, false, null, true, false, tcpMode);
+        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, ssrcCheck, false, null, true, false, false, tcpMode);
         if (ssrcInfo == null || ssrcInfo.getPort() < 0) {
             logger.info("[国标级联] 发起语音喊话 开启端口监听失败, platform: {}, channel: {}", platform.getServerGBId(), channelId);
             SipSubscribe.EventResult<Object> eventResult = new SipSubscribe.EventResult<>();
@@ -544,14 +580,14 @@ public class PlatformServiceImpl implements IPlatformService {
                 }
             }
         }, userSetting.getPlayTimeout());
-        commanderForPlatform.broadcastInviteCmd(platform, channelId, mediaServerItem, ssrcInfo, (mediaServerItemForInvite, hookParam)->{
+        commanderForPlatform.broadcastInviteCmd(platform, channelId, mediaServerItem, ssrcInfo, (hookData)->{
             logger.info("[国标级联] 发起语音喊话 收到上级推流 deviceId: {}, channelId: {}", platform.getServerGBId(), channelId);
             dynamicTask.stop(timeOutTaskKey);
             // hook响应
-            playService.onPublishHandlerForPlay(mediaServerItemForInvite, hookParam, platform.getServerGBId(), channelId);
+            playService.onPublishHandlerForPlay(hookData.getMediaServer(), hookData.getMediaInfo(), platform.getServerGBId(), channelId);
             // 收到流
             if (hookEvent != null) {
-                hookEvent.response(mediaServerItem, hookParam);
+                hookEvent.response(hookData);
             }
         }, event -> {
 
@@ -604,13 +640,12 @@ public class PlatformServiceImpl implements IPlatformService {
         });
     }
 
-    private void inviteOKHandler(SipSubscribe.EventResult eventResult, SSRCInfo ssrcInfo, int tcpMode, boolean ssrcCheck, MediaServerItem mediaServerItem,
+    private void inviteOKHandler(SipSubscribe.EventResult eventResult, SSRCInfo ssrcInfo, int tcpMode, boolean ssrcCheck, MediaServer mediaServerItem,
                                  ParentPlatform platform, String channelId, String timeOutTaskKey, ErrorCallback<Object> callback,
                                  InviteInfo inviteInfo, InviteSessionType inviteSessionType){
         inviteInfo.setStatus(InviteSessionStatus.ok);
         ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
         String contentString = new String(responseEvent.getResponse().getRawContent());
-        System.out.println(1111);
         System.out.println(contentString);
         String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString);
         // 兼容回复的消息中缺少ssrc(y字段)的情况
@@ -709,7 +744,7 @@ public class PlatformServiceImpl implements IPlatformService {
 
 
     private void tcpActiveHandler(ParentPlatform platform, String channelId, String contentString,
-                                  MediaServerItem mediaServerItem, int tcpMode, boolean ssrcCheck,
+                                  MediaServer mediaServerItem, int tcpMode, boolean ssrcCheck,
                                   String timeOutTaskKey, SSRCInfo ssrcInfo, ErrorCallback<Object> callback){
         if (tcpMode != 2) {
             return;
@@ -737,8 +772,8 @@ public class PlatformServiceImpl implements IPlatformService {
             }
             logger.info("[TCP主动连接对方] serverGbId: {}, channelId: {}, 连接对方的地址:{}:{}, SSRC: {}, SSRC校验:{}",
                     platform.getServerGBId(), channelId, sdp.getConnection().getAddress(), port, ssrcInfo.getSsrc(), ssrcCheck);
-            JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream());
-            logger.info("[TCP主动连接对方] 结果: {}", jsonObject);
+            Boolean result = mediaServerService.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream());
+            logger.info("[TCP主动连接对方] 结果: {}", result);
         } catch (SdpException e) {
             logger.error("[TCP主动连接对方] serverGbId: {}, channelId: {}, 解析200OK的SDP信息失败", platform.getServerGBId(), channelId, e);
             dynamicTask.stop(timeOutTaskKey);
@@ -757,7 +792,7 @@ public class PlatformServiceImpl implements IPlatformService {
     }
 
     @Override
-    public void stopBroadcast(ParentPlatform platform, DeviceChannel channel, String stream, boolean sendBye, MediaServerItem mediaServerItem) {
+    public void stopBroadcast(ParentPlatform platform, DeviceChannel channel, String stream, boolean sendBye, MediaServer mediaServerItem) {
 
         try {
             if (sendBye) {

Файловите разлики са ограничени, защото са твърде много
+ 291 - 263
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java


+ 137 - 132
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java

@@ -1,6 +1,5 @@
 package com.genersoft.iot.vmp.service.impl;
 
-import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.genersoft.iot.vmp.common.GeneralCallback;
@@ -9,17 +8,19 @@ import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
-import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
+import com.genersoft.iot.vmp.media.bean.MediaInfo;
+import com.genersoft.iot.vmp.media.event.hook.Hook;
+import com.genersoft.iot.vmp.media.event.hook.HookType;
+import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
+import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
+import com.genersoft.iot.vmp.media.event.media.MediaNotFoundEvent;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
 import com.genersoft.iot.vmp.service.IGbStreamService;
-import com.genersoft.iot.vmp.service.IMediaServerService;
-import com.genersoft.iot.vmp.service.IMediaService;
 import com.genersoft.iot.vmp.service.IStreamProxyService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
@@ -29,11 +30,14 @@ import com.genersoft.iot.vmp.storager.dao.StreamProxyMapper;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import com.github.pagehelper.PageInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.TransactionDefinition;
@@ -60,12 +64,6 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
     @Autowired
     private IVideoManagerStorage videoManagerStorager;
 
-    @Autowired
-    private IMediaService mediaService;
-
-    @Autowired
-    private ZLMRESTfulUtils zlmresTfulUtils;
-
     @Autowired
     private ZLMServerFactory zlmServerFactory;
 
@@ -94,7 +92,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
     private IMediaServerService mediaServerService;
 
     @Autowired
-    private ZlmHttpHookSubscribe hookSubscribe;
+    private HookSubscribe hookSubscribe;
 
     @Autowired
     private DynamicTask dynamicTask;
@@ -105,31 +103,62 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
     @Autowired
     TransactionDefinition transactionDefinition;
 
+    /**
+     * 流到来的处理
+     */
+    @Async("taskExecutor")
+    @org.springframework.context.event.EventListener
+    public void onApplicationEvent(MediaArrivalEvent event) {
+        if ("rtsp".equals(event.getSchema())) {
+            updateStatus(true, event.getApp(), event.getStream());
+        }
+    }
+
+    /**
+     * 流离开的处理
+     */
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaDepartureEvent event) {
+        if ("rtsp".equals(event.getSchema())) {
+            updateStatus(false, event.getApp(), event.getStream());
+        }
+    }
+
+    /**
+     * 流离开的处理
+     */
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaNotFoundEvent event) {
+        if ("rtp".equals(event.getApp())) {
+            return;
+        }
+        // 拉流代理
+        StreamProxyItem streamProxyByAppAndStream = getStreamProxyByAppAndStream(event.getApp(), event.getStream());
+        if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnableDisableNoneReader()) {
+            start(event.getApp(), event.getStream());
+        }
+    }
+
 
     @Override
     public void save(StreamProxyItem param, GeneralCallback<StreamInfo> callback) {
-        MediaServerItem mediaInfo;
+        MediaServer mediaServer;
         if (ObjectUtils.isEmpty(param.getMediaServerId()) || "auto".equals(param.getMediaServerId())){
-            mediaInfo = mediaServerService.getMediaServerForMinimumLoad(null);
+            mediaServer = mediaServerService.getMediaServerForMinimumLoad(null);
         }else {
-            mediaInfo = mediaServerService.getOne(param.getMediaServerId());
+            mediaServer = mediaServerService.getOne(param.getMediaServerId());
         }
-        if (mediaInfo == null) {
+        if (mediaServer == null) {
             logger.warn("保存代理未找到在线的ZLM...");
             throw new ControllerException(ErrorCode.ERROR100.getCode(), "保存代理未找到在线的ZLM");
         }
         String dstUrl;
         if ("ffmpeg".equalsIgnoreCase(param.getType())) {
-            JSONObject jsonObject = zlmresTfulUtils.getMediaServerConfig(mediaInfo);
-            if (jsonObject.getInteger("code") != 0) {
-                throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取流媒体配置失败");
-            }
-            JSONArray dataArray = jsonObject.getJSONArray("data");
-            JSONObject mediaServerConfig = dataArray.getJSONObject(0);
-            if (ObjectUtils.isEmpty(param.getFfmpegCmdKey())) {
-                param.setFfmpegCmdKey("ffmpeg.cmd");
-            }
-            String ffmpegCmd = mediaServerConfig.getString(param.getFfmpegCmdKey());
+
+            String ffmpegCmd = mediaServerService.getFfmpegCmd(mediaServer, param.getFfmpegCmdKey());
+
             if (ffmpegCmd == null) {
                 throw new ControllerException(ErrorCode.ERROR100.getCode(), "ffmpeg拉流代理无法获取ffmpeg cmd");
             }
@@ -140,25 +169,25 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
             int port;
             String schemaForUri;
             if (schema.equalsIgnoreCase("rtsp")) {
-                port = mediaInfo.getRtspPort();
+                port = mediaServer.getRtspPort();
                 schemaForUri = schema;
             }else if (schema.equalsIgnoreCase("flv")) {
-                port = mediaInfo.getRtmpPort();
+                port = mediaServer.getRtmpPort();
                 schemaForUri = schema;
             }else {
-                port = mediaInfo.getRtmpPort();
+                port = mediaServer.getRtmpPort();
                 schemaForUri = schema;
             }
 
             dstUrl = String.format("%s://%s:%s/%s/%s", schemaForUri, "127.0.0.1", port, param.getApp(),
                     param.getStream());
         }else {
-            dstUrl = String.format("rtsp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtspPort(), param.getApp(),
+            dstUrl = String.format("rtsp://%s:%s/%s/%s", "127.0.0.1", mediaServer.getRtspPort(), param.getApp(),
                     param.getStream());
         }
         param.setDstUrl(dstUrl);
         logger.info("[拉流代理] 输出地址为:{}", dstUrl);
-        param.setMediaServerId(mediaInfo.getId());
+        param.setMediaServerId(mediaServer.getId());
         boolean saveResult;
         // 更新
         if (videoManagerStorager.queryStreamProxy(param.getApp(), param.getStream()) != null) {
@@ -170,17 +199,17 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
             callback.run(ErrorCode.ERROR100.getCode(), "保存失败", null);
             return;
         }
-        HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed(param.getApp(), param.getStream(), true, "rtsp", mediaInfo.getId());
-        hookSubscribe.addSubscribe(hookSubscribeForStreamChange, (mediaServerItem, response) -> {
-            StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
-                    mediaInfo, param.getApp(), param.getStream(), null, null);
+        Hook hook = Hook.getInstance(HookType.on_media_arrival, param.getApp(), param.getStream(), mediaServer.getId());
+        hookSubscribe.addSubscribe(hook, (hookData) -> {
+            StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(
+                    mediaServer, param.getApp(), param.getStream(), null, null);
             callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
         });
         if (param.isEnable()) {
             String talkKey = UUID.randomUUID().toString();
             String delayTalkKey = UUID.randomUUID().toString();
             dynamicTask.startDelay(delayTalkKey, ()->{
-                StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(param.getApp(), param.getStream(), mediaInfo.getId(), false);
+                StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStreamWithCheck(param.getApp(), param.getStream(), mediaServer.getId(), false);
                 if (streamInfo != null) {
                     callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
                 }else {
@@ -188,12 +217,12 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
                     callback.run(ErrorCode.ERROR100.getCode(), "超时", null);
                 }
             }, 7000);
-            JSONObject jsonObject = addStreamProxyToZlm(param);
-            if (jsonObject != null && jsonObject.getInteger("code") == 0) {
-                hookSubscribe.removeSubscribe(hookSubscribeForStreamChange);
+            WVPResult<String> result = addStreamProxyToZlm(param);
+            if (result != null && result.getCode() == 0) {
+                hookSubscribe.removeSubscribe(hook);
                 dynamicTask.stop(talkKey);
-                StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
-                        mediaInfo, param.getApp(), param.getStream(), null, null);
+                StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(
+                        mediaServer, param.getApp(), param.getStream(), null, null);
                 callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
             }else {
                 param.setEnable(false);
@@ -203,16 +232,16 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
                 }else {
                     updateStreamProxy(param);
                 }
-                if (jsonObject == null){
+                if (result == null){
                     callback.run(ErrorCode.ERROR100.getCode(), "记录已保存,启用失败", null);
                 }else {
-                    callback.run(ErrorCode.ERROR100.getCode(), jsonObject.getString("msg"), null);
+                    callback.run(ErrorCode.ERROR100.getCode(), result.getMsg(), null);
                 }
             }
         }
         else{
-            StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
-                    mediaInfo, param.getApp(), param.getStream(), null, null);
+            StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(
+                    mediaServer, param.getApp(), param.getStream(), null, null);
             callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
         }
     }
@@ -308,38 +337,34 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
     }
 
     @Override
-    public JSONObject addStreamProxyToZlm(StreamProxyItem param) {
-        JSONObject result = null;
-        MediaServerItem mediaServerItem = null;
+    public WVPResult<String> addStreamProxyToZlm(StreamProxyItem param) {
+        WVPResult<String> result = null;
+        MediaServer mediaServer = null;
         if (param.getMediaServerId() == null) {
             logger.warn("添加代理时MediaServerId 为null");
             return null;
         }else {
-            mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
+            mediaServer = mediaServerService.getOne(param.getMediaServerId());
         }
-        if (mediaServerItem == null) {
+        if (mediaServer == null) {
             return null;
         }
-        if (zlmServerFactory.isStreamReady(mediaServerItem, param.getApp(), param.getStream())) {
-            zlmresTfulUtils.closeStreams(mediaServerItem, param.getApp(), param.getStream());
+        if (mediaServerService.isStreamReady(mediaServer, param.getApp(), param.getStream())) {
+            mediaServerService.closeStreams(mediaServer, param.getApp(), param.getStream());
         }
+        String msgResult;
         if ("ffmpeg".equalsIgnoreCase(param.getType())){
-            result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrcUrl().trim(), param.getDstUrl(),
-                    param.getTimeoutMs() + "", param.isEnableAudio(), param.isEnableMp4(),
+            result = mediaServerService.addFFmpegSource(mediaServer, param.getSrcUrl().trim(), param.getDstUrl(),
+                    param.getTimeoutMs(), param.isEnableAudio(), param.isEnableMp4(),
                     param.getFfmpegCmdKey());
         }else {
-            result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl().trim(),
+            result = mediaServerService.addStreamProxy(mediaServer, param.getApp(), param.getStream(), param.getUrl().trim(),
                     param.isEnableAudio(), param.isEnableMp4(), param.getRtpType());
         }
-        if (result != null && result.getInteger("code") == 0) {
-            JSONObject data = result.getJSONObject("data");
-            if (data == null) {
-                logger.warn("[获取拉流代理的结果数据Data] 失败: {}", result );
-                return result;
-            }
-            String key = data.getString("key");
+        if (result != null && result.getCode() == 0) {
+            String key = result.getData();
             if (key == null) {
-                logger.warn("[获取拉流代理的结果数据Data中的KEY] 失败: {}", result );
+                logger.warn("[获取拉流代理的结果数据Data] 失败: {}", result );
                 return result;
             }
             param.setStreamKey(key);
@@ -349,16 +374,23 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
     }
 
     @Override
-    public JSONObject removeStreamProxyFromZlm(StreamProxyItem param) {
+    public Boolean removeStreamProxyFromZlm(StreamProxyItem param) {
         if (param ==null) {
             return null;
         }
-        MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
-        JSONObject result = null;
+        MediaServer mediaServer = mediaServerService.getOne(param.getMediaServerId());
+        if (mediaServer == null) {
+            return null;
+        }
+        List<StreamInfo> mediaList = mediaServerService.getMediaList(mediaServer, param.getApp(), param.getStream(), null);
+        if (mediaList == null || mediaList.isEmpty()) {
+            return true;
+        }
+        Boolean result = false;
         if ("ffmpeg".equalsIgnoreCase(param.getType())){
-            result = zlmresTfulUtils.delFFmpegSource(mediaServerItem, param.getStreamKey());
+            result = mediaServerService.delFFmpegSource(mediaServer, param.getStreamKey());
         }else {
-            result = zlmresTfulUtils.delStreamProxy(mediaServerItem, param.getStreamKey());
+            result = mediaServerService.delStreamProxy(mediaServer, param.getStreamKey());
         }
         return result;
     }
@@ -379,8 +411,8 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
             gbStreamMapper.del(app, stream);
             videoManagerStorager.deleteStreamProxy(app, stream);
             redisCatchStorage.removeStream(streamProxyItem.getMediaServerId(), "PULL", app, stream);
-            JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem);
-            if (jsonObject != null && jsonObject.getInteger("code") == 0) {
+            Boolean result = removeStreamProxyFromZlm(streamProxyItem);
+            if (result != null && result) {
                 logger.info("[移除代理]: 代理: {}/{}, 从zlm移除成功", app, stream);
             }else {
                 logger.info("[移除代理]: 代理: {}/{}, 从zlm移除失败", app, stream);
@@ -393,16 +425,16 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
         boolean result = false;
         StreamProxyItem streamProxy = videoManagerStorager.queryStreamProxy(app, stream);
         if (streamProxy != null && !streamProxy.isEnable() ) {
-            JSONObject jsonObject = addStreamProxyToZlm(streamProxy);
-            if (jsonObject == null) {
+            WVPResult<String> wvpResult = addStreamProxyToZlm(streamProxy);
+            if (wvpResult == null) {
                 return false;
             }
-            if (jsonObject.getInteger("code") == 0) {
+            if (wvpResult.getCode() == 0) {
                 result = true;
                 streamProxy.setEnable(true);
                 updateStreamProxy(streamProxy);
             }else {
-                logger.info("启用代理失败: {}/{}->{}({})", app, stream, jsonObject.getString("msg"),
+                logger.info("启用代理失败: {}/{}->{}({})", app, stream, wvpResult.getMsg(),
                         streamProxy.getSrcUrl() == null? streamProxy.getUrl():streamProxy.getSrcUrl());
             }
         } else if (streamProxy != null && streamProxy.isEnable()) {
@@ -416,8 +448,8 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
         boolean result = false;
         StreamProxyItem streamProxyDto = videoManagerStorager.queryStreamProxy(app, stream);
         if (streamProxyDto != null && streamProxyDto.isEnable()) {
-            JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyDto);
-            if (jsonObject != null && jsonObject.getInteger("code") == 0) {
+            Boolean removed = removeStreamProxyFromZlm(streamProxyDto);
+            if (removed != null && removed) {
                 streamProxyDto.setEnable(false);
                 result = updateStreamProxy(streamProxyDto);
             }
@@ -426,20 +458,8 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
     }
 
     @Override
-    public JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem) {
-        JSONObject result = new JSONObject();
-        JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
-        if (mediaServerConfigResuly != null && mediaServerConfigResuly.getInteger("code") == 0
-                && mediaServerConfigResuly.getJSONArray("data").size() > 0){
-            JSONObject mediaServerConfig = mediaServerConfigResuly.getJSONArray("data").getJSONObject(0);
-
-            for (String key : mediaServerConfig.keySet()) {
-                if (key.startsWith("ffmpeg.cmd")){
-                    result.put(key, mediaServerConfig.getString(key));
-                }
-            }
-        }
-        return result;
+    public Map<String, String> getFFmpegCMDs(MediaServer mediaServer) {
+        return mediaServerService.getFFmpegCMDs(mediaServer);
     }
 
 
@@ -465,8 +485,8 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
                 mediaServerId, true);
         for (StreamProxyItem streamProxyDto : streamProxyListForEnable) {
             logger.info("恢复流代理," + streamProxyDto.getApp() + "/" + streamProxyDto.getStream());
-            JSONObject jsonObject = addStreamProxyToZlm(streamProxyDto);
-            if (jsonObject == null) {
+            WVPResult<String> wvpResult = addStreamProxyToZlm(streamProxyDto);
+            if (wvpResult == null) {
                 // 设置为离线
                 logger.info("恢复流代理失败" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream());
                 updateStatus(false, streamProxyDto.getApp(), streamProxyDto.getStream());
@@ -516,44 +536,30 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
     }
 
     private void syncPullStream(String mediaServerId){
-        MediaServerItem mediaServer = mediaServerService.getOne(mediaServerId);
+        MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
         if (mediaServer != null) {
             List<OnStreamChangedHookParam> allPullStream = redisCatchStorage.getStreams(mediaServerId, "PULL");
-            if (allPullStream.size() > 0) {
-                zlmresTfulUtils.getMediaList(mediaServer, jsonObject->{
-                    Map<String, StreamInfo> stringStreamInfoMap = new HashMap<>();
-                    if (jsonObject.getInteger("code") == 0) {
-                        JSONArray data = jsonObject.getJSONArray("data");
-                        if(data != null && data.size() > 0) {
-                            for (int i = 0; i < data.size(); i++) {
-                                JSONObject streamJSONObj = data.getJSONObject(i);
-                                if ("rtsp".equals(streamJSONObj.getString("schema"))) {
-                                    StreamInfo streamInfo = new StreamInfo();
-                                    String app = streamJSONObj.getString("app");
-                                    String stream = streamJSONObj.getString("stream");
-                                    streamInfo.setApp(app);
-                                    streamInfo.setStream(stream);
-                                    stringStreamInfoMap.put(app+stream, streamInfo);
-                                }
-                            }
-                        }
+            if (!allPullStream.isEmpty()) {
+                List<StreamInfo> mediaList = mediaServerService.getMediaList(mediaServer, null, null, null);
+                Map<String, StreamInfo> stringStreamInfoMap = new HashMap<>();
+                if (mediaList != null && !mediaList.isEmpty()) {
+                    for (StreamInfo streamInfo : mediaList) {
+                        stringStreamInfoMap.put(streamInfo.getApp() + streamInfo.getStream(), streamInfo);
                     }
-                    if (stringStreamInfoMap.size() == 0) {
-                        redisCatchStorage.removeStream(mediaServerId, "PULL");
-                    }else {
-                        for (String key : stringStreamInfoMap.keySet()) {
-                            StreamInfo streamInfo = stringStreamInfoMap.get(key);
-                            if (stringStreamInfoMap.get(streamInfo.getApp() + streamInfo.getStream()) == null) {
-                                redisCatchStorage.removeStream(mediaServerId, "PULL", streamInfo.getApp(),
-                                        streamInfo.getStream());
-                            }
+                }
+                if (stringStreamInfoMap.isEmpty()) {
+                    redisCatchStorage.removeStream(mediaServerId, "PULL");
+                }else {
+                    for (String key : stringStreamInfoMap.keySet()) {
+                        StreamInfo streamInfo = stringStreamInfoMap.get(key);
+                        if (stringStreamInfoMap.get(streamInfo.getApp() + streamInfo.getStream()) == null) {
+                            redisCatchStorage.removeStream(mediaServerId, "PULL", streamInfo.getApp(),
+                                    streamInfo.getStream());
                         }
                     }
-                });
+                }
             }
-
         }
-
     }
 
     @Override
@@ -569,13 +575,13 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
     @Scheduled(cron = "* 0/10 * * * ?")
     public void asyncCheckStreamProxyStatus() {
 
-        List<MediaServerItem> all = mediaServerService.getAllOnline();
+        List<MediaServer> all = mediaServerService.getAllOnline();
 
         if (CollectionUtils.isEmpty(all)){
             return;
         }
 
-        Map<String, MediaServerItem> serverItemMap = all.stream().collect(Collectors.toMap(MediaServerItem::getId, Function.identity(), (m1, m2) -> m1));
+        Map<String, MediaServer> serverItemMap = all.stream().collect(Collectors.toMap(MediaServer::getId, Function.identity(), (m1, m2) -> m1));
 
         List<StreamProxyItem> list = videoManagerStorager.getStreamProxyListForEnable(true);
 
@@ -585,15 +591,14 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
 
         for (StreamProxyItem streamProxyItem : list) {
 
-            MediaServerItem mediaServerItem = serverItemMap.get(streamProxyItem.getMediaServerId());
+            MediaServer mediaServerItem = serverItemMap.get(streamProxyItem.getMediaServerId());
 
-            // TODO 支持其他 schema
-            JSONObject mediaInfo = zlmresTfulUtils.isMediaOnline(mediaServerItem, streamProxyItem.getApp(), streamProxyItem.getStream(), "rtsp");
+            MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServerItem, streamProxyItem.getApp(), streamProxyItem.getStream());
 
             if (mediaInfo == null){
                 streamProxyItem.setStatus(false);
             } else {
-                if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) {
+                if (mediaInfo.getOnline() != null && mediaInfo.getOnline()) {
                     streamProxyItem.setStatus(true);
                 } else {
                     streamProxyItem.setStatus(false);

+ 176 - 96
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java

@@ -1,21 +1,25 @@
 package com.genersoft.iot.vmp.service.impl;
 
-import com.alibaba.fastjson2.JSON;
-import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
-import com.alibaba.fastjson2.TypeReference;
 import com.baomidou.dynamic.datasource.annotation.DS;
+import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.MediaConfig;
 import com.genersoft.iot.vmp.conf.UserSetting;
-import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.gb28181.bean.GbStream;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
-import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.*;
+import com.genersoft.iot.vmp.media.bean.MediaInfo;
+import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
+import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
+import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
 import com.genersoft.iot.vmp.media.zlm.dto.hook.OriginType;
 import com.genersoft.iot.vmp.service.IGbStreamService;
-import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IStreamPushService;
 import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -27,7 +31,9 @@ import com.github.pagehelper.PageInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.TransactionDefinition;
 import org.springframework.transaction.TransactionStatus;
@@ -66,9 +72,6 @@ public class StreamPushServiceImpl implements IStreamPushService {
     @Autowired
     private EventPublisher eventPublisher;
 
-    @Autowired
-    private ZLMRESTfulUtils zlmresTfulUtils;
-
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
 
@@ -87,33 +90,125 @@ public class StreamPushServiceImpl implements IStreamPushService {
     @Autowired
     private MediaConfig mediaConfig;
 
+    /**
+     * 流到来的处理
+     */
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaArrivalEvent event) {
+        MediaInfo mediaInfo = event.getMediaInfo();
+        if (mediaInfo == null) {
+            return;
+        }
+        if (mediaInfo.getOriginType() != OriginType.RTMP_PUSH.ordinal()
+                && mediaInfo.getOriginType() != OriginType.RTSP_PUSH.ordinal()
+                && mediaInfo.getOriginType() != OriginType.RTC_PUSH.ordinal()) {
+            return;
+        }
 
-    @Override
-    public List<StreamPushItem> handleJSON(String jsonData, MediaServerItem mediaServerItem) {
-        if (jsonData == null) {
-            return null;
+        StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(event.getApp(), event.getStream());
+        if (streamAuthorityInfo == null) {
+            streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(event);
+        } else {
+            streamAuthorityInfo.setOriginType(mediaInfo.getOriginType());
         }
+        redisCatchStorage.updateStreamAuthorityInfo(event.getApp(), event.getStream(), streamAuthorityInfo);
+        StreamPushItem transform = StreamPushItem.getInstance(event, userSetting.getServerId());
+        transform.setPushIng(true);
+        transform.setUpdateTime(DateUtil.getNow());
+        transform.setPushTime(DateUtil.getNow());
+        transform.setSelf(true);
+        StreamPushItem pushInDb = getPush(event.getApp(), event.getStream());
+        if (pushInDb == null) {
+            transform.setCreateTime(DateUtil.getNow());
+            streamPushMapper.add(transform);
+        }else {
+            streamPushMapper.update(transform);
+            gbStreamMapper.updateMediaServer(event.getApp(), event.getStream(), event.getMediaServer().getId());
+        }
+        // TODO 相关的事件自行管理,不需要写入ZLMMediaListManager
+//        ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(transform.getApp(), transform.getStream());
+//        if ( channelOnlineEventLister != null)  {
+//            try {
+//                channelOnlineEventLister.run(transform.getApp(), transform.getStream(), transform.getServerId());;
+//            } catch (ParseException e) {
+//                logger.error("addPush: ", e);
+//            }
+//            removedChannelOnlineEventLister(transform.getApp(), transform.getStream());
+//        }
+        // 冗余数据,自己系统中自用
+        redisCatchStorage.addPushListItem(event.getApp(), event.getStream(), event);
+
+        // 发送流变化redis消息
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("serverId", userSetting.getServerId());
+        jsonObject.put("app", event.getApp());
+        jsonObject.put("stream", event.getStream());
+        jsonObject.put("register", true);
+        jsonObject.put("mediaServerId", event.getMediaServer().getId());
+        redisCatchStorage.sendStreamChangeMsg(OriginType.values()[event.getMediaInfo().getOriginType()].getType(), jsonObject);
+    }
 
-        Map<String, StreamPushItem> result = new HashMap<>();
+    /**
+     * 流离开的处理
+     */
+    @Async("taskExecutor")
+    @EventListener
+    public void onApplicationEvent(MediaDepartureEvent event) {
+        // 兼容流注销时类型从redis记录获取
+        OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
+                event.getApp(), event.getStream(), event.getMediaServer().getId());
+        if (onStreamChangedHookParam != null) {
+            String type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
+            redisCatchStorage.removeStream(event.getMediaServer().getId(), type, event.getApp(), event.getStream());
+            if ("PUSH".equalsIgnoreCase(type)) {
+                // 冗余数据,自己系统中自用
+                redisCatchStorage.removePushListItem(event.getApp(), event.getStream(), event.getMediaServer().getId());
+            }
+            if (type != null) {
+                // 发送流变化redis消息
+                JSONObject jsonObject = new JSONObject();
+                jsonObject.put("serverId", userSetting.getServerId());
+                jsonObject.put("app", event.getApp());
+                jsonObject.put("stream", event.getStream());
+                jsonObject.put("register", false);
+                jsonObject.put("mediaServerId", event.getMediaServer().getId());
+                redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
+            }
+        }
+        GbStream gbStream = gbStreamMapper.selectOne(event.getApp(), event.getStream());
+        if (gbStream != null) {
+            if (userSetting.isUsePushingAsStatus()) {
+                streamPushMapper.updatePushStatus(event.getApp(), event.getStream(), false);
+                eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
+            }
+        }else {
+            streamPushMapper.del(event.getApp(), event.getStream());
+        }
+    }
 
-        List<OnStreamChangedHookParam> onStreamChangedHookParams = JSON.parseObject(jsonData, new TypeReference<List<OnStreamChangedHookParam>>() {});
-        for (OnStreamChangedHookParam item : onStreamChangedHookParams) {
 
+    private List<StreamPushItem> handleJSON(List<StreamInfo> streamInfoList) {
+        if (streamInfoList == null || streamInfoList.isEmpty()) {
+            return null;
+        }
+        Map<String, StreamPushItem> result = new HashMap<>();
+        for (StreamInfo streamInfo : streamInfoList) {
             // 不保存国标推理以及拉流代理的流
-            if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal()
-                    || item.getOriginType() == OriginType.RTMP_PUSH.ordinal()
-                    || item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) {
-                String key = item.getApp() + "_" + item.getStream();
+            if (streamInfo.getOriginType() == OriginType.RTSP_PUSH.ordinal()
+                    || streamInfo.getOriginType() == OriginType.RTMP_PUSH.ordinal()
+                    || streamInfo.getOriginType() == OriginType.RTC_PUSH.ordinal() ) {
+                String key = streamInfo.getApp() + "_" + streamInfo.getStream();
                 StreamPushItem streamPushItem = result.get(key);
                 if (streamPushItem == null) {
-                    streamPushItem = transform(item);
+                    streamPushItem = streamPushItem.getInstance(streamInfo);
                     result.put(key, streamPushItem);
                 }
             }
         }
-
         return new ArrayList<>(result.values());
     }
+
     @Override
     public StreamPushItem transform(OnStreamChangedHookParam item) {
         StreamPushItem streamPushItem = new StreamPushItem();
@@ -122,7 +217,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
         streamPushItem.setStream(item.getStream());
         streamPushItem.setAliveSecond(item.getAliveSecond());
         streamPushItem.setOriginSock(item.getOriginSock());
-        streamPushItem.setTotalReaderCount(item.getTotalReaderCount());
+        streamPushItem.setTotalReaderCount(item.getTotalReaderCount() + "");
         streamPushItem.setOriginType(item.getOriginType());
         streamPushItem.setOriginTypeStr(item.getOriginTypeStr());
         streamPushItem.setOriginUrl(item.getOriginUrl());
@@ -164,15 +259,10 @@ public class StreamPushServiceImpl implements IStreamPushService {
         gbStreamService.sendCatalogMsg(stream, CatalogEvent.DEL);
         platformGbStreamMapper.delByAppAndStream(stream.getApp(), stream.getStream());
         int del = gbStreamMapper.del(stream.getApp(), stream.getStream());
-        MediaServerItem mediaInfo = mediaServerService.getOne(stream.getMediaServerId());
-        JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaInfo, stream.getApp(), stream.getStream());
-        if (mediaList != null) {
-            if (mediaList.getInteger("code") == 0) {
-                JSONArray data = mediaList.getJSONArray("data");
-                if (data == null) {
-                    streamPushMapper.del(stream.getApp(), stream.getStream());
-                }
-            }
+        MediaServer mediaInfo = mediaServerService.getOne(stream.getMediaServerId());
+        List<StreamInfo> mediaList = mediaServerService.getMediaList(mediaInfo, stream.getApp(), stream.getStream(), null);
+        if (mediaList != null && mediaList.isEmpty()) {
+            streamPushMapper.del(stream.getApp(), stream.getStream());
         }
         return del > 0;
     }
@@ -195,8 +285,8 @@ public class StreamPushServiceImpl implements IStreamPushService {
         gbStreamMapper.del(app, streamId);
         int delStream = streamPushMapper.del(app, streamId);
         if (delStream > 0) {
-            MediaServerItem mediaServerItem = mediaServerService.getOne(streamPushItem.getMediaServerId());
-            zlmresTfulUtils.closeStreams(mediaServerItem,app, streamId);
+            MediaServer mediaServerItem = mediaServerService.getOne(streamPushItem.getMediaServerId());
+            mediaServerService.closeStreams(mediaServerItem,app, streamId);
         }
         return true;
     }
@@ -204,7 +294,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
     @Override
     public void zlmServerOnline(String mediaServerId) {
         // 同步zlm推流信息
-        MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
+        MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId);
         if (mediaServerItem == null) {
             return;
         }
@@ -232,71 +322,61 @@ public class StreamPushServiceImpl implements IStreamPushService {
         for (StreamAuthorityInfo streamAuthorityInfo : allStreamAuthorityInfo) {
             streamAuthorityInfoInfoMap.put(streamAuthorityInfo.getApp() + streamAuthorityInfo.getStream(), streamAuthorityInfo);
         }
-        zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{
-            if (mediaList == null) {
-                return;
-            }
-            String dataStr = mediaList.getString("data");
-
-            Integer code = mediaList.getInteger("code");
-            List<StreamPushItem> streamPushItems = null;
-            if (code == 0 ) {
-                if (dataStr != null) {
-                    streamPushItems = handleJSON(dataStr, mediaServerItem);
-                }
-            }
-
-            if (streamPushItems != null) {
-                for (StreamPushItem streamPushItem : streamPushItems) {
-                    pushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream());
-                    streamInfoPushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream());
-                    streamAuthorityInfoInfoMap.remove(streamPushItem.getApp() + streamPushItem.getStream());
-                }
+        List<StreamInfo> mediaList = mediaServerService.getMediaList(mediaServerItem, null, null, null);
+        if (mediaList == null) {
+            return;
+        }
+        List<StreamPushItem> streamPushItems = handleJSON(mediaList);
+        if (streamPushItems != null) {
+            for (StreamPushItem streamPushItem : streamPushItems) {
+                pushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream());
+                streamInfoPushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream());
+                streamAuthorityInfoInfoMap.remove(streamPushItem.getApp() + streamPushItem.getStream());
             }
-            List<StreamPushItem> offlinePushItems = new ArrayList<>(pushItemMap.values());
-            if (offlinePushItems.size() > 0) {
-                String type = "PUSH";
-                int runLimit = 300;
-                if (offlinePushItems.size() > runLimit) {
-                    for (int i = 0; i < offlinePushItems.size(); i += runLimit) {
-                        int toIndex = i + runLimit;
-                        if (i + runLimit > offlinePushItems.size()) {
-                            toIndex = offlinePushItems.size();
-                        }
-                        List<StreamPushItem> streamPushItemsSub = offlinePushItems.subList(i, toIndex);
-                        streamPushMapper.delAll(streamPushItemsSub);
+        }
+        List<StreamPushItem> offlinePushItems = new ArrayList<>(pushItemMap.values());
+        if (offlinePushItems.size() > 0) {
+            String type = "PUSH";
+            int runLimit = 300;
+            if (offlinePushItems.size() > runLimit) {
+                for (int i = 0; i < offlinePushItems.size(); i += runLimit) {
+                    int toIndex = i + runLimit;
+                    if (i + runLimit > offlinePushItems.size()) {
+                        toIndex = offlinePushItems.size();
                     }
-                }else {
-                    streamPushMapper.delAll(offlinePushItems);
+                    List<StreamPushItem> streamPushItemsSub = offlinePushItems.subList(i, toIndex);
+                    streamPushMapper.delAll(streamPushItemsSub);
                 }
-
+            }else {
+                streamPushMapper.delAll(offlinePushItems);
             }
-            Collection<OnStreamChangedHookParam> offlineOnStreamChangedHookParamList = streamInfoPushItemMap.values();
-            if (offlineOnStreamChangedHookParamList.size() > 0) {
-                String type = "PUSH";
-                for (OnStreamChangedHookParam offlineOnStreamChangedHookParam : offlineOnStreamChangedHookParamList) {
-                    JSONObject jsonObject = new JSONObject();
-                    jsonObject.put("serverId", userSetting.getServerId());
-                    jsonObject.put("app", offlineOnStreamChangedHookParam.getApp());
-                    jsonObject.put("stream", offlineOnStreamChangedHookParam.getStream());
-                    jsonObject.put("register", false);
-                    jsonObject.put("mediaServerId", mediaServerId);
-                    redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
-                    // 移除redis内流的信息
-                    redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream());
-                    // 冗余数据,自己系统中自用
-                    redisCatchStorage.removePushListItem(offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream(), mediaServerItem.getId());
-                }
+
+        }
+        Collection<OnStreamChangedHookParam> offlineOnStreamChangedHookParamList = streamInfoPushItemMap.values();
+        if (offlineOnStreamChangedHookParamList.size() > 0) {
+            String type = "PUSH";
+            for (OnStreamChangedHookParam offlineOnStreamChangedHookParam : offlineOnStreamChangedHookParamList) {
+                JSONObject jsonObject = new JSONObject();
+                jsonObject.put("serverId", userSetting.getServerId());
+                jsonObject.put("app", offlineOnStreamChangedHookParam.getApp());
+                jsonObject.put("stream", offlineOnStreamChangedHookParam.getStream());
+                jsonObject.put("register", false);
+                jsonObject.put("mediaServerId", mediaServerId);
+                redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
+                // 移除redis内流的信息
+                redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream());
+                // 冗余数据,自己系统中自用
+                redisCatchStorage.removePushListItem(offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream(), mediaServerItem.getId());
             }
+        }
 
-            Collection<StreamAuthorityInfo> streamAuthorityInfos = streamAuthorityInfoInfoMap.values();
-            if (streamAuthorityInfos.size() > 0) {
-                for (StreamAuthorityInfo streamAuthorityInfo : streamAuthorityInfos) {
-                    // 移除redis内流的信息
-                    redisCatchStorage.removeStreamAuthorityInfo(streamAuthorityInfo.getApp(), streamAuthorityInfo.getStream());
-                }
+        Collection<StreamAuthorityInfo> streamAuthorityInfos = streamAuthorityInfoInfoMap.values();
+        if (streamAuthorityInfos.size() > 0) {
+            for (StreamAuthorityInfo streamAuthorityInfo : streamAuthorityInfos) {
+                // 移除redis内流的信息
+                redisCatchStorage.removeStreamAuthorityInfo(streamAuthorityInfo.getApp(), streamAuthorityInfo.getStream());
             }
-        }));
+        }
     }
 
     @Override
@@ -470,8 +550,8 @@ public class StreamPushServiceImpl implements IStreamPushService {
         int delStream = streamPushMapper.delAllForGbStream(gbStreams);
         if (delStream > 0) {
             for (GbStream gbStream : gbStreams) {
-                MediaServerItem mediaServerItem = mediaServerService.getOne(gbStream.getMediaServerId());
-                zlmresTfulUtils.closeStreams(mediaServerItem, gbStream.getApp(), gbStream.getStream());
+                MediaServer mediaServerItem = mediaServerService.getOne(gbStream.getMediaServerId());
+                mediaServerService.closeStreams(mediaServerItem, gbStream.getApp(), gbStream.getStream());
             }
 
         }

+ 17 - 18
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java

@@ -6,12 +6,12 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.media.event.hook.Hook;
+import com.genersoft.iot.vmp.media.event.hook.HookType;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
+import com.genersoft.iot.vmp.media.bean.MediaServer;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.bean.*;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
@@ -61,9 +61,9 @@ public class RedisGbPlayMsgListener implements MessageListener {
      */
     public static final  int ERROR_CODE_TIMEOUT = -3;
 
-    private Map<String, PlayMsgCallback> callbacks = new ConcurrentHashMap<>();
-    private Map<String, PlayMsgCallbackForStartSendRtpStream> callbacksForStartSendRtpStream = new ConcurrentHashMap<>();
-    private Map<String, PlayMsgErrorCallback> callbacksForError = new ConcurrentHashMap<>();
+    private final Map<String, PlayMsgCallback> callbacks = new ConcurrentHashMap<>();
+    private final Map<String, PlayMsgCallbackForStartSendRtpStream> callbacksForStartSendRtpStream = new ConcurrentHashMap<>();
+    private final Map<String, PlayMsgErrorCallback> callbacksForError = new ConcurrentHashMap<>();
 
     @Autowired
     private UserSetting userSetting;
@@ -87,9 +87,9 @@ public class RedisGbPlayMsgListener implements MessageListener {
 
 
     @Autowired
-    private ZlmHttpHookSubscribe subscribe;
+    private HookSubscribe subscribe;
 
-    private ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
+    private final ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
 
     @Qualifier("taskExecutor")
     @Autowired
@@ -219,7 +219,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
      * 处理收到的请求推流的请求
      */
     private void requestPushStreamMsgHand(RequestPushStreamMsg requestPushStreamMsg, String fromId, String serial) {
-        MediaServerItem mediaInfo = mediaServerService.getOne(requestPushStreamMsg.getMediaServerId());
+        MediaServer mediaInfo = mediaServerService.getOne(requestPushStreamMsg.getMediaServerId());
         if (mediaInfo == null) {
             // TODO 回复错误
             return;
@@ -258,7 +258,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
      * 处理收到的请求sendItem的请求
      */
     private void requestSendItemMsgHand(RequestSendItemMsg content, String toId, String serial) {
-        MediaServerItem mediaServerItem = mediaServerService.getOne(content.getMediaServerId());
+        MediaServer mediaServerItem = mediaServerService.getOne(content.getMediaServerId());
         if (mediaServerItem == null) {
             logger.info("[回复推流信息] 流媒体{}不存在 ", content.getMediaServerId());
 
@@ -274,7 +274,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
             return;
         }
         // 确定流是否在线
-        Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream());
+        Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, content.getApp(), content.getStream());
         if (streamReady != null && streamReady) {
             logger.info("[回复推流信息]  {}/{}", content.getApp(), content.getStream());
             responseSendItem(mediaServerItem, content, toId, serial);
@@ -297,9 +297,8 @@ public class RedisGbPlayMsgListener implements MessageListener {
             }, userSetting.getPlatformPlayTimeout());
 
             // 添加订阅
-            HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(content.getApp(), content.getStream(), true, "rtsp", mediaServerItem.getId());
-
-            subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam)->{
+            Hook hook = Hook.getInstance(HookType.on_media_arrival, content.getApp(), content.getStream(), content.getMediaServerId());
+            subscribe.addSubscribe(hook, (hookData)->{
                         dynamicTask.stop(taskKey);
                         responseSendItem(mediaServerItem, content, toId, serial);
                     });
@@ -317,7 +316,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
     /**
      * 将获取到的sendItem发送出去
      */
-    private void responseSendItem(MediaServerItem mediaServerItem, RequestSendItemMsg content, String toId, String serial) {
+    private void responseSendItem(MediaServer mediaServerItem, RequestSendItemMsg content, String toId, String serial) {
         SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, content.getIp(),
                 content.getPort(), content.getSsrc(), content.getPlatformId(),
                 content.getApp(), content.getStream(), content.getChannelId(),
@@ -449,7 +448,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
             logger.info("[REDIS 执行其他平台的请求停止推流] 失败: sendRtpItem为NULL");
             return;
         }
-        MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+        MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
         if (mediaInfo == null) {
             // TODO 回复错误
             return;

+ 0 - 0
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamCloseResponseListener.java


Някои файлове не бяха показани, защото твърде много файлове са промени